Document: loomworks-remember-about-me-confirmation-loop-scoping-note-v0_1
Version: v0.1
Status: scoping note — precedes a fix CR. Three findings; the fix shape and sequencing are for the Operator to settle before a CR is drafted.
Date: 2026-06-27
Origin: Operator testing on E0007 ("Goosey the Moose"), 2026-06-27 — "park the car on level 8" entered a permanent confirmation loop. Diagnosed read-only by CC (DB trace on conversation_turns + current_memory_objects, engine line citations).
Not caused by: CR-2 list-loading. CR-2 is frontend/read-path; it touched zero engine files; classification and commit live in loomworks-engine, untouched. CR-2 is cleared to land independently.
The Operator said "park the car on level 8", then answered "yes" repeatedly. Each "yes" produced another confirmation prompt instead of committing the note. The note never appeared in the engagement's Held or Settled. The loop is permanent — every subsequent "yes" repeats it.
CC traced it to the engine remember_about_me flow. Two stages caused the loop; a third observation is worth a decision.
"park the car on level 8" classified as general_conversation, not remember_about_me / add_knowledge. It is an imperative ("park the car…"); the classifier's write-intent patterns expect declaratives (add_knowledge → domain facts, remember_about_me → personal facts stated as facts). Contrast the immediately prior "Uncle John's birthday is April 25th" — a clean declarative — which classified correctly and committed in two turns. The imperative matched neither write-intent and fell to general_conversation. The responder LLM then free-formed "Noted — parked on Level 8. Want me to save that?" — an offer with no deterministic held-write behind it — which offset the confirmation rhythm by one turn.
Once a held draft finally existed (the first "yes" took the new-fact path and created it), the confirmation branch became reachable. But the held content and the continuation fact no longer match:
_normalize_personal_fact (router.py:196) collapses punctuation/whitespace and expands month abbreviations — it does not reorder words or strip possessives. So is_confirmation (router.py:2388) is False. "yes" carries no refinement signal, so the handler falls to the pending_held_blocks_new_fact refuse-overwrite guard (router.py:2442) — which writes nothing and re-surfaces the pending. Permanent stall.
This is the Phase-60-recovery Bug A class (held-creation form vs continuation form mismatch) that _normalize_personal_fact was meant to close — but its normalization is too shallow for word-order/possessive drift, and that drift here was caused by Stage 1 (the held was minted from a general_conversation-worded turn while the continuation came from a differently-worded remember_about_me turn).
The held draft ("Car parked on Level 8.", display 18) was written to the Operator's personal engagement (95774ecd), not the active E0007. This is why it never appeared in E0007's Held — it was in a different engagement entirely. remember_about_me writes to the person's personal store, not the active engagement. This may be correct by design (a car-parked note is a personal fact, personal facts belong in the personal store) or it may be a surprise worth examining. It is a separate question from the loop.
Correction recorded (drift-surface): the earlier inference from the screenshot was "the note was never written." It was written — as a held draft — just to the personal engagement and never committed. The absence in E0007 was real; the cause was "written elsewhere + never committed," not "no write."
| # | Finding | Layer | Shape of fix |
|---|---|---|---|
| F1 | Imperatives that imply a fact ("park the car on level 8" = "the car is parked") don't match any write-intent → fall to general_conversation | intent classifier | Broaden write-intent coverage to catch fact-bearing imperatives, or add a normalization/recognition step that maps imperative→declarative before classification. |
| F2 | _normalize_personal_fact can't see through word-order/possessive drift between held-form and continuation-form → genuine "yes" fails is_confirmation → refuse-overwrite guard stalls | confirmation state machine | Deepen the match: word-order-insensitive and possessive-stripping comparison, or a different held↔continuation matching strategy (e.g. match on the held's id/pending reference rather than re-deriving and re-comparing the fact text). |
| F3 | remember_about_me writes to the personal store while the Operator is in an engagement | routing / store selection | A decision, not obviously a fix: is personal-store routing correct here, or should a fact stated inside an engagement land in that engagement? |
F2 is the deeper fragility. Even if F1 were fixed — even if "park the car" classified correctly — F2 could still strand a confirmation any time the Companion rewords the fact in its confirm-back, which is a normal thing it does. F1 triggered F2 here, but F2 is the load-bearing weakness: any held whose confirm-back gets reworded with a possessive or reordered words is a latent stuck-loop. Fixing only F1 makes this case work and leaves F2 waiting for the next reword.
So the recommended ordering of concern:
The pending_held_blocks_new_fact refuse-overwrite guard is working as designed — it prevents a new fact from silently overwriting a pending held (data-loss protection, the "shape β" guard). It is firing spuriously because the upstream confirmation check (F2) wrongly reads a genuine "yes" as a pivot to a new fact. The fix is upstream of the guard, not in it. Do not weaken the guard.
Held assertion display 18, "Car parked on Level 8.", in personal engagement 95774ecd, state held — recoverable once the fix lands (commit it, or retract it). Note it so it is not left as orphaned test residue.
None of these block CR-2 landing; CR-2 is independent and cleared.
DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — remember-about-me confirmation-loop — scoping note — v0.1 — 2026-06-27 Diagnosed by CC (read-only, DB trace + engine citations). Not a CR-2 regression. Three findings; F2 is the deep fragility. Precedes a fix CR pending Operator scope decisions.