Docs⌘ K  Search
Docs/Memory/Outcome Memory
Outcome Learning

Outcome Memory

Record decisions before acting, report delayed outcomes, and learn from evidence without treating silence as failure.

Choose, act, observe: the three-call path

Stable Python 2.6.1 and TypeScript 2.5.1 remain compatible with the existing three-call workflow. The current patch versions below add the version-bound integration helpers; older installed packages do not acquire those helpers automatically.

Start with one policy, fixed action meanings and the context your application knows matters. Ask Hebbrix to choose, perform the selected action only under your own authorization rules, then record what actually happened. You do not need an advice prompt or a separate framework to close this loop; proofloop is simply the SDK resource name.

Stable Python 2.6.2: setup → native decision → actual outcome
await client.proofloop.setup_policy(
    "support.reply.v1", collection_id=collection_id, user_id="customer-42",
    context_schema={"version": "v1", "fields": {
        "issue": {"values": ["delivery", "billing"], "required": True}
    }},
    actions={
        "explain": {"description": "Explain the delivery status", "target": "ticket",
                    "risk_tier": "low", "exploration_allowed": True},
        "review": {"description": "Request a support review", "target": "ticket",
                   "risk_tier": "low", "exploration_allowed": True},
    },
    configuration={"strategy": "posterior_sampling"},
)
receipt = await client.proofloop.decide(
    policy_key="support.reply.v1", collection_id=collection_id, user_id="customer-42",
    mode="auto", context={"issue": "delivery"},
    candidates=[{"action_key": "explain"}, {"action_key": "review"}],
    idempotency_key="ticket-42-decision",
)
# Read receipt["chosen_action_key"]. Your application separately checks permission,
# performs that action, and determines actual_success. Do not score a proposal.
await client.proofloop.record_outcome(
    receipt["decision_id"], success=actual_success,
    idempotency_key="ticket-42-result",
)

The snippet assumes an authenticated client and an existing collection. TypeScript 2.5.2 exposes the same path as setupPolicy, decide and recordOutcome. Keep the same collection and user scope throughout. Use a new policy/version when changing an action, target or outcome meaning; missing outcomes remain unknown.

Recommended integration defaults

  • Use native mode: "auto" when Hebbrix should select among actions your application already allows. Use the returned chosen_action_key, not the largest rounded number in an advice card.
  • Use mode: "observe" only when another controller intentionally chooses. Report its actual complete choice distribution if known; model confidence and advisory samples are not execution propensities.
  • Use advice as context for an external chooser, not as proof that an LLM will follow it or explore. Independently check present permissions and required human approval before every side effect.
  • Declare relevant context and risk before collecting outcomes. Changing-condition controls require an explicit versioned opt-in; there is no universally best stationary or drift preset.
Release boundary: the stable three-call path above is available in the listed SDK versions. The compact views, readiness wait, explicit confirmation, batches, canonical scope alias and native selection controls in the candidate integration section below require the matching Round9 server and Python 2.6.2 / TypeScript 2.5.2. This page is not a deployment or publication receipt. Check GET /v1/release and your installed package changelog.

Versioned change response and optional feature sharing

Explicitly opt into outcome-policy-v3 for the experimental selection methods below. Unversioned new setup retains v2, and existing v1/v2 contracts retain their stored behavior. These methods do not establish generalization, calibrated confidence, reliable drift adaptation or superiority. Benchmark material stays private.

In the currently released contract, V3 change_response: true reconstructs a bounded one-sided CUSUM from current final, full-confidence, exact-context outcomes. A detected drop limits serving selection to the reconstructed post-change epoch and temporarily raises low-risk exploration to 0.20 for 24 post-change observations. It does not delete history, rewrite rewards, certify a change or reset the full-trial ACT gate. The separate action-advice distinction is described below. Corrections can retract an unsupported alarm. Set change_response: false to disable this heuristic.

Opt into contextual_model: "linear_optional" only after declaring relevant categorical context. The bounded additive linear model shares declared optional features within the same required-context partition, owner, policy, action and metric contract. At most four optional fields and 32 categories are supported. This is an approximate utility model and can miss interactions; required context is never dropped and pooled evidence never earns ACT. Default "exact" retains exact buckets with bounded optional-field priors.

Explicit v3 contextual_model: "adaptive_optional" compares required-only, exact and optional predictions frozen before each selected action's actual final outcome. Optional fields must earn their predictive complexity; shared rows are not counted three times. Historical rows without prediction receipts are not reconstructed. This observed-action competition does not establish causal relevance, calibrated intervals or improved performance.

The advice card suggests a challenger, never the current empirical leader. Under-sampled alternatives receive wider optimism and a reason to explore; a single failed trial does not settle an action's value. Your application owns its exploration budget and risk decision. The card is not a receipt, authenticated randomization or execution permission.

Read the machine-readable recommended_action rather than taking the first maximum of rounded card values. For v3 /advice, declare remaining_decisions (1–10,000), optionally max_pilot_decisions (1–8, default four). Without a horizon, the suggested exploration budget is zero. Bounded pilot estimates include immediate expected utility forgone and remaining exploitation; equally valued positive pilots are sampled uniformly. Re-read after actual outcomes. These estimates rely on unverified stationarity and utility assumptions, and ignored suggestions never become trials.

Inspect policy_path to distinguish legacy_challenger_v2 from finite_horizon_v3. Supplying a horizon to a v2 policy does not upgrade it; the card explicitly reports that the legacy path ignored it. Explicit v3 defaults omitted change_response to true; adaptive optional features and policy mixture still require separate opt-ins.

Explicit v3 advice with a context-local remaining horizon
await client.proofloop.setup_policy(
    "support.pilot", collection_id=collection_id, user_id="alice",
    context_schema=owner_declared_schema, actions=owner_declared_actions,
    configuration={"schema_version":"outcome-policy-v3", "change_response":False})
# Your application tracks this horizon for the SAME context, not all users.
card = await client.proofloop.policy_advice(
    "support.pilot", collection_id=collection_id, user_id="alice",
    context={"issue":"late"}, remaining_decisions=remaining_in_this_context,
    max_pilot_decisions=4)
assert card["policy_path"]["horizon_applied"]
# Re-read after an actual outcome; decrement your declared horizon.
# An ignored suggestion is not a trial or a behavior probability.

Explicit v3 proposal_mode: "policy_mixture" combines the native distribution with prior_action using prior_strength as initial odds, not reward trials. Native receipts log the actual distribution and selected propensity. Complete same-context native outcomes can attenuate proposal weight through bounded relative policy losses; protected, provisional and caller-forged receipts do not qualify. Importance clipping can introduce bias. Default legacy_prior is unchanged; faster cold start and hybrid gains are unqualified.

Built-in success minus actual cost

Pass value_objective to atomic setup: success_value, max_cost and cost_unit. It creates separate required success and cost metrics. Within declared bounds, normalized utility preserves the ordering of success_value × success − cost. Record actual units, not a guessed provider charge. Cost normalization clips outside [0, max_cost]; choose a defensible bound and add a separate guardrail for unacceptable costs. Missing or provisional cost cannot complete the outcome.

Stable SDK: explicit business value
await client.proofloop.setup_policy(
    "support.value", collection_id=collection_id, user_id="alice",
    context_schema={"version":"v1", "fields":{"issue":{"values":["late","damaged"]}}},
    actions=owner_declared_actions,
    configuration={"schema_version":"outcome-policy-v3"},
    value_objective={"success_value":10, "max_cost":20, "cost_unit":"USD"})
# After independently authorized action execution, report both actual metrics:
await client.proofloop.record_outcome(receipt["decision_id"], observations=[
    {"metric_key":"success", "value":1, "is_final":True},
    {"metric_key":"cost", "value":5, "is_final":True}])

For separately modeled value selection, explicitly set value_objective.selection_model: "success_cost_v1" and declare declared_action_costs for every configured action. This requires v3, exact context and posterior sampling. Those pre-action costs are caller assumptions, not recorded charges. After admitted complete final confidence-1 binary success and bounded actual cost, the selector samples the success posterior separately and subtracts the weighted mean actual cost. Missing, provisional, implicit, future, protected and unproven legacy components do not qualify. The actual-cost mean replaces the cold-start assumption; cost uncertainty is not modeled and utility intervals are conditional on that estimate. No optional pooling or optimistic proposal pseudo-success is applied. Use policy_mixture for a transient proposal rather than the legacy prior. Python 2.6.2 accepts this objective dictionary; use the HTTP contract for new component fields not yet exposed by TypeScript 2.5.2's convenience type.

Opt-in component-value objective
{
  "success_value": 40, "max_cost": 20, "cost_unit": "USD",
  "selection_model": "success_cost_v1",
  "declared_action_costs": {"review": 2, "assist": 14}
}

Action keys and costs above are illustrative, not a recommended configuration. Log actual selected-action costs separately. Native selection_contract.inputs.summaries and advice component_value_estimate expose success evidence, expected cost, cost basis and expected value. Finite-horizon component pilots assume costs remain constant during the pilot. This opt-in has not established a performance gain, calibrated uncertainty or monetary return. Existing composite_utility behavior is unchanged.

Opt-in follow-up outcome capture

On POST /v1/chat/completions, use features.learning: true and outcome_followup: {"decision_id": "prior-receipt-UUID"} in the same owner, collection and end-user scope. API keys also need learning:write; chat permission alone is insufficient. Standalone signals such as “thanks, that fixed it” or “customer reopened the ticket” can be recorded as source: implicit, confidence 0.35, is_final: false. Inspect memory_context.outcome_capture in JSON or the final SSE chunk.

The decision may have been created directly through /v1/learning/decisions in observe or automatic mode. No chat-session, trace-ID or capture-specific time-window join is required. Match the exact end-user value: omission differs from supplying one. Only a canonical maximized binary success contract allowing implicit evidence is eligible; protected episodes and foreign scopes return 404, conflicting receipts return 409 and other contract errors return 400. Completed/censored decisions are preserved, and repeated text on different decisions has separate generated receipt keys.

Default semantic_fallback: true on the explicit binding adds at most one meaning extractor for unsupported natural language, capped at 400 output tokens and eight seconds within the request deadline. It requires a current direct report attributable to the bound action and a verbatim source witness; unknown, quoted, hypothetical, request-only, invalid or unavailable extraction abstains. The witness authenticates text, not inferred meaning. Set semantic_fallback: false for the finite-parser-only path. Broad language accuracy remains unqualified. Implicit capture does not confirm execution, complete an outcome, train serving selection or earn autonomy. Confirm actual outcomes explicitly. Learning disabled prevents capture.

Recorded captures now include outcome_capture.observation: persisted observation ID, metric, value, polarity, confidence, source and finality, plus stored source_sha256 and character-offset source_span. First capture and replay project the same committed interpretation even when retry casing or spacing differs. No message text is echoed; old receipts without a witness return null fields instead of fabricating provenance. A source witness authenticates text, not semantic truth or execution.

Round9 integration: smaller reads, explicit writes

Version-bound integration. Python 2.6.2 and TypeScript 2.5.2 contain the new convenience helpers. These require matching Round9 server routes; check the deployed release and installed package before using them.

Compact evidence and one machine-readable gate

POST /v1/search with view: "compact" keeps the top-level grounding, evidence IDs, proof token and row content while omitting optional diagnostics. GET /v1/learning/policies/{key}/advice?view=compact keeps brief lesson lines, scope, per-action direct completed counts and an exact native-decision request template. Missing binary counts remain null with binary_counts_available: false, not invented zero successes or failures. Full views and complete server-side receipts remain available; compact presentation does not promote evidence.

Candidate insights and full/compact advice also expose outcome_evidence_counts: distinct stored observations in the exact owner, collection/user, policy and stored primary context. Final, provisional, implicit-provisional and correction counts describe retained history, including historical frozen action/metric versions. They are not completed trial counts, current-version selection eligibility or action permission. When observations are only provisional, provisional_evidence_guidance points to explicit checked caller confirmation or the bound registered-verifier path for protected episodes; it never automatically promotes evidence or certifies its meaning.

Configured advice uses canonical gate: ASK, REVIEW, ACT or BLOCK. BLOCK dominates weaker recommendations; inspect blocked_by and blocked_hint. ACT still requires your independent current permission. The Round9 confidence contract accepts canonical user_id, retaining end_user_id/enduser_id aliases; conflicting aliases are rejected, never substituted for the authenticated account identity.

Candidate post-change and recent-outcome checks

The action-advice path shares the latest reconstructed change epoch across actions in the same exact context. Each action uses only its own matching post-change outcomes; other actions or contexts cannot restore ACT. The declared minimum and configured lower-bound thresholds remain unchanged, including a separate recent-window check. The currently released recovery rule still requires eight consecutive successes during recovery probation. Round10 distinguishes a normal stationary miss from actual recent-gate loss: the latest failure still demotes immediately, but recovery probation begins only after a previously qualified recent gate fails or an epoch requires requalification. Eight successes alone never bypass the full-period, recent-window, scope, risk or aggregate guards. History is retained; the reconstructed epoch is descriptive, not a certified change event.

Wait for the original chat-learning job

Chat extraction is asynchronous. Read memory_context.memory_learning for its original job_id, poll_url, status and memory IDs. A non-streaming memory+learning request can set wait_for_learning_seconds from 0 to 20; timeout may still return a pending job. Resume observation of that original job rather than submitting the chat again. include_low_confidence: true is an explicit diagnostic opt-in, not a correctness certification.

Confirm one checked capture explicitly

A model interpretation or repeated phrase never finalizes a provisional capture. After an authorized person or application checks the actual result, the candidate POST /v1/learning/decisions/{id}/confirm-capture accepts the original observation, exact scope, explicit Boolean success, affirmative confirmed: true and an idempotency key.

Candidate confirmation body — caller assertion, not a verifier
{
  "observation_id": "ORIGINAL_CAPTURE_OBSERVATION_ID",
  "collection_id": "ORIGINAL_COLLECTION_ID", "user_id": "ORIGINAL_USER_ID",
  "success": true, "confirmed": true,
  "idempotency_key": "checked-result-event-42"
}

The frozen metric/source contract must allow the explicit source; supply required source-system, event and digest fields when that contract demands them. Existing finals are not silently replaced, foreign scope is refused, and protected episodes remain on their separate verifier path. Caller confirmation does not authenticate semantic truth or execution.

Bounded batches with per-item results

Candidate POST /v1/learning/decisions/batch accepts 1–50 existing decision requests, each with a nonempty idempotency_key. POST /v1/learning/outcomes/batch accepts 1–50 objects containing decision_id, idempotency_key and outcome. The authenticated account needs Starter or above through its existing batch_operations capability; Free remains ineligible and a supplied tier field cannot grant access. Compact is the default batch view. The response says atomic: false: inspect every indexed accepted/rejected item because later failure does not undo earlier commits. Replay only an unchanged original item and key; do not repeat a partially accepted batch with replacement keys. Ownership, source/metric, protected-episode and per-item rate guards still apply. Batching is not a throughput or latency SLA.

Opt-in native trial and proposal controls

Candidate selection_controls belongs in an explicit outcome-policy-v3 configuration. It can exclude approximately dominated actions using direct completed exact-context full-confidence utility evidence, explicitly cap proposal weight at 0–0.8, and reserve 0–20 assignments per action/context under one declared budget identity. These are owner choices and selection heuristics, not calibrated safe or performance-optimal settings.

Candidate configuration fragment — separately approved finite trials
{
  "schema_version": "outcome-policy-v3",
  "strategy": "posterior_sampling", "proposal_mode": "policy_mixture",
  "selection_controls": {
    "exclude_dominated_actions": true, "minimum_completed_decisions": 8,
    "maximum_proposal_weight": 0.25,
    "reserved_trials_per_action": 4,
    "reservation_budget_key": "approved-initial-trials"
  }
}

Add the separately configured actions and immutable context to this fragment. An assignment consumes a reserved slot even if no outcome arrives. Reads and retries do not replenish it, and a disappointing result does not authorize a new budget. Reserved assignments and a policy-owned horizon cannot be enabled together. Read receipts and probation/change status before choosing further actions; no stronger-baseline, real-drift or learning superiority is established.

Selection, cold start and evaluation

V3 selection, transient advisor proposals and decision-count aging are experimental. Performance targets and reliable model compliance with feedback have not been established. Setup and report helpers expose configuration and evidence, not proof of improved decisions. ACT remains advice, never execution permission. Check /v1/release for the deployed build.

New outcome-policy-v2 and explicit outcome-policy-v3 configurations default to finite posterior sampling and decision mode: "auto". Exploration is restricted to actions explicitly declared low-risk and exploration-allowed, subject to existing aggregate guards. Without that configuration, or when no such actions exist, auto remains conservative. A schema alone does not declare action risk or authorize exploration. Existing v1/v2 configurations retain their stored behavior; explicit recommend remains conservative.

The conservative selector starts with your baseline (otherwise the first candidate), requires at least 5 effective observations, and switches only when a challenger's lower bound exceeds the baseline mean by 0.02. Evidence decays with a default 90-day half-life. Inspect each receipt’s frozen selection_contract for its actual distribution, inputs and thresholds.

recommend never explores. Server-side explore accepts epsilon from 0 to 0.2: each of N candidates receives epsilon/N, and the conservative recommendation receives the remaining 1 − epsilon. Cold-start discovery can therefore be slow and is not guaranteed to outperform random choice. For an independently controlled experiment, use observe and record its true distribution; never invent propensities after seeing outcomes.

Five effective observations are a per-action conservative selection gate, not an all-candidates visibility gate. Insights show an action’s stored evidence before eligibility, with stored_but_not_selection_eligible. A tiny comparison tolerance prevents seconds-old rounding from requiring an extra observation. With epsilon 0.2 and four candidates, each challenger receives only 5% of traffic: collecting five observations averages about 100 decisions in that context, before the uncertainty/lift test. This is not a promise of switching at 100.

Risk-declared adaptive exploration

Configure actions before collecting decisions. Omit mode for auto, or use mode: "explore", omit chosen_action_key, and set exploration_rate: 0. The server computes a deterministic 512-draw posterior-win distribution with one residual vote per eligible action, mixes a default 0.04 uniform exploration fraction across eligible actions in v2, then samples that exact categorical distribution. Each eligible action therefore has probability at least 0.04/N, not a guarantee of being selected in any finite window. Receipts record the actual distribution—not estimated propensities for an ideal Thompson sampler. Aggregate guards still exclude ineligible actions.

PUT /v1/learning/policies/sandbox.restart/configuration
{
  "collection_id":"YOUR_COLLECTION_ID", "user_id":"alice",
  "expected_revision":0,
  "configuration":{
    "strategy":"posterior_sampling", "window_decisions":1000,
    "half_life_days":90, "partial_pool_strength":2,
    "actions":{
      "restart":{
        "description":"Restart sandbox-web pods", "target":"sandbox-web",
        "risk_tier":"low", "exploration_allowed":true,
        "autonomy_allowed":true, "minimum_evaluated":8,
        "minimum_success_lower_bound":0.65,
        "prior_mean":0.5, "prior_strength":0, "prior_source":"owner_assumption"
      },
      "review":{
        "description":"Review sandbox-web configuration", "target":"sandbox-web",
        "risk_tier":"low", "exploration_allowed":true
      }
    }
  }
}

GET the same configuration route to read its revision. Updates require the current expected_revision; 409 means read and reconsider, not blindly retry. Risk defaults to high; only low risk may opt into exploration or ACT advice. These are explicit caller assumptions, not independent risk certification. A production refund or deployment must not be relabeled low risk merely to pass a gate.

Prior strength is bounded to 0–10 per action and never counts as observed trials or earns autonomy. A changed action description or target cannot reuse old bound-action evidence. Configured policies require a declared schema for nonempty context. Optional-field partial pooling contributes a bounded prior, not duplicated trials; required fields and tenant boundaries are never dropped. V2 adaptive pooling multiplies the available prior strength by 5/(5 + local effective evidence), reducing its influence as local observations accumulate. pooling_mode: "fixed" preserves fixed behavior, and strength 0 disables borrowing. No algorithm can guarantee identifying the true winner from finite noisy outcomes.

Windows and change diagnostics

Configured policies read the latest 20–2,000 comparable decisions (default 1,000) with a configurable half-life of one minute to 3,650 days. A one-minute fresh plateau avoids immediate numerical decay. Required-context, action and metric admission precedes the bounded window; unrelated required contexts do not evict comparable evidence. Insights report truncation. Two adjacent windows of 20 full-confidence outcomes can produce observed_performance_drop when the normalized mean falls by at least 0.30. local_prior_divergence reports a local/pooled posterior-mean gap of at least 0.30 with at least five effective observations in each. Both are descriptive diagnostics, not statistical or causal change-point proof.

The experimental selector also supports selection_half_life_decisions from 20 to 2,000, or null to disable it. New configurations without a schema version default to 64; existing versioned configurations missing this field retain null. The latest 20 completed, positive-confidence exact-context observations have no decision-age discount; older observations decay by this half-life across candidate actions. This discount combines with wall-clock decay for selection only. It never rewrites stored outcomes or supplies the full trials required for ACT advice. Faster adaptation and higher quality are not established.

Migration and one-call setup

Create a new policy with POST /v1/learning/policies/{key}/setup, sending context_schema, configuration.actions, collection_id and user_id. Schema and configuration commit atomically; an existing configuration conflicts rather than being reset. Python setup_policy and TypeScript setupPolicy wrap this one request. Declare risk honestly; leave high-risk actions conservative.

Python: recommended setup
await client.proofloop.setup_policy(
    policy_key="sandbox.inspect", collection_id=collection_id, user_id="alice",
    context_schema={"version":"v1", "fields":{"issue":{"values":["slow","error"]}}},
    actions={
        "logs":{"description":"Read sandbox-web logs", "target":"sandbox-web",
                "risk_tier":"low", "exploration_allowed":True},
        "metrics":{"description":"Read sandbox-web metrics", "target":"sandbox-web",
                   "risk_tier":"low", "exploration_allowed":True}
    })
# Next decide() calls use auto; record real outcomes after authorized execution.

For an existing policy, GET its configuration, retain the exact scope/action identities, and PUT the current expected_revision with schema_version: "outcome-policy-v3", strategy: "posterior_sampling", pooling_mode: "adaptive", minimum_exploration: 0.04 and a deliberate change_response choice. Review risk and exploration opt-ins before this change. Pin mode: "recommend" for no exploration, or retain the old version. No historical outcomes are relabeled by migration.

Default integration: native selection; advisor logging is optional

For reliable ownership of exploration, let the server select with decide(mode="auto"). An LLM reading a card can ignore an under-tested action, so advisor logging is not the recommended learning selector. Native selection does not guarantee beating a fixed strategy or Thompson sampling, and never grants execution permission.

When Hebbrix should select among actions, use the setup → native decide → actual outcome path above. Explicitly configure v3, low-risk exploration and a decision budget where appropriate. Inspect the receipt's actual distribution: an alternative with zero probability cannot be learned from selected-action feedback. Neither exploration nor v3 guarantees discovering a better action within a finite budget.

Use Python decide_with_advice / TypeScript decideWithAdvice only when your advisor deliberately owns the choice: fetch /advice, let it choose once, then log in observe mode. Observe does not make the native selector explore or compel the advisor to follow the card. Stable Python 2.6.2 and TypeScript 2.5.2 snapshot scope, candidates and context before the callback and reject malformed logging distributions without normalization or retries. Caller probabilities are not authenticated randomization. This is an optional integration pattern, not evidence of improved performance.

The callback must return chosen_action_key, its actual action_probability and complete behavior_probabilities. Do not pass an LLM's verbal confidence as a selection probability. A deterministic advisor logs probability 1 for its choice and 0 elsewhere, with limited retrospective support. The helper never executes an action or reports an outcome. Independently authorize execution and report the actual outcome afterward.

Python: evidence-guided selection with honest logging
async def advisor(card):
    # Implement one application-owned model call using this evidence card.
    # Return its validated action key; this is deterministic routing, not
    # a calibrated randomization probability or an execution permit.
    chosen = await choose_allowed_action(card, allowed=["logs", "metrics"])
    probabilities = {key: float(key == chosen) for key in ["logs", "metrics"]}
    return {"chosen_action_key": chosen, "action_probability": 1.0,
            "behavior_probabilities": probabilities}

receipt = await client.proofloop.decide_with_advice(
    policy_key="sandbox.inspect", collection_id=collection_id, user_id="alice",
    context={"issue": "slow"},
    candidates=[{"action_key": "logs"}, {"action_key": "metrics"}],
    advisor=advisor, idempotency_key="before-action-001")
# No external action was executed. Separately authorize, execute, then report.

Experimental transient advisor prior

Server-chosen posterior exploration accepts prior_action and prior_strength in (0, 5]. This caller proposal adds optimistic pseudo-evidence only to that decision, attenuated by 5/(5 + the proposed action’s exact-context effective evidence). Other actions retain their own outcome evidence. It is not a calibrated model-confidence score, persisted observation or autonomy evidence. The receipt records the actual mixed distribution; outcomes can overturn the proposal. It does not guarantee better early or overall performance.

V3 also attenuates proposal strength using completed outcomes where that same proposed action was actually selected, in the same context and contract. Untested proposals retain the caller's bounded strength; observed underperformance reduces it. Outcomes for actions not executed are never invented. This is selection-biased descriptive reliability, not calibrated proposer accuracy or a causal comparison. Transient priors have not met the existing early/overall performance qualification; evaluate them independently on your workload.

Weekly learning report and webhooks

GET /v1/learning/policies/{key}/report?days=7 and the authenticated Learning report dashboard show exact-scope observed leaders, successes, failures, unevaluated decisions and recorded readiness transitions. Supply the same collection/user scope as writes; blank scope is unscoped, not an all-user aggregate. History is bounded to 2,000 rows and 50 contexts; truncation and the UTC window are explicit.

Uplift is shown only when the separate evaluator identifies its supported retrospective conservative-policy diagnostic. New adaptive-policy uplift remains not_identified_for_current_policy; raw action averages are not a causal comparison, and normalized reward differences are not automatically percentages of tickets resolved. Missing outcomes remain missing.

Subscribe to learning.autonomy_promoted, learning.autonomy_demoted and learning.change_alert. V2/v3 outcome writes transactionally record new readiness/alert transitions and stage delivery through the existing durable webhook system. Event IDs support deduplication; delivery can be retried. These events describe outcome readiness only, not current rule clearance or execution authority. No past transitions are fabricated, and configuration/time changes without a new outcome do not emit events.

Advisor evidence card

GET /v1/learning/policies/{key}/advice accepts URL-encoded JSON context, collection_id and user_id. It returns ranked actions, approximate 90% intervals, observed counts, configured priors, change diagnostics and an exploration candidate among eligible low-risk opt-ins. An LLM can read evidence_card; the suggestion is neither a new decision receipt nor permission. Obtain a fresh decision and independently authorize execution.

Caller-controlled logging
{
  "policy_key": "support.reply", "user_id": "alice",
  "candidates": [{"action_key":"concise"},{"action_key":"detailed"}],
  "mode": "observe", "chosen_action_key": "detailed",
  "action_probability": 0.5,
  "behavior_probabilities": {"concise":0.5,"detailed":0.5},
  "context": {"segment":"premium"},
  "idempotency_key": "decision-before-action-001"
}

behavior_probabilities must cover exactly all candidates, sum to 1, and match the chosen probability. A chosen probability alone cannot establish support for unchosen actions. Caller-reported randomization is not independently verified. Evaluation uses a chronological 70/30 split with point-in-time training, support, missing-outcome, effective-sample and metric-contract checks. ready and evaluation_ready concern evaluability; recommendation_ready is separate. Inspect exclusion_counts when estimates are null. Retrospective estimates never automatically permit promotion.

Declare relevant context explicitly

Without a schema, the full context hash can fragment on unique ticket IDs and fall back to globally pooled action history within the same owner/user scope. Receipts warn about this. Hebbrix does not automatically discover which fields matter.

PUT /v1/learning/policies/support.reply/context-schema
{
  "user_id":"alice",
  "context_schema":{
    "schema_version":"outcome-context-v1", "version":"segments-v1",
    "fields":{
      "segment":{"values":["premium","standard"],"required":true},
      "channel":{"values":["chat","email"],"required":false}
    },
    "backoff":[["segment"]], "unknown_fields":"ignore"
  }
}

Use 1–12 categorical fields, each with 1–32 distinct string, boolean or integer values. There is no type field. required defaults to true and at least one field must be required. Backoff is a maximum of three strictly narrowing lists of field names; every level retains all required fields and the final level is exactly the required set. unknown_fields controls undeclared keys, not unseen category values.

Unknown categorical values remain a validation error: silently pooling an unseen required segment would break the safety boundary. Schema contracts are immutable; plan a separately qualified policy/version for new categories. There is no automatic schema extension or default cross-segment fallback. Per-user isolation also means no implicit population prior; deliberate shared policies require a separate data/consent design.

Metrics and two distinct guardrail contracts

Maximize metrics map the configured range linearly to −1…1; for a 0…100 metric, 50 is neutral and 90 becomes 0.8. Minimize reverses the scale. These are reward utilities, not probabilities. Definitions are frozen into each decision, so later edits do not regrade old receipts.

is_guardrail, guardrail_operator and guardrail_threshold alone define a raw-unit, per-decision penalty. A violation forces that decision’s composite reward to −1; it does not promise a maximum population complaint rate.

For an explicit action-rate constraint also configure max_violation_rate. Example: a binary complaint metric with lte threshold 0.1 and maximum violation rate 0.1 treats complaint=1 as a violation, then requires the action’s conservative violation-rate upper bound to be at most 10%. This is separate from the revenue reward.

The aggregate gate uses at least 20 same-context, same-metric-contract decisions from the most recent 2,000, a simultaneous one-sided Hoeffding bound across candidates and guardrails, and conservative treatment of missing, immature and low-confidence feedback. Twenty observations will often be insufficient for a strict cap. Unknown evidence fails closed with 409 rather than falling back to an unsafe baseline. Exploration is restricted to eligible actions. observe can record independently collected outcomes but does not authorize data collection. Bounds assume independent stationary outcomes; those assumptions and caller reports are not independently certified. Protected episodes and retrospective constrained-policy evaluation are not supported by this new opt-in gate.

Finals, procedure feedback and confidence

A conflicting final for the same decision metric returns 409. Revise it explicitly with an observation using source: "correction"; prior evidence remains auditable. Use the Idempotency-Key header for atomic request replay and per-observation keys where needed. Missing outcomes remain unknown.

Procedure execution completion now leaves the outcome pending until feedback. Success rate counts explicit feedback only, and confidence is the Beta(1,1) smoothed success estimate, not a guarantee. Exact structured-condition matches rank outcome reliability before wording. Legacy execution-only records are not relabeled as successful feedback.

/confidence semantically ranks bounded decision history and preserves distinguishing action identifiers and time qualifiers. It exposes matched decision IDs and separate no_evidence, mixed, success and failure evidence states. Retrieval remains fallible and does not prove causal transfer; sparse histories cannot bypass the conservative action gate. Its advisory result never grants execution permission.

ASK → REVIEW → ACT is advisory

Memories alone cannot earn ACT. memory_support describes memory evidence; action_confidence is the eligible action’s outcome lower bound (or zero when blocked), not the old text score. The legacy confidence/answer_confidence fields remain groundedness signals and must not be thresholded into permission.

For configured action advice, send policy_key, action_key, JSON context and query to GET /v1/confidence. These structured fields bind the configured action and target; query text is checked for conflicts, including different targets, operations and parameters. matched_action explains the binding and conflicts; its match score is not a calibrated semantic probability. Round9 supports canonical user_id and retained aliases end_user_id/enduser_id; do not send conflicting values. The default low-risk gate requires at least eight final full-confidence binary outcomes and a 95% Wilson lower bound of at least 0.65. Eight successes give about 0.676. Only matching frozen action/target, exact context and current metric contract qualify; priors, unbound legacy history and protected episodes do not. A latest failure immediately demotes; independent rules, incomplete scans and failed aggregate guards prevent ACT. Query-conflict detection is bounded, not universal language understanding. ACT never inherits or grants execution permission.

An unspecified environment remains ASK with needs_scope: ["environment"]. Ask the caller to supply that scope, then request fresh advice; do not silently choose a default or treat the hint as permission. Explicit incident clauses such as ; error rate is 37% are descriptive measurements, not action parameters. This finite syntax does not interpret every natural-language observation; operational amounts, limits and quantities remain constrained.

Tool proposals never imply execution

With tool_advice, the non-streaming proxy returns deterministic proposal-status text for each choice. No-call replies say that no external action was executed; annotated calls are proposals only; hold mode withholds every call, even ACT advice. Read memory_context.tool_advice.response_status and per-call verdicts. The same status text is stored in history, preventing unchecked completion prose from becoming a later execution claim. This opt-in mode deliberately replaces free-form generated text; it does not certify execution results supplied by callers.

The model is instructed to propose a supplied tool when explicitly requested and its required arguments are known, while still respecting tool_choice. Automatic tool selection remains model-dependent. For an application that requires a proposal, use OpenAI-compatible tool_choice: {"type":"function","function":{"name":"your_tool"}} or "required"; this requests a proposal, never authorizes execution or bypasses advice.

Inspect each choice's selection and completion contracts rather than inferring success from prose. Auto permits no call; required asks the model to choose among the supplied tools. None/named selection violations and explicit length/filter termination withhold calls even in annotate mode. Unknown termination remains unverified. These engineering guards preserve contracts; they do not qualify automatic-proposal reliability or hybrid performance.

Complete named-tool request — proposals remain held
{
  "model": "gpt-5-nano",
  "messages": [{"role":"user", "content":"Use flush_cache for the sandbox Redis cache."}],
  "features": {"memory":false, "user_profile":false, "personalization":false, "learning":false},
  "tools": [{"type":"function", "function":{
    "name":"flush_cache", "description":"Propose a separately authorized sandbox cache flush.",
    "parameters":{"type":"object", "properties":{"cache":{"type":"string"}},
                  "required":["cache"], "additionalProperties":false}
  }}],
  "tool_choice": {"type":"function", "function":{"name":"flush_cache"}},
  "tool_advice": {
    "mode":"hold",
    "bindings":{"flush_cache":{
      "policy_key":"sandbox.cache.v1", "action_key":"flush",
      "context":{"alert":"error_spike"},
      "target_argument":"cache", "fixed_arguments":{}
    }}
  }
}

Set up the referenced policy, action, target and context first. The required target_argument names the tool parameter bound to that target; any other parameters must match exact fixed_arguments. This selection contract asks for a proposal only: held calls are not executed, and ACT advice is not permission. Model-dependent automatic no-calls are not fixed by forcing calls from ambiguous wording.

Procedure creation uses objects, not strings

POST /v1/procedures — complete structured review example
{
  "name": "Review a sandbox cache incident",
  "description": "Store a review procedure, not permission for a production action.",
  "condition": {"expression": "sandbox cache error spike"},
  "action": {"steps": ["Inspect current cache metrics", "Request approval before a cache flush"]},
  "category": "sandbox-review", "parameters": {}
}

condition and action are object-valued HTTP fields. SDK convenience fields such as trigger_condition and action_sequence map to those objects. Creating or completing a procedure does not fabricate successful feedback or authorize its external steps.

Legacy decision outcomes and ordinary learning receipts both inform scoped recall and failure warnings. A learning correction replaces the current signal rather than adding a second trial. Arbitrary composite rewards, pending/provisional records and partial-confidence signals are not binary success trials. Evidence remains caller-reported; independence, stationarity and transfer are not verified.

Failure-aware recall

Link supporting memories using evidence_manifest.references with kind: "memory", reference_id and role: "supporting". Search uses outcomes as a final ordering input without rewriting relevance scores. Read rank, outcome_boost and metadata.outcome_evidence, including attempt IDs and coverage. Relevance votes are a separate signal, not successful executions. Cached results re-read current outcome evidence.

The chat proxy includes linked outcome counts and attempts by default in memory_context.outcome_evidence and its model context. It asks the model to warn about failures and consider supported alternatives. This cannot guarantee that a model will never repeat a mistake. Unavailable outcome scans are disclosed, not treated as evidence that an approach has never failed.

Write completion and honest search scores

For extraction, wait_for_index: true with omitted async_dispatch waits through the synchronous path and bounded indexing check. Explicit async_dispatch: true conflicts with that flag and returns 422 before writing. Background callers should poll the returned canonical job URL; a 202 receipt is acceptance, not searchable completion. Indexing timeouts remain explicit rather than claiming success.

Search scores are ranking signals, not calibrated correctness probabilities. Prefer top-k with min_score: 0, then inspect grounding and evidence. A useful first-ranked paraphrase can have a low numeric score; no universal 0.3 or 0.5 cutoff is promised. Explicit “summarize this user” Reason queries return bounded source quotations across the exact user/agent/run scope; memory_limit is 1–50 (default 20), with truncation disclosed and quotations marked not independently verified.

Decision creation follows the configured plan request limit: defaults are free 60, starter 300, pro 1,200, scale 2,000 and enterprise 3,000 per minute. Other expensive endpoints retain separate limits. Use bounded concurrency, backoff and idempotency. Round9 batch routes retain per-item guards and tier admission; batching is not an unlimited bypass. Source SDK helpers and published SDK release versions are separate—use the canonical released HTTP contract until your installed SDK supports a new field.

The candidate raw_rerank_score, when available, is an unrounded provider signal or local logit, not a probability or a universal threshold. Unsupported providers may return null. Compact search never drops the grounding/safety envelope merely to reduce optional diagnostics.

The shared-IP edge limit is separate: 2,000 requests per five-minute window, with a separate signup-path limit. A rate rejection uses JSON 429 and Retry-After: 300; eligible GET health paths are excluded from that general rate rule, not from managed security checks. Account limits still apply, and a security-rule 403 is not a rate-limit response. Back off with jitter; do not probe limits with a production burst.

Read the latency guide for end-to-end measurements versus objectives. No sub-50ms end-to-end search guarantee is made. See experiences, reviewed lessons and constrained permits for the separate beta execution workflow.

Explicit causal loop. Outcome Memory does not claim to infer success from ordinary traffic. Your agent records the candidate set and selected action, then reports a delayed result. Hebbrix keeps propensity, uncertainty, correction, and idempotency evidence tenant-scoped.

01. How Outcome Memory Learns

A reliable learning loop separates the decision-time record from the later observation. Missing outcomes remain censored; they are not silently converted into failures or synthetic rewards.

  1. Decision Receipt. Before acting, record the policy key, authorized candidates, context, collection, and idempotency key.
  2. Propensity Evidence. Store the selected action and its probability so later evaluation can distinguish policy evidence from cherry-picked successful examples.
  3. Delayed Outcome. Report success, reward, or typed business metrics after the real-world result is known.
  4. Correction Semantics. A corrected outcome revises the evidence for the original decision instead of creating another decision.
  5. Posterior Uncertainty. Policy insights expose sample counts, posterior probability, and uncertainty rather than one opaque score.
  6. Conservative Evaluation. Promotion evaluation can refuse weak or non-randomized evidence instead of declaring a winner.

02. Agent Autonomy Features

Confidence, corrections, and Outcome Memory help agents decide when to act, when to abstain, and how to learn from results:

  • Confidence Scoring. Agents fetch confidence scores from GET /v1/confidence before acting to check "do I know enough to act without asking the user?" Returns a score and recommendation, including a hard do_not_act result when operational constraints conflict with the proposed action.
  • Correction Memory. When a user corrects an agent, it calls POST /v1/corrections. The correction is stored as versioned evidence. Agents still need to retrieve and apply relevant corrections; no system can guarantee that a mistake never repeats. You can also submit explicit user feedback via POST /v1/feedback/relevance (see the API Reference below for the full feedback, decisions, and confidence schemas).
  • Decision Receipts & Delayed Outcomes. Use POST /v1/learning/decisions before acting, then report the observed result to /v1/learning/decisions/{id}/outcomes. The older /v1/decisions route remains a decision log, not an automatic policy-training guarantee.

03. What Happens Behind the Scenes

  1. Your application supplies the actions it is actually allowed to take
  2. Hebbrix records the candidates, mode, choice, and action probability
  3. The agent performs the external action under your authorization rules
  4. Your application reports the delayed outcome or business metric
  5. Hebbrix updates policy evidence idempotently, including corrections
  6. You inspect insights and evaluate evidence before changing behavior

04. Safety & Quality Controls

  • Caller Authorization. Outcome Memory ranks only the candidate actions your application supplies. It does not grant tool, data, or side-effect permissions.
  • Bounded Exploration. Default epsilon exploration is capped at 0.2. Explicit low-risk posterior sampling uses its separately recorded finite distribution. Neither grants action authority.
  • Censored Missing Feedback. A missing or late outcome is not interpreted as failure. Final observations and corrections remain attached to the original decision.
  • Isolation & Idempotency. Collection and user scope prevent cross-tenant evidence leakage, while idempotency keys make decision and outcome retries safe.

05. Outcome Memory API

The causal learning surface is explicit and inspectable:

  • POST /v1/learning/decisions: choose or record an action receipt
  • GET /v1/learning/decisions/{id}: inspect the immutable decision context
  • POST /v1/learning/decisions/{id}/outcomes: report delayed or corrected outcomes
  • GET/POST /v1/learning/metrics: define and inspect typed business metrics
  • GET /v1/learning/policies/{key}/insights: inspect posterior evidence
  • POST /v1/learning/policies/{key}/evaluate: evaluate whether evidence is sufficient
The Bottom Line. Outcome learning is powerful only when your integration closes the loop. Record the decision before acting, report the real result afterward, and use the returned uncertainty when deciding whether evidence is strong enough to change policy.

06. API Reference

These established confidence, correction, decision-log, and relevance endpoints complement the Outcome Memory API above. They do not by themselves train or promote a policy. All require authentication (Authorization: Bearer <api_key>).

07. Schemas & Scoping

Two rules trip agents up most often: the strict enums on the create schemas, and the difference between the create body and the read endpoint's query parameter.

  • decisions.outcome normalizes to an enum. POST /v1/decisions stores one of success, failure, partial, or unknown (default unknown), but accepts common synonyms and maps them, e.g. positive/good/ok/pass → success, negative/bad/fail/error → failure, mixed → partial, neutral/na → unknown. Only a genuinely-unrecognized value, for example safe_block, returns HTTP 422. decision_type is a free string up to 50 chars; description is required.
  • corrections.correction_type is an optional enum. POST /v1/corrections requires only corrected_content; correction_type is optional and defaults to preference. When supplied it must be one of preference, factual, or procedural — any other value returns HTTP 422.
query is a read parameter, not a create field. query belongs to the GET read endpoints, /v1/corrections/relevant and /v1/decisions/similar, where it describes the agent's current task. The create schemas reject unknown fields, so sending query in a POST body to /v1/corrections or /v1/decisions returns HTTP 422.

Tenant isolation via collection_id. Both corrections and decisions are tenant-isolatable. Pass collection_id when creating to scope a record to one collection. On the read endpoints, collection_id enforces strict isolation (only that collection's records), and include_global additionally folds in account-wide records. Omitting collection_id keeps everything account-wide, fully backward compatible.

Compatibility and deprecation

Pin the installed SDK version in production and read its changelog before upgrading. Stable SDK packages use semantic versioning; additive optional fields/methods are distinct from incompatible contract changes. Versioned policy contracts preserve their stored context/action/outcome meanings and do not regrade old receipts. Experimental features are labeled separately; their presence in local source is not a published support commitment.

Security and correctness guards may tighten immediately. Incompatible changes require a documented replacement and migration guidance, but no fixed 90-day support or deprecation window is promised here. Check GET /v1/release, canonical OpenAPI, and GET /v1/users/me/capabilities for the actual deployed artifact and account matrix. Release cadence, private fixtures and source changes do not establish adoption, compliance, calibrated safety, real-world reliability or learning superiority.

curl
# Create a correction scoped to one tenant/collection
curl -X POST https://api.hebbrix.com/v1/corrections \
  -H "Authorization: Bearer $HEBBRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "corrected_content": "Refund approvals above $500 require human review",
    "correction_type": "procedural",
    "original_content": "above $250 require human review",
    "collection_id": "col_tenant_acme"
  }'

# Read corrections for that tenant only (strict isolation)
curl "https://api.hebbrix.com/v1/corrections/relevant?query=approve+a+refund&collection_id=col_tenant_acme" \
  -H "Authorization: Bearer $HEBBRIX_API_KEY"

# ...or include account-wide corrections alongside the tenant's
curl "https://api.hebbrix.com/v1/corrections/relevant?query=approve+a+refund&collection_id=col_tenant_acme&include_global=true" \
  -H "Authorization: Bearer $HEBBRIX_API_KEY"

# Log a decision (outcome normalizes to success|failure|partial|unknown, synonyms accepted; query is NOT a body field)
curl -X POST https://api.hebbrix.com/v1/decisions \
  -H "Authorization: Bearer $HEBBRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "decision_type": "refund_approval",
    "description": "Auto-approved a $120 refund",
    "outcome": "success",
    "agent_confidence": 0.82,
    "collection_id": "col_tenant_acme"
  }'
Ask the docs
reading · this page

Hi! I'm the Hebbrix docs assistant. Ask me anything about this page: setup, code examples, endpoints, pricing, or integrations.