Skip to content

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.


  1. ID-131 is done (closed). Confirmed via get 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.

  2. The mempalace MCP recall path is CURRENTLY CORRUPT and fails closed. PRAGMA quick_check on ~/.mempalace/palace/chroma.sqlite3 returns malformed inverted index for FTS5 table main.embedding_fulltext_search (the #1665/#1843 HNSW↔sqlite drift). mempalace_search and mempalace_diary_read both return MCP error -32002: Palace SQLite integrity check failed; refusing tool call until the palace is repaired. So the two tools that start-session step 2a AND the mempalace-recall skill depend on are down right now — any skill-driven recall silently errors. The mempal-recall.sh hook’s raw mode=ro&immutable=1 FTS read still returns rows despite the corruption (verified: 6 rows for seed canonical). So the SessionStart digest survives the exact failure that kills every mid-session recall — but nothing mid-session uses that resilient path.


Session (date)What was presented / assumedWhat recall/owner showedCost 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 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.