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.
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.
| Field | What it does |
|---|---|
domain | The subject being scored. Arbitrary label, chosen by you. |
success | How it went. The score is a function of these and nothing else. |
failurePattern | On a failure, added to the domain’s known failure patterns — the part that reaches the prompt. |
strategy | On 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.