Skip to content

§5.5 stale-reply withholding: thread-follow replies yield to a room that moved mid-turn - #8

Merged
Octember merged 5 commits into
mainfrom
claude/bevelina-regression-8de2db
Jul 23, 2026
Merged

§5.5 stale-reply withholding: thread-follow replies yield to a room that moved mid-turn#8
Octember merged 5 commits into
mainfrom
claude/bevelina-regression-8de2db

Conversation

@Octember

Copy link
Copy Markdown
Owner

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:

  • In a wake whose batch contains no direct address, replies buffer until turn end. If newer addressed messages arrived on the same conversation mid-turn, the reply is withheld (ledger effect kind: withheld, never a false posted) 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).
  • A directly-addressed wake never buffers: the asker is owed the answer even if the thread moved (SPEC MUST NOT).
  • toolset: optional bufferReply seam on ToolsetContext; a buffered reply records no effect at call time.
  • turn: beforeRecord hook 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 check green: 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

Octember and others added 2 commits July 23, 2026 08:50
…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>
@Octember

Copy link
Copy Markdown
Owner Author

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>
@Octember

Copy link
Copy Markdown
Owner Author

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.

Octember and others added 2 commits July 23, 2026 10:43
…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>
@Octember
Octember merged commit c6b7135 into main Jul 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant