Recall-Failure Pattern — Root-Cause Investigation
Recall-Failure Pattern — Root-Cause Investigation
Section titled “Recall-Failure Pattern — Root-Cause Investigation”Date: 2026-07-14 · read-only investigation · sources: retros S461–S470, mempalace
store, recall infra (mempal-recall.sh, mempalace-recall skill, start-session
step 2a, workflow-orchestration + dispatch-primitives).
The pattern under investigation: an agent presents a conclusion/plan → Liam suggests searching mempalace → the results corroborate what Liam already thought. Liam is acting as the memory system. Related symptom: sessions referencing CLOSED tasks as if open.
Headline findings (two are decisive)
Section titled “Headline findings (two are decisive)”-
ID-131 is
done(closed). Confirmed viaget task 131 status→"done". Yet the S469 retro (2026-07-12) records the MEMO-DELTA Planner proposing “entity_mentions/ entity_relationships get a real updated_at+trigger via an id-131 migration (bl-458)” and making {132.38} depend on that migration. This is exactly the brief’s “id-131-lineage schema change on a closed task” example — a live instance of stale-status reference. -
The mempalace MCP recall path is CURRENTLY CORRUPT and fails closed.
PRAGMA quick_checkon~/.mempalace/palace/chroma.sqlite3returnsmalformed inverted index for FTS5 table main.embedding_fulltext_search(the #1665/#1843 HNSW↔sqlite drift).mempalace_searchandmempalace_diary_readboth returnMCP error -32002: Palace SQLite integrity check failed; refusing tool call until the palace is repaired. So the two tools thatstart-sessionstep 2a AND themempalace-recallskill depend on are down right now — any skill-driven recall silently errors. Themempal-recall.shhook’s rawmode=ro&immutable=1FTS read still returns rows despite the corruption (verified: 6 rows for seedcanonical). So the SessionStart digest survives the exact failure that kills every mid-session recall — but nothing mid-session uses that resilient path.
Incident catalogue
Section titled “Incident catalogue”| Session (date) | What was presented / assumed | What recall/owner showed | Cost of the miss |
|---|---|---|---|
| S469 (07-12, main) | Planner: deps= auto-invalidation is the “correct, DR-047-mandated” config policy. Also proposed an id-131 migration (id-131 is closed). | Owner’s instinct: “re-drafting the whole corpus on a prompt tweak can’t be what the framework intends” — right. Resolved only by reading the OKF framework spec (~4 WebFetches → DR-060). | A load-bearing PRODUCT decision nearly ratified wrong; owner was the correctness system. |
| S470 (07-12, proc) | {145.14} “cross-tenant Authorization IDOR” — framing carried from S467 security review + continuation prompt. Also “Extend UI already in-tree” premise. | Procurement DBs are per-client single-tenant → no cross-tenant scope (DR-061). “The wrong framing had already wasted analysis.” Extend is ABSENT. | Wasted analysis on a non-bug; a whole security framing dissolved on re-grounding. |
| S466 (07-12, proc) | S462 dossier’s two error-boundary root causes, carried as truth. ID-145 OQ-8 hard-rename of umbrella tools. | Live repro: boundaries do NOT reproduce (zombie-dev-server 500s). Owner’s OWN “BI-42 check id-71” annotation had anticipated the OQ-8 vs id-71 OQ-4 conflict; a peer session surfaced it before the orchestrator’s review reached it. | Near-miss re-encoding an anti-pattern id-71 already ratified-rejected. Owner annotation + peer session WERE the memory. |
| S464 (07-11, main) | Fresh Planner carried LI-8’s stale “no shared nav registry, deliberate three-file edit” framing. | components/shell/nav-config.ts (id-118) shipped a single-source NAV_ZONES registry one day earlier, already declaring /okf reserved. Only the Checker’s round-1 FAIL caught it. | A full Checker round burned on day-old staleness. |
| S463 (07-11, main) | (Omission) An owner-ratified consequence — “OKF-LANDING.md needs an amendment pass BEFORE dispatch” — sat only in a done subtask journal. | ”Fell through four sessions untracked… caught only when the owner asked who owns UI surfacing.” | 4 sessions of silent drift; owner had to re-surface it. |
| S462 (07-11, main) | Two ledger status notes asserted pending prod work; belief “id-136 implemented retirement + manual upload”. | Live SQL proved the work already done; id-136 was pipeline-only. NOTE: the 8-reader workflow’s “stale-journal warning embedded in every reader prompt is what kept stale id-130/S242 framings from leaking into findings as truth.” | Confirms stale done-state imports as truth UNLESS a grounding block is explicitly embedded. |
| S468 (07-12, proc) | (Durability) A real IDOR security finding lived only in orchestrator context. | ”Survived to next session only because the S468 salvage re-read the full transcript… security findings need a durable queue, not orchestrator-context memory that dies with the session.” | One session’s death nearly lost a security fix. |
| S461 (07-11, main) | e2e failures journaled as “seed-data gaps”; FC-95 eval asserting 5 layers. | Owner corrected to “EXPECTED UI drift behind completed id-130 backend”; where_are_we_exposed was trimmed to 4 layers at ID-131.19. “Orchestrator lacked product-domain context to self-adjudicate.” | Misdiagnosis routed as bugs; a 6-day-latent stale eval. |
Cross-session constant: the stale source is almost always the continuation prompt or a prior-session dossier/journal, imported as current truth. In every case a recall/grounding pass seeded on the task topic or the symbols the claim cites would have surfaced the correction the owner supplied.
Root-cause hypotheses — confirmed / refuted
Section titled “Root-cause hypotheses — confirmed / refuted”(a) Recall happens only at session start, never at decision points — CONFIRMED (primary).
mempalace-recall is explicitly question-driven: “Search the palace before answering
whenever the user asks …” and “Recall is question-driven, not reflexive.” It triggers on
USER questions, never on the AGENT being about to present a conclusion/plan/ratification/spec.
start-session 2a is the only proactive recall and it fires once, at start. There is no
lifecycle hook at the exact moment the failure occurs (agent forming a conclusion). This is
the direct mechanism of the “agent presents → Liam points at memory” loop.
(b) Seeding (branch + cwd) misses task-topic — CONFIRMED, and worse than stated.
mempal-recall.sh seeds only on branch slug + cwd basename (lines 26-41), and drops main,
dev, wip, ca as generic (line 35). On the main branch the only surviving token is
canonical (the cwd basename = the repo name) — a seed that matches everything and nothing.
Worktree branches (agent-a61ddb0a…) seed on hex garbage. Only a descriptively-named branch
yields a useful seed. There is no active-task-id / task-title seeding anywhere in the hook.
Step 2a’s prose says “branch + active-task-seeded recall” but delegates that to the model via
the (currently-broken) MCP tools — it is not mechanized.
(c) CHECKPOINT auto-noise buries signal — REFUTED as a primary cause.
The hook already filters CHECKPOINT three ways (NOT LIKE 'CHECKPOINT:%', topic!=‘checkpoint’,
plus the “Base directory for this skill” skill-boilerplate filter) and ranks diary rows
first (lines 62-66). Noise handling is actually sound. Not the driver.
(d) Done-task journals/details imported as current truth — CONFIRMED (primary).
S462 (stale status notes), S466 (stale dossier), S470 (stale IDOR framing), S469 (id-131-closed
reference), S461 (stale eval) all instances. DR-002 (“done = don’t-re-flag ONLY, never import
as current truth”) exists precisely for this but is a manual discipline surfaced only in
start-session 2f — not enforced, and not carried into sub-agent briefs where the fresh
Planners actually consumed the stale framing.
(e) Skills never instruct sub-agents to recall — CONFIRMED (primary for the fresh-agent cases).
workflow-orchestration references mempalace only as a diary write at close (line 433);
dispatch-primitives mentions mempalace_search once, only as a “narrow the query” note
(line 112). task-planner/task-executor/task-checker briefs contain no recall step.
Per .ast-dataflow/CLAUDE.md propagation discipline, sub-agents inherit only what the
Orchestrator explicitly embeds — so fresh Planners (S464, S469, S466) started with zero recall
and carried forward whatever the brief/continuation prompt handed them. S462 is the control
case: when a grounding block WAS embedded in every reader prompt, stale framings did not leak.
(f) NEW — the MCP recall path fails closed with no mid-session fallback — CONFIRMED (enabling).
See headline #2. When the palace is corrupt (it is, now), every mempalace-recall /step-2a
call errors, and there is no automatic fall-through to the raw mode=ro FTS read that the
hook proves still works. So proactive recall silently degrades to “SessionStart hook only,”
which (per (b)) is topic-blind on main. The skill’s unhappy-path guidance says “surface the
error, never fall back to guessing” — but in practice the agent proceeds and Liam becomes the
fallback.
Fix candidates (mapped to root causes)
Section titled “Fix candidates (mapped to root causes)”FIX 1 — Decision-point recall trigger [addresses (a); highest leverage].
Amend mempalace-recall (and add a workflow-orchestration rule) so recall fires not only on
USER questions but before the agent presents a conclusion/plan/ratification/spec that
references a task id, a DR, prior-session framing, or settled state. This directly closes the
“agent presents → Liam points at memory” loop — it inserts the search at the exact lifecycle
point the pattern occurs. Pair with a cheap guard: any brief/answer that cites id-N, DR-NNN,
or {N.M} must first confirm that record’s live status (catches the id-131-closed class).
FIX 2 — Task-topic seeding + main-branch fallback [addresses (b), partially (d)].
(i) In mempal-recall.sh, when the surviving seed set is empty or degenerates to the repo
basename, add seed tokens from the active in-progress task id(s)/title. (ii) In start-session
2a, mechanize “active-task-seeded” by resolving seed terms from the continuation-prompt-named
ids + list task --status in_progress, and pass them to recall explicitly rather than leaving
it to the model. This makes the one reliably-firing recall (SessionStart) topically relevant on
main and on worktree branches.
FIX 3 — Recall + grounding block in every dispatch brief [addresses (d) + (e)].
Make workflow-orchestration/dispatch-primitives require, in every Planner/Executor/Checker
brief, a grounding block = (active-task recall seeds) + (DR-002 “done = don’t-re-flag, verify
status_note against live state before citing”) + (“orient on the symbols your own invariants
cite, not just the feature vocabulary” — the S464 lesson). This is the S462-proven pattern
(embedded grounding block kept stale framings out) generalized from ad-hoc reader prompts to the
standing dispatch primitive.
FIX 4 — Repair palace + mid-session raw-sqlite fallback [addresses (f); enabling].
Run mempalace repair --mode from-sqlite --archive-existing --yes now (the store is corrupt).
Then give the recall skill a fallback: on MCP -32002/integrity error, fall through to the
hook’s mode=ro&immutable=1 FTS read (proven to survive the corruption) instead of proceeding
recall-blind. Without this, Fixes 1-3 still fail-closed whenever the index drifts.
Ordering: FIX 1 and FIX 3 remove the behavioral gap (recall at the right moment, in sub-agents); FIX 2 makes the recall that does fire useful; FIX 4 keeps all of them working when the index is corrupt. FIX 1 + FIX 3 are the two that most directly stop Liam being the memory.