§5.5 stale-reply withholding: thread-follow replies yield to a room that moved mid-turn - #8
Conversation
…that moved mid-turn Live 2026-07-23: in the font/image incident thread, a thread-follow wake answered a question a human had already answered — her reply composed against a wake-start snapshot and landed a minute stale, then the next wake misread the human's answer as a correction and flip-flopped. SPEC §5.5 (and the §18.2 'Stale-reply withholding' row) prescribe the guard: in a wake with no direct address, replies buffer until turn end; if newer addressed messages arrived on the same conversation mid-turn, the reply is withheld and the immediately following wake carries it as an unsent draft to reconsider. A directly-addressed wake never buffers — the asker is owed the answer. - toolset: optional bufferReply seam; a buffered reply records no effect (the flush owns posted/withheld truth in the turn row) - turn: beforeRecord hook so the flush lands inside the turn's ledger record - service: buffer + flush (post / withhold + unsent-draft slot, in-memory like ear notes); failed attempts drop their buffer (same rule as clearCards) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…is ownership
Live 2026-07-21..23: 26-44 wake verdicts/day, many for conversations the ear
itself judged to belong to someone else ('noah owns the incident response and
should investigate', 'kate still owes that check', 'someone should investigate',
'no owner is named yet' — all woke the mind). Third-party debts also landed in
attention items and rode the wake prompt as hers, priming her to step into other
people's threads. Result: replies into ~40 distinct threads/day.
One boundary, stated once in the ear soul: when people are talking to each
other, the conversation is theirs; a question aimed at another teammate is that
person's to answer even when she knows it; record only debts aimed at her.
Verdict tool description tightened to match (wake = hers AND now; open_ask =
never what one teammate owes another). Unowned machine signals are untouched:
alert front-running stays licensed by her memory facts, which the ear reads.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Second commit on this branch (e0d39b6): the over-helpfulness half of the regression. The ear was waking her for conversations it itself judged to belong to someone else ("noah owns the incident response", "kate still owes that check", "someone should investigate") and recording third-party debts that rode her wake prompt as hers — ~40 threads/day posted into on Jul 21–22. Fix: one ownership boundary in the ear soul (a question aimed at another teammate is theirs even when she knows the answer; record only debts aimed at her) + matching verdict-tool description. Alert front-running stays licensed via her memory facts. Deployed to the VM 2026-07-23 ~14:05 UTC; the one open third-party attention item was operator-closed. |
…r closes are final
Live 2026-07-22/23, the 'stfu youre not helping' incident: Noah posted open team
asks ('Needs QA:', a GTM voice-memo request) with no addressee. The ear recorded
them as HER debts ('noah is asking you to QA BEV-4611', 'the gtm team, including
you') — one opened seconds after Noah told her 'not for u' in the same channel.
Both were recorded with thread_root_id NULL, so her step_back (which settles by
thread root) could never touch them; the ear then reopened the 7951 debt three
times on 'the work is still outstanding' logic. Result: repeated blocked-QA
announcements and an 'i've got capacity' interjection into a team she is not on.
Three fixes:
- open_ask requires an anchor; a top-level ask roots on its own ts (the router's
convention), so in-thread answers and step_back can actually settle it
- reopenAttentionItem refuses operator closes; step-back closes stay reopenable
per SPEC ('the ear MAY reopen one that truly was hers')
- ear soul: an ask to the room or a team is not hers to claim unless a name or a
standing rule makes it hers; unfinished work is not an unanswered ask
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Third commit (2481ae3), from the "stfu youre not helping" evidence: the ear was converting open team asks into her personal debts — "noah is asking you to QA BEV-4611" was recorded seconds after Noah said "not for u" in the same channel, and "the gtm team, including you" invented her team membership, producing the "i've got capacity" interjection. Both QA debts were recorded anchor-less (thread_root_id NULL) so her step_backs could never settle them, and the ear reopened one three times on work-still-outstanding logic. Fixes: open_ask now requires an anchor (top-level asks root on their own ts), operator closes are ear-irreversible, and the ear soul gets the open-ask boundary (open work is not hers to claim unless a name or standing rule makes it hers; unfinished work is not an unanswered ask). Deployed ~15:20 UTC; the two orphaned QA debts operator-closed; her open-debts list is now empty. |
…tured room earshot replay --db <snapshot> --from <iso> --to <iso> [--venue C…] [--speed N] Carves the window's surface messages out of a ledger snapshot (RawMessage round-trip from the router's stored payloads), rewinds the snapshot COPY to the moment before the window (events + contentless-FTS docs, turns, attention items incl. re-opening ones closed in-window, participation step-backs, cursors; tasks/executions/steering/timers cleared — scheduler state firing mid-replay is noise), then relives the traffic through the real Service at recorded pacing: - real codex sessions via the same factory as start (extracted, shared) — a replay driving different wiring would test the wrong bot - CaptureAdapter: replies/reactions recorded, never delivered; no streaming methods so every reply funnels through the plain-post capture point; read_thread serves the room as recorded - integration registries keep their real specs but record instead of executing (writes report done; reads report unavailable) — the report shows what she reached for without touching Linear/GitHub/Notion - --speed compresses gaps; speed 1 preserves mid-turn race timing Report prints her original window actions next to the replay's. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-1 replay findings, fixed mechanically (no prompt additions — reverted the
soul lines from the first attempt):
- toolset: an identical repeated outward call within one wake is refused after a
success ('already done'). Shared across §14.2 retry attempts on purpose:
external calls record no ledger effects, so a wake that wrote then died would
otherwise re-run the write on retry. Failures never arm the guard.
- replay: read-grain tools run their REAL implementations (side-effect-free by
the grain contract); only writes are stubbed+captured. A sandbox where she
cannot look anything up distorts her more than reads answering with today's
world — round 1's failed lookups produced the duplicate ticket and the
fabricated 'I checked'.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Why
Live 2026-07-23 ("bevelina is back to being an idiot"): in the font/image incident thread, Nina asked Noah a question; Noah answered it himself 14 seconds into Bevelina's thread-follow wake; her reply — composed against the wake-start snapshot — landed a minute later, stale and partly wrong, and the following wake misread Noah's answer as a correction of her and posted a flip-flop. Not rot (M20 healthy), not model routing — the SPEC §5.5 staleness guard was unimplemented.
What
Implements SPEC §5.5 / the §18.2 "Stale-reply withholding" row:
kind: withheld, never a falseposted) and the immediately following wake carries it as an unsent draft to reconsider — the model's words, the model's call. Otherwise it posts normally at turn end (home thread still via the reply stream).toolset: optionalbufferReplyseam on ToolsetContext; a buffered reply records no effect at call time.turn:beforeRecordhook so the flush lands before the turn row records — the ledger stays truthful.service: buffer + flush; failed/timed-out attempts drop their buffer (same rule as clearCards); unsent drafts held in-memory like ear notes (fail-open).Tests
Three new §18.2 conformance rows in
test/resident.test.ts(withheld + draft carried; no-arrivals posts at turn end; direct never withheld).bun run checkgreen: 361 pass.Deployed to tag-daemon.exe.xyz 2026-07-23 12:50 UTC (VM base verified against parent commit before scp).
🤖 Generated with Claude Code