Intent
Represent the epistemic status of a memory explicitly and make promotion, rejection, correction, retrieval, and pruning depend on that status.
The problem
An LLM-generated fact, a user assertion, a document sentence, and a corroborated observation do not deserve the same authority. A single active/inactive flag hides that difference. Confidence scores alone are also ambiguous: they often mix truth, retrieval relevance, and model certainty.
The pattern
Start with a small state machine:
Diagram source
%% caption: the states a memory moves between and what moves it — corroboration and review promote, contradiction and correction reject, and reactivation is explicit rather than automatic
stateDiagram-v2
[*] --> Candidate
Candidate --> Verified: trusted source or corroboration
Candidate --> Rejected: review or contradiction
Verified --> Stale: age or superseding evidence
Verified --> Rejected: correction
Stale --> Verified: revalidation
Rejected --> Candidate: explicit reactivationKeep separate dimensions for:
- Trust state: whether the memory may be treated as established.
- Epistemic confidence: how strongly the evidence supports it.
- Retrieval strength: how useful or reachable it has been.
Retrieval policy can then prefer verified memory, include candidates with visible uncertainty when necessary, and suppress rejected records from ordinary context.
Why it works
The state machine gives every transition a policy boundary. It creates places to require corroboration, human review, held-out evals, or privileged actors. It also makes correction and audit language precise: a memory was rejected, not merely assigned a lower similarity score.
Tradeoffs
More states create more transitions, UI, and operational policy. A candidate queue can grow forever. Verification can become theater if the verifier repeats the same model or source. Stale is temporal uncertainty, not proof of falsity. Promotion rules must be explainable and scoped to the consequences of being wrong.
For low-risk note retrieval, a full trust machine may be excessive. It matters most when memory changes decisions, identity, permissions, or long-lived behavior.
Cost to adopt
Build: a status column, the legal transitions, and a promotion rule. The column is trivial; the promotion rule is a policy decision that no schema makes for you.
Forces elsewhere: retrieval must filter by status, or the states are decoration. Every consumer — prompt assembly, exports, summaries, the UI — has to decide which states it accepts, and each is a place to get it wrong.
Ongoing: candidates pile up unless something promotes or expires them, and "what verifies a candidate" is a question most systems never answer, which is how a trust model quietly becomes an unused column.
Skip it if you have no verification signal at all. Three states with nothing able to move a memory between them is worse than one honest bucket.
Seen in the atlas
Read these first
- OmniIntelligence —
candidate,provisional,validatedanddeprecatedon one column, with a lifecycle filter on all three injection queries. - AgentDatabase — a
disputedstate that takes a claim out of service without anyone deciding it is wrong, and a status that gates what the agent boots with. - Hats — a
canarystate betweendraftandactive, injected into a deterministic half of runs so the other half is a control group. - Magic Context — a lifecycle status the reads filter on, beside a verification status that gates nothing: the difference between a state and a label.
- Membrane — the enum without the read predicate, which is the failure this page exists to name.
Every instance
Portable Handoff
contributes a narrow enforcement half without the filtering half. Its
Trust enum is
verified · observed · claimed · inferred · untrusted, its
Provenance enum names eight channels, and
cap_trust(provenance, trust) downgrades
verified to claimed on any claim whose
provenance is not git, tool,
test, file or transcript —
applied while parsing, so a capsule from another tool
meets the same rule. The draft author writes the provenance as well, so
the cap binds the pairing rather than the source: a claim declared
tool and verified passes. The question this
page raises — who is allowed to set the state — gets half an answer:
local code sets it for the re-hashed files and, through a preflight file
finalize reads back without re-checking, for the repository
snapshot; the author sets it for everything else. Nothing acts on it:
every state is rendered as a label and no read path filters, ranks or
omits, so the mark is withheld.
Magic Context keeps
two independent axes rather than one column, and only
one of them is a state in this page's sense. status is
active | permanent | archived — where a memory sits in its
lifecycle — and ten reads of the memories table narrow on it.
verificationStatus is
unverified | verified | stale | flagged — what is known
about its truth, written by a measurement against the files a memory
describes — and it appears in no WHERE clause: it is
surfaced to a reader and gates nothing. A memory can be
active and stale at once, which a single enum
cannot express; the axis that says so is the one no read path
consults.
Gini declares a five-value
enum — proposed | active | archived | rejected | conflicted
— behind a CHECK constraint, and all four recall channels
admit only active. The only native write of a non-default
value is archived, for observation rows;
proposed, rejected and conflicted
are produced by no engine path and arrive only through a migration
importer, where the filter honours them. Gini also carries a
network of
world | experience | opinion | observation, so what
kind of claim it is stays separate from how much it is
believed.
Verel remains the reference for how states participate in recall, promotion, consolidation, and pruning, and for separating epistemic confidence from retrieval strength. RainBox ties the transitions to an actor model and an operator review queue.
Daimon keeps two questions apart
and gives only one of them a filter. Its verbatim /
inferred field is a provenance label fixed at write: the
item ships with the class the extractor chose, the quote is matched
against the transcript and a miss forces the item down to
inferred, and an outcome claim with no tool-result citation
is forced down even when its quote matched. Supersession, overturn and
contradiction then act as demotions, and the project's comments rule out
the filter for them explicitly: an overturned item is still evidence, so
it "ranks down and renders flagged rather than
disappearing."
The state that withholds is a person's. A quarantine ledger folds to
candidate, active, dismissed or
released per value, and an active value is
withheld by one read view that the briefing, the recall index and the
viewer all read through — never inserted into the index, and judged
again on every hit — until a human releases it, while an unreadable
ledger withholds every item. An agent may propose one and cannot confirm
it. The source states the rule in one line — "a human-confirmed
verdict may withhold; a machine signal only ever ranks down" — and
the report carries trust_state on that ledger. The
id-addressed reads and every ledger printer go through the same view,
and the project's read census asserts that no surface shows a
quarantined value to an agent.
Its refutation ledger answers a different question: how do you
know a human approved it. A refutation — an approach recorded as
having lost under cited evidence — folds to candidate,
active or overturned, and the transition to
active requires human authority. What makes it worth
copying is that authority is not a field the caller sets: every row
stamps the channel the write actually arrived through
(cli-agent, cli-tty, ui,
signed, mechanical), and a table maps channel
to authority. A --by human flag was deleted for being
"a flag whose only function was to let the caller assert its own
authority… an actor acting as witness for its own claim"; what
survives is --by agent, which can only narrow. The human
path is the absence of that flag plus an interactive terminal, and the
two strongest channels are unreachable from the CLI entirely,
"because a channel an agent can reach by shelling out is the deleted
--by human renamed." A hand-edited
ratified: true on an agent row changes nothing, because the
fold re-derives authority from the channel — there is a test for exactly
that. The design states its own ceiling in the same comment: nothing
local is unforgeable, and what this earns is provenance rather than
proof. A revision returns an active record to candidate
unless the revising channel is itself human, so approval attaches to the
content and not to the row.
The same design also separates lifecycle from epistemics the way Magic Context does, but by location rather than by column. Trust lives on the item and is append-only; liveness — resolved, reopened, superseded-candidate, forgotten — lives in an event log folded at read time. That split has a concrete payoff: the system can add a lifecycle state without rewriting a single stored memory, and a rejection recorded in the wrong stream would hide the item instead of demoting it, which its own source comments call out as the reason the two logs are kept apart.
Two systems show the states are only half the work. Gini declares
conflicted and no engine path ever writes it, and MateClaw ships a dedicated
ContradictionDetector and a member-gated resolve endpoint
whose recorded verdict nothing reads. A conflict state nothing enters,
and detection without a path to resolution, both leave the operator
holding a list.
Counterexamples remain instructive. Holographic collapses truth and
reachability into one trust_score that feedback mutates
directly. Mercury grades
confidence, importance, and durability separately — good — but assigns
all three at extraction, and a merge only ratchets each upward with
Math.max, so they are estimates rather than states that
change with evidence. Cognee has
rich provenance and ontology validity with no factual promotion state;
Claude-Mem and A-MEM activate generated content with
none at all.
Graphify is the label without
the state. Its three bands — preferred,
tentative, contested — are none of them
supplied by a model; all three are derived on each run from a
directory of outcome-tagged Q&A files, from a signed, time-decayed
score and a count threshold. A source cited by one successful answer is
tentative, and only a second distinct
result moves it to preferred; the threshold is a
parameter with a test asserting it is not hardcoded, and the docstring
gives the reason — "one save can't mint a trusted lesson."
contested is entered by the presence of both a positive and
a negative signal, then given a verdict by the sign of a
30-day-half-life score. Every consumer then annotates and nothing
withholds: the band is appended to the node line the model reads,
printed, coloured and collected into a start-here list, and the code
that loads the overlay says it is there to annotate
"display-only". So a tentative node is shown
exactly as a preferred one is, with a different suffix —
named bands on a confidence score, used for ordering and advice, which
is why the report withholds trust_state.
Two design choices carry over to a real state machine. The bands are
stored on a derived sidecar and recomputed wholesale,
never written back into the structural store — so the layer is
disposable and rebuildable, and changing the threshold or the half-life
is an experiment rather than a migration. And the band reaches
the model as a suffix on the thing it qualifies
(learning=contested:stale beside the node), rather than as
a separate block a reader has to correlate.
CLIO is a tier that costs something
and withholds nothing. Its two states — unverified and
trusted — act in three independent
channels: a literal [UNVERIFIED] badge on the
entry in the per-request memory block, a 0.3x multiplier in
the score that decides which entries survive when a type exceeds its
cap, and a 30-day age-out against 90 with doubled confidence decay in
the consolidation pass. None of them filters. The selector,
score_ltm, has no tier term, so the two states compete for
injection on equal terms although the project's docs say otherwise. That
is the ranking side of the line the rubric draws, and the report carries
no trust_state for it. A tier that only filters is
invisible to the model, and one that only badges is invisible to the
ranker, which is where CLIO's selector leaves it.
Promotion needs two corroborations from distinct
agent:session pairs, with the source identities
stored as an array rather than a count so independence is checkable, and
the unconditional override is wired only to the
/memory promote slash command a person types. The identity
is where it gives way. Both shipped entry points assign
CLIO_AGENT_ID and CLIO_SESSION_ID at startup,
and the library falls back to 'unknown' for anything that
embeds it without them — a fallback a committed regression test pins
deliberately beside a second subtest asserting that two distinct sources
promote. And add_corroboration is an operation of the
model's own memory tool whose schema exposes
source_agent and source_session as optional
strings, passed straight into the sybil key, so two calls naming two
sources promote an entry to [TRUSTED] without a second
agent or a person. update_ltm, on the same tool, rewrites a
trusted entry's text and leaves its tier and source list in place.
Two lessons generalize. A trust threshold is only as good as
the identity it counts: if independence is the property, the
identity must come from the runtime, not from a default and not from a
caller-supplied argument. And a silent fallback converts missing
configuration into a policy change — // 'unknown'
is a defect for every caller that does not set the variables.
Memory Palace is a
procedural instance, and its state reaches the read path
directly. review_state is
draft | human_reviewed | rejected, validated on
construction, and recommend_for_trigger returns only
human_reviewed rows — the docstring calls this "the
read-side enforcement of the draft-by-default invariant".
approve_draft refuses a rejected row ("create a new draft
instead of approving a rejection") and increment_success
refuses anything not approved, "so the success counter truly reflects
approved usage". The gap is the one this pattern always has to be
checked for: nothing compares a new draft's source_hashes
against rejected rows, so the state is durable for a row and not for a
claim.
Two systems in this atlas show the failure mode from opposite
directions. memory-lancedb-pro has the
enum (pending | confirmed | archived) and a read-path gate
on it, and then wrote the candidate state out of existence: every writer
emits confirmed, four of them with the comment "write
confirmed to unblock auto-recall". The gate survives over an empty
population. YesMem has the opposite
half — a real trust model computed from use count, source and
importance that downgrades a supersede to
pending_confirmation — and no state machine to receive it:
nothing reads or clears that value, so the correction is simply dropped.
A trust state needs both the field and a transition somebody can
make.
Midas is a deliberate
non-instance. Its provenance field is a discrete four-value
enum consulted on the decision path, and it is not a trust state: it
records where a memory came from, not what anyone believes about it.
Separating authority from credence turns out to be the
more useful axis for gating actions, and conflating the two because the
field is an enum is the easy mistake.
breadcrumbs is the promotion
half of this pattern on its own, without the mark, and the interesting
part is where it puts the refusal. Its semantic tier carries
two states — asserted and verified — and
store_fact() writes asserted unconditionally,
on a stated rule: "Nothing an agent stores starts verified."
Promotion goes through one function, verify_fact(), which
raises before it writes on empty evidence, an unnamed verifier, an
unknown authority class, agent authority, and a verifier
equal to the asserting actor. The first refusal states the rule:
"verified requires naming the oracle (a CI run, a data assertion, a human ruling); an agent may not mark its own claim verified with nothing behind it"
The refusals convert "the model said so" from a default into
something a caller has to lie about deliberately, and the authority
class and both names are caller-supplied strings, so the lie is a
renamed argument. The context block renders the oracle inline beside the
value, so a reader of the prompt can see which claims nobody
checked. That label is all the state does: build_context
emits every fact that passes its time and scope masks whatever its
status, no read withholds an asserted one, and the mark is
withheld. Its correction ledger sharpens the same idea into an admission
rule — only ci_failure, data_assertion,
operator_ruling or reverted_pr count — with
one exclusion worth copying verbatim: "Model-vs-model disagreement
is never a correction." A stronger model disagreeing with a cheaper
one has no ground truth behind it, and a self-improvement loop that
admits it optimises a proxy.
OmniIntelligence runs two
state machines on one row and wires them in one direction,
which is the arrangement this pattern usually collapses. Lifecycle
status — candidate → provisional → validated → deprecated —
decides whether a pattern may be injected. An evidence tier —
unmeasured → observed → measured → verified — decides
whether it may advance. Evidence gates status and status never
touches evidence, so "how sure are we" and "what is this allowed to do"
cannot contaminate each other.
Two mechanisms are worth taking whole. The tier is monotonic
in SQL: the UPDATE that writes it carries a
CASE mapping each tier to a weight and only matches when
the new weight exceeds the stored one, so a redelivered message, a
concurrent writer and a caller with a stale read all fail by touching no
rows. And the transitions are asymmetric with a stated
band — promotion at a 60% success rate over five injections,
demotion at 40% over ten plus a five-failure streak and a 24-hour
cooldown — with the 20-point gap named in the constant's own docstring
as the thing that keeps variance from flip-flopping a pattern between
states, and the operator's override bounded so the band cannot be
closed.
The failure is instructive for anyone building a ladder.
verified is a valid value, is gated on, and is written by
nothing: the sole writer's pure function returns only
OBSERVED or MEASURED, above a docstring saying
verification "requires independent validation (not computed here)". A
top state nothing can reach makes every gate that names it unsatisfiable
while reading, from the schema and from the enum, exactly like a state
that works.
OmniNode's
knowledge base gives each kind of claim its own vocabulary instead
of one shared enum, which is the cheapest refinement on this
page. A decision record moves through proposed,
accepted, superseded, deprecated
or rejected; an architectural pivot through
observed, emerging, accepted,
historical or superseded; a doctrine principle
only draft, accepted or
deprecated. The vocabularies are Pydantic
Literals discriminated on a type field,
exported into the published JSON schema by the validator that enforces
them, so the contract cannot drift from the check. A pivot also carries
confidence — low, medium,
high — as a separate field from status, which is
this pattern's central discipline written into a schema: how sure you
are and whether the claim is current are different questions, and one
column cannot answer both.
The same repository shows the limit of a schema-only state machine.
Nothing reads a status back: supersedes and
superseded_by are validated as lists of strings and never
checked against each other, so the corpus's single supersession pair is
reciprocal by hand. A state machine enforced at the boundary and nowhere
else is a vocabulary, not a machine.
Flatbread Proof builds
the machine and leaves the readers outside it. Its Decisions
move from proposed to accepted,
rejected or superseded only through typed
mutations: acceptance refuses a non-proposed target and a second
accepted Decision on the same question Issue, rejects the
proposed siblings in one transaction, and a revived Decision cannot be
accepted while the Decision that rejected it stands
(packages/proof/src/decision-lifecycle.ts:31-116). No read
excludes a row by that state, and a Finding's invalidates
edge leaves its target accepted
(packages/flatbread/src/proof/read.ts:581-602,
packages/proof/src/planner.ts:428-440). The only property a
read filters on is a retracted boolean. Enforcing
transitions at the writer settles which states exist; only a read
predicate decides whether an overturned record reaches the next
session.
Ouroboros splits the
axis one further step than this page usually argues for, and the reason
is recorded in the code. Its ledger carries LedgerSource —
what kind of authority a value rests on, from
USER_GOAL down through REPO_FACT,
CONSERVATIVE_DEFAULT and ASSUMPTION — and,
orthogonally, DecisionProvenance: how the decision was
reached, from USER_CONFIRMED,
MODEL_INFERRED, TIMEOUT_DEFAULT,
LATERAL_CONSENSUS and MAINTAINER_POLICY. The
module says the second axis was added because a timeout-defaulted
decision had been indistinguishable from a user-confirmed one, so
degraded specifications executed silently — the failure this page's
confidence-versus-status split exists to prevent, found one level up.
Only the two model-derived provenances face a clarity gate before the
state may execute; the other three pass unconditionally, which is a
machine rather than a vocabulary because the gate is where the states
are read. USER_CONFIRMED comes from a regex over transcript
section labels: where text sat, not who wrote it.
Its pre-commit form repeats the split under different names:
CandidateContentSource for where content came from,
ConfirmationAuthority for who signed off, and
CandidateResolution for where it stands, so "why do you
believe that" and "who approved it" have separate answers and both are
stored.
Hats puts a
canary arm between draft and active, with a
control group no other system on this page keeps. A lesson
distilled from a failed run enters at draft with confidence
0.5, becomes canary on first injection, and while it is
unproven inCanarySlice — an FNV-1a hash of
runId:lessonId against a 0.5 share
(src/memory/lessons.ts) — decides whether this run sees it
at all. The runs that do not are a control group, deterministically so:
the same run and the same lesson always decide the same way, so a rerun
is comparable. Promotion then reads outcomes rather than intentions —
two acceptances at confidence ≥ 0.6 promotes; three rejections or
confidence below 0.2 disables with
retiredReason: 'contradicted by outcomes'; and six
injections with zero tag matches disables it as "never matched —
expired as noise", which separates wrong from
useless as exits. An explicit human correction skips the ladder
and is stored active at 0.9, verbatim, because a correction
is not a hypothesis. What the machine does not do is record its own
transitions anywhere: the counters and retiredReason are
overwritten in place by a whole-file rewrite, and the runtime's
hash-chained audit log is never told.
AgentDatabase adds the
state this page's implementations mostly lack: a claim can be
taken out of service without anyone deciding it is wrong.
dispute moves a record to disputed and sets
conflict.state to unresolved, and the
eligibility filter excludes it — so a contested memory stops being
answerable while the adjudication stays open, instead of forcing a
winner at the moment of conflict, which is the moment there is least
information. Caura and dense-mem hold the same position
with their own values: Caura's conflicted and dense-mem's
disputed both fall outside the status set their recall
reads admit.
Two details are worth copying beside it. The transition is written
before the mutation: append_transition appends
{transaction_id, operation, recorded_at, recorded_by, from_status, to_status, valid_to_before, valid_to_after, reason}
into recorded_time.transitions, then sets the new status —
so the audit trail is a property of the record rather than a side table
that can drift from it, and project_record_at can replay it
backwards to answer what the status was at a given record time. And the
refusals are stable error codes carrying no record content, on the
reasoning stated in the exception class: a failure message about a
memory should not become a second copy of the memory.
The caution is the same one the report states. The status field is
populated across three values in the live store — 6 active, 108
candidate, 84 retired — and disputed has never been used,
no record carries a transition, and nothing has ever superseded
anything. A state machine whose interesting transitions have not run is
a design, and the difference between a design and a mechanism is visible
only in the data.
Scope Recall answers
the question this page asks first — who may set the state — by giving
the answer to no caller. claim_versions.state is a SQL
CHECK over proposed, active,
disputed, superseded and
retracted, and select_effective never returns
a proposed or a retracted version, so the
state decides whether a memory may answer rather than where it ranks. No
promote, approve or apply verb exists on either adapter's tool surface:
a proposal becomes active because qualify
finds an authority-bearing root among the sources it cites, and stays
proposed otherwise. The candidate evaluator may re-propose
a claim against fresh sources, and its prompt ends "Do not choose a
fact state: Core qualification owns that decision" — enforced by
the code never reading a state from the model's reply.
Utopia is the version of this
pattern where the states are tables rather than an enum column, and its
source argues the choice in terms that apply to either. A claim sits in
pending_facts, facts,
derived_facts or rejected_facts; confirm and
reject move it. The source rejects the status-column version explicitly:
roughly fifty queries select live facts by
invalidated_at IS NULL, so a forgotten predicate would put
an unconfirmed fact on the graph, while a forgotten UNION
only makes the queue invisible — "failing in the direction where the
mistake is visible". The same judgement was made once before for
derived facts, after a first attempt with a flag on facts
let the low-confidence review queue offer deductions for confirmation
and let the temporal engine close a human assertion with a machine
conclusion. Beside the states sits a confidence float that
gates automatic interval closing and fills the low-confidence queue and
filters no retrieval result at all — with a comment saying a person's
verdict is not expressed as a number, it goes in the ledger.
GrayMatter declares
the vocabulary and wires nothing to it. Its confidence field
holds verified, inferred or
unverified, validated at the write path and rejected
otherwise — the shape this page asks for, including a state that should
withhold a memory from being treated as true. Every reader displays it:
the explain receipt copies it, the TUI prints it, the note exporter
writes it into frontmatter. It appears in no ranking, no filter and no
consolidation decision, and the field's own comment concedes "it
never affects ranking, decay or pruning." Its only producer sits on
the Go library interface and on no MCP tool or CLI command, so an agent
cannot mark its own inference unverified even for display. The result is
a store of claims wearing the vocabulary of a store of beliefs.
Membrane has the enum
and not the predicate, which is the failure this page exists to
name. Its revision status is
active · contested · retracted, declared with per-value doc
comments citing the specification, and four code paths write it:
contest, retract, supersede and a semantic re-activation. A search for
reads of that field at the tree root returns those writes, one erasure
in the audit path and the copy into the gRPC response — no filter and no
ranking term, so a retracted record comes back labelled
retracted and only a caller who reads the label knows. What
retrieval sees instead is the salience of zero that retraction and merge
set at the same moment, and that only excludes anything when the caller
passes a positive minimum, which both SDKs default to zero and the
project's own lifecycle eval raises so its retraction scenario passes.
Contest sets the state and leaves salience alone, so the one status that
means withhold this pending resolution changes nothing about
what retrieval returns. The repair is a status predicate on the root
query and on graph-neighbour hydration, which applies no salience floor
at all, so a superseded record can return beside its replacement even
under a positive minimum.
aios (rexleimo) earns the
filtering half and seals the transition. Its claimStatus is
decided inside the append from the writer's runtime identity, a shared
write without a publish capability lands candidate, and
every default memo read drops candidates. But authorize
denies promote, reject and expire unconditionally, so the state has an
entry and no exit. The writers that pass no identity — the MCP and Pi
write tools the model holds — land verified as
local-user, which is the case this page's question about
who may set the state exists to catch.
AlphaOne ai-memory
puts its one trust state on a lifecycle column built for work items, and
earns the mark on the filter rather than the vocabulary.
quarantined is system-only — refused as caller input and
absent from every caller transition — and is set by federation receive
on a relayed write whose author signature did not verify, when
AI_MEMORY_FED_QUARANTINE_UNATTRIBUTED is on. Reads admit
only an allow-list of five other states, so the row is stored but
invisible until a signed copy of the same id verifies and a raw update
returns it to open. Local writes never enter the state, and
the knob defaults off.
Attune AI builds the states
and then forbids using them. Curated memories resolve to
settled, check-before-acting or
suspect from a human verdict log, with wrong
forcing a tombstoned basis and a substantive edit voiding a
verification through a content digest. Its decision D1 — "Nothing
here may filter a memory out of a result on the basis of age" — is
applied to every recall surface, so a memory a person judged wrong is
returned with the label suspect · judged WRONG. The rule is
right about age and over-applied to an explicit rejection, which is the
distinction the mark turns on.
Graft writes a doubt state
and spends it nowhere. STALE is set by
maintain resolve --action stale in an audited transaction,
and the skill offers it for a note the agent could not confirm. Every
search arm selects state IN (0,1), so it filters nothing;
the query and hook outputs carry no state, so it labels nothing; and
every maintenance scan selects state = 0, so it drops the
note out of the one process that would revisit it. A test pins
"stale nodes stay searchable". SUPERSEDED and
RETIRED do filter, and both mean replaced or removed rather
than disbelieved, so the report withholds trust_state.
MuscleMemory keeps
the states honest and lets the client supply the evidence that moves
them. A revision is provisional,
validated or flagged and a procedure
provisional, active, flagged or
retired; search, plan composition and related-skill links
all require active, validated and a person's
approval (packages/musclememory/retrieval.py:228-230,
:409-410, planning.py:40-41).
flagged is terminal for a revision: no later success clears
it (service.py:895-905). The transitions are driven by
outcomes the caller reports, and the compiler stamps every revision
"Completion evidence is caller-attested"
(compiler.py:212). retired is terminal too: a
revise of a retired procedure is refused
(service.py:510-513), and retiring stores a fingerprint
that refuses the same skill on any later learn. The hooks produce
flagged in one case only, when a session was handed a skill
and that skill's own test ran last and failed
(service.py:1232-1289).
Tests to require
- Prove candidates cannot enter verified-only context.
- Exercise every allowed and forbidden state transition.
- Verify rejected records do not return through normal recall.
- Test that retrieval feedback changes usefulness signals, not truth automatically.
- Verify stale records can be revalidated without losing history.
- Test actor permissions for promotion, rejection, and override.