The Companion is supposed to recall the Operator's personal memories — birthdays, preferences, who's who — from any engagement, because personal memory is committed as cross-engagement. It was failing: asked "birthdays" in one engagement it returned the list; asked the same word in another it returned a capability menu and no birthdays. Same word, same person, same store — different result.
The root cause: personal-memory storage was correct and cross-engagement, but personal recall had no first-class capability of its own. Recall was emergent — it worked only when a turn happened to reach the responder, which read a personal-memory block from its prompt. When the same query misrouted into the responder-bypassing orientation intent, recall silently failed. And even when it worked, the block loaded only for engagement-scoped turns and was budget-clipped, so old facts could drop silently.
The fix made personal recall first-class and reliable: a recall_personal intent that mirrors the existing engagement-recall intent (ask_about_past_input), pointed at the personal store. It reads the personal engagement directly (works from any engagement), retrieves the Operator's personal memories wholesale (the complete set — no silent clip), and the Companion composes the answer. Plus classifier guidance that makes the intent fire reliably, and a structural guard that makes the failure mode — a recall query landing on the orientation bypass — impossible rather than merely discouraged.
The capability was built with the same gate discipline as the orientation work: a live-classifier hard gate (run with a real key, not just prompt-content tests) and an Operator browser review before a separate push. The live gate caught a real miss — an ambiguous "what can you remember for me" still classified as orientation — and the fix was the load-bearing lesson: soft classifier guidance loses to a strong model prior; a guarantee is enforced in code, not asked for in a prompt.
Personal memory is committed as cross-engagement and person-scoped (functional spec §4.1; classifier remember_about_me — "person-scoped, travels across all the Operator's projects"). Storage honoured this — personal facts are committed assertions on the person's invisible personal engagement (Phase 41), loaded identically from every engagement. But recall diverged: "birthdays" surfaced personal facts in engagement E0005 and not in E0060.
The locus was classification/routing, not storage. There was no personal-recall intent; recall was emergent via the responder reading a personal-memory prompt block. Orientation (CR-2026-099's lever-3 server-compose) bypasses the responder. So "birthdays" classified into a responder-bound intent in E0005 (surfaced the facts) but into orient in E0060 (bypassed the responder, returned the menu). Two further fragilities made even the responder path unreliable: the block loaded only when project_id is not None, and it was budget-clipped newest-first (~500 tokens), dropping old facts silently.
ask_about_past_input at the personal storeask_about_past_input is an existing retrieval-backed, responder-composed recall intent for engagement memory. recall_personal is the same shape with three swaps: engagement_id → person.personal_engagement_id; the no-project guard → a no-personal-engagement guard; the engagement-recall template → a personal-recall template. Personal-recall and engagement-recall became siblings — architecturally consistent, not bespoke. The handler reads the personal engagement directly (removing the project_id gate) and puts recalled facts authoritatively into operation_data (removing the budget-clip fragility).
Wholesale. Recall retrieves the complete set of committed personal assertions; the responder composes from the complete set. Any topic-filtering is a subset of the complete set — a deferred optimization, never a capability the wholesale path lacks. The retrieval limit was set to 500 (not ask_about_past_input's 50) — deliberately, because reusing 50 would have reintroduced the silent clip this CR exists to remove. 500 is an effectively-wholesale backstop bounding only a pathological query; total_count is surfaced so any overflow is visible, not silent. This limit difference is load-bearing — it is the difference between "complete" and "clipped," which is the bug's whole nature.
Eight commits on branch personal-recall, atop the CR archive.
- Piece 1 — recall_personal in the IntentLabel Literal; count-lock tests bumped 21→22.
- Pieces 2+4 — _route_recall_personal handler (mirrors _route_ask_about_past_input, reads person.personal_engagement_id, wholesale, no-personal-engagement guard) + router dispatch.
- Piece 3 — the personal-recall responder template (plain-terms, lead-expert voice; honours the engagement/general invisibility commitment — no scope/engine narration; offers to remember on a miss).
- Piece 5 (load-bearing) — the classifier guidance: a recall_personal section and two disambiguations (vs ask_about_past_input: personal life/world vs this project's content; vs orient: recalling a memory is not a capability query).
- Piece 6 — orient-tightening: a bare topic noun is never orient; orient is for explicit capability queries only.
- Checkpoint A safe-default — by Operator ruling, ambiguous recall-vs-capability cases default away from orient (orient reserved for unambiguous capability queries).
- Checkpoint B structural guard — see §4.
The Checkpoint A guidance was correct and reviewed. The Checkpoint B live-classifier run (real API key, the silent-failure gate) confirmed 11 of 12 cases — including the bug case ("birthdays" → recall_personal, 0.85). One missed: "What can you remember for me?" classified as orient (0.72). The classifier reads "what can you ___" as a capability query, and the soft prompt guidance ("ambiguous → not orient") lost to that strong prior. The query landed on the bypass — the exact failure the safe-default rule forbids. The hard gate halted.
This is the orientation lesson, one level over: soft prompt guidance loses to a model prior under pressure; a guarantee is enforced in code, not asked for in a prompt. (The orient fabrication taught the same — a structural-contract reply is composed server-side, not generated; here, "recall queries never hit the bypass" is enforced by a deterministic guard, not by classifier persuasion.)
The fix was a structural guard, not an additional exemplar. An exemplar ("what can you remember for me" → recall_personal) would fix the tested phrase but not the class — the next untested recall-flavoured "what can you ___" phrasing would leak again, silently. The guard encodes the definition: orientation is a capability-query intent; a query whose content is a recall is definitionally not a capability query. So: if the classifier returns orient and the utterance contains a recall verb ("remember"/"recall") and no clear capability signal, redirect away from orient (to recall_personal if personal, else general_conversation — never the bypass). The "no capability signal" clause is load-bearing — it lets genuine capability-about-memory queries through ("how do I get you to remember things?" stays orient).
The re-run confirmed both sides clean: the ambiguous recall-flavoured query redirects off the bypass; the genuine capability query stays orient. The failure mode is now structurally impossible for the whole class, not enumerated phrase by phrase.
The immediate bug could have been patched by tightening orient's classification so bare nouns stop misrouting (Fix A). That would have left recall emergent — working when classification reaches the responder, failing silently when it doesn't, with old facts still dropping off the clipped block. For a memory product, "recall that usually works" is the worst failure mode — it reads as forgetting. Fix B made recall first-class (a real retrieval-backed intent), reliable (structurally guarded against the bypass), and complete (wholesale, no silent clip). The structural guard is the same standard applied to classification: not "usually routes right," but "cannot route a recall onto the bypass." Reliable by construction, not by luck.
Shipped and reliable (v1.0). Engine main ec0b383, tagged personal-recall-v0_1; OL unchanged. The Companion recalls personal memories from any engagement — first-class, wholesale, structurally guarded. Engine suite 2881 passed / 0 failed; gated live-classifier 13/13; Operator browser review passed in the previously-broken Fenwick engagement (E0060) — "birthdays" recalls, "what can you remember for me" redirects to recall not the menu, engagement recall stays separate.
Deferred (logged for queued-directions):
- "Ask-first on genuinely ambiguous recall" — the clarification affordance (the Companion asks "Personal, or this project?" and routes on the answer) — scoped as a fast-follow, not built here; the structural guard handles the safe-default now.
- Topic-filtered / relevance-ranked personal recall — the subset optimization where the memory=recall "find the relevant thing" vision lives.
- The *_about_me write-family name observation — the family is named "about me" but holds personal memories broadly (subject can be anyone); recall_personal diverges deliberately; a family rename is possible future work.
- The Companion-Markdown rendering gap (literal ** in Companion replies) — unrelated to this CR, observed during its browser review; the rich-Companion-response thread.
This work advances the memory=recall seed-level reframe (recall becomes first-class and reliable) without performing it (no seed change; the relevance vision is deferred). Decoupled by Operator direction.