How it works

The self-model

Reliability per domain, over outcomes you record. Nothing is inferred — which is the point, and also the most common reason it looks like it is not working.

On this page

What it tracks

Reliability per domain, tracked as a moving average over outcomes you record. Any active domain below 75% is written into every prompt under a Weak Domains — Under 75% Reliability heading, listing its known failure patterns and what has worked.

ts
await memory.selfModel.record({  domain: "database",  success: false,  failurePattern: "migrated the schema without a dry run"})

A domain is a label you choose and pass on the turn — the same domain the quickstart sets. There is no classifier deciding what a task was about, because a misfiled domain produces a confident score about the wrong subject.

Why the average carries a prior

One detail worth stating plainly, because the obvious formula gets it wrong. The average carries a prior of two samples.

Dividing by the sample count makes the first outcome fully replace the starting estimate, so a single failed task takes a domain from 0.8 to 0 — and the domain then trips the 75% threshold forever, on the strength of one data point. With the prior, one failure is a strong signal and ten are conclusive, which is what a reliability number should mean.

Concretely, with a domain that started at 0.8: the first failure moves it to 0.533, the second to 0.4, and the weight keeps shrinking — 1 / (min(samples, 10) + 2) — so early evidence moves the number and later evidence refines it. The naive version would have put it at 0 on the first failure and left it there, tripping the guardrail on the strength of a single task.

A prior is not a thumb on the scale in the direction you want. It is the reason one observation cannot manufacture certainty in either direction — which is the only property that makes a score worth displaying.

What counts as an outcome

Whatever POST /v1/self-model/outcome accepts: a domain, a boolean success, and optionally a failurePattern or a strategy.

The optional strings are what turn the score into something actionable, because “database: 0.6” does not suggest a fix and “database: 0.6, always migrates without a dry run” does. A failure contributes its failurePattern to the domain’s list; a success contributes its strategy. Both are deduplicated, so recording the same pattern twice does not pad the prompt.

FieldWhat it does
domainThe subject being scored. Arbitrary label, chosen by you.
successHow it went. The score is a function of these and nothing else.
failurePatternOn a failure, added to the domain’s known failure patterns — the part that reaches the prompt.
strategyOn a success, added to the domain’s recommended strategies.

Every sample behind a score is retained, not just the aggregate. The self-model page shows them individually, which is the difference between a number and an explanation.

Nothing here is inferred

Without outcomes recorded through POST /v1/self-model/outcome, the model stays at its priors and no weak-domain warning ever fires.

The same caution applies to tensions: both features are fed by explicit signals, and a deployment that records neither will have a perfectly healthy store that teaches its agent nothing about itself.

How a weak domain reaches the prompt

As a guardrail at L0 — a full body, always, outside the budget — headed Weak Domains — Under 75% Reliability. It carries the domain’s known failure patterns and what has worked, which is the form that changes behaviour: “you have been unreliable at database” is a fact, and the patterns are instructions.

The threshold is 75% because below that a domain is costing more in rework than it saves in context. It is a deployment-wide constant rather than a setting, on the grounds that a per-organisation reliability threshold is a number nobody would be able to justify tuning.