Skip to content

step-backs survive the wake boundary — reply gate + digest line - #14

Open
Octember wants to merge 5 commits into
mainfrom
claude/bevelina-issues-10c086
Open

step-backs survive the wake boundary — reply gate + digest line#14
Octember wants to merge 5 commits into
mainfrom
claude/bevelina-issues-10c086

Conversation

@Octember

Copy link
Copy Markdown
Owner

What happened (live, 2026-08-10)

A resident wake posted "these aren't duplicates… 4782 should be reopened" into a thread where two humans had just settled the opposite — 33 minutes after she herself had stepped out of that thread with the reason "a stale reply would reopen settled triage." The wake was a fresh session: the step-back wasn't in its digest, nothing gated the post, and her own post cleared the step-back bit (threads.ts re-engage-on-post).

Full trace in the session ledger/rollouts: wake triggered by an unrelated task update; batch carried two raw lines with no thread context; one linear_read; reply queued without ever reading the thread.

Fix

Two of the four gaps from the incident (the ear-hold delivery annotation and task hygiene are follow-ups):

  1. Reply gate on stepped-back threads (toolset.ts): the first reply into a currently stepped-back thread doesn't send — it returns her recorded step-back reason plus the thread's current tail (threadTailBefore, reused from the ear). Sending again is her informed call and goes through, re-engaging the thread as any post does. Same shape as the §5.5 withhold, keyed on durable thread state instead of batch timing. One bounce per thread per turn.

  2. Step-backs ride the digest (turns.ts, service.ts): stepped_back effects now come back from outboundEffectsSince, so the next wake's [what you did recently] and the ear's own-voice section both show she just left a conversation and why.

Prompt/tool-side only — the harness still never posts.

Tests

  • resident.test.ts: bounce carries why + tail, bounced text never lands, informed re-send posts; mention re-engagement skips the gate; step-back appears in the next wake's digest.
  • threads.test.ts: steppedBackState lifecycle (engaged → stepped back → cleared by re-participation).
  • turns.test.ts: outboundEffectsSince recovers posted/reacted/stepped_back.

bun run check green (391 tests). Note: needed bun install locally — node_modules had agent-tools 0.4.0 against the 0.5.0 lockfile.

🤖 Generated with Claude Code

Octember and others added 5 commits August 10, 2026 14:52
Live 2026-08-10: a fresh wake posted into a thread she'd stepped out of 33
minutes earlier, contradicting a decision two humans had just settled,
without reading it. Every wake is a fresh session; nothing carried her own
choice to leave, and nothing made her look before speaking.

- reply now bounces the first send into a stepped-back thread with her
  recorded why and the thread's current tail (threadTailBefore, reused); a
  repeat send is her informed call and goes through, re-engaging as any
  post does. Same shape as the §5.5 withhold, keyed on thread state
  instead of batch timing.
- stepped_back effects ride outboundEffectsSince, so the next wake's
  [what you did recently] and the ear's own-voice digest both show she
  just left a conversation, and why.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First-principles pass over the 2026-08-10 incident surfaced two more holes,
one of them in the fix I shipped an hour ago:

- The retry loop reused one toolset across attempts, so a step-back bounce
  consumed by a dead attempt disarmed the gate for the retry — a fresh
  session that never saw the bounce would post straight through. Toolsets
  now build per attempt (each retry re-decides against re-armed tools);
  shared wake state (effects, stream, answered, checklist holder) is
  hoisted and survives rebuilds. Test proven to fail against the old code.

- The deeper asymmetry: since 2026-07-30 the ear reads every batch with
  the already-heard tail of the threads it touches, but the wake that
  SPEAKS still judged bare lines — at 18:10 that was two lines of new
  chatter and no surrounding conversation. The tail builder is extracted
  (threadTailContext) and now rides both prompts; the correction she
  contradicted would have been in her context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nthesis)

Product of a 21-agent workflow: 5 ground-truth readers (SPEC/design-doc audit,
wake dataflow inventory, ledger state-smear map, live VM traffic numbers, patch
archaeology), 4 independent architects on divergent theses, 12 adversarial
judge passes replaying the full 12-incident catalog against each design.

All four architects converged on the same spine: the conversation as the ledger
unit (per-conversation watermarks replacing both global cursors), judgment
co-located with delivery (holds as rows, not discarded verdicts), one renderer
for both readers with her own posts in the event stream. The doc records what
survived, what the judges killed and why (so it doesn't get relitigated), the
bankruptcy list of ~15 mechanisms retired, and a 4-phase migration where P0 is
an afternoon and needs no schema.

PROPOSAL status — SPEC.md unamended until the operator blesses it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e envelope

Two afternoon-sized changes the redesign judges found carry most of the
measurable wake/failure cost, independent of everything else in the proposal:

- An identical repeat of a task's previous worker report still lands durably
  in events (rides the next wake — nothing dangles) but no longer forces a
  wake. A stuck task re-reporting "waiting on a human" cannot drag the mind
  out of bed for it (2026-08-10: that exact wake posted stale into a settled
  thread).

- Envelope turns now carry the existing stall watchdog (turns.stall_timeout_ms,
  default 45s): a runtime with no activity dies early as a FAILED attempt the
  retry loop covers, while honest streaming work keeps the full envelope. One
  number was measuring "gateway blackholed" and "long job" — opposite
  conditions (2026-07-27 starvation, 2026-08-10 full-envelope burns).
  Fail-safe: a session without msSinceLastActivity never stalls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…delivery consumes

First structural cut of the one-room redesign (specs/2026-08-10-one-room-redesign.md).
New ledger unit: one row per (identity, venue, thread root) with per-conversation
delivered/judged watermarks (CHECK judged >= delivered — cursor skew unrepresentable),
a holds count, a BOUNDED hold-why history (never a single latest-wins why), and the
ear's wake-why.

- An ear hold writes the row instead of dying with the verdict. A wake consumes each
  batch conversation's accumulated judgment in the same transaction that advances its
  watermark: a wake structurally cannot take a conversation's messages and leave the
  reads that held them behind. The 18:10 shape — four discarded "this is settled"
  holds, then bare lines delivered to a fresh session — is now a regression test.
- Wake-whys with a venue land on the row (durable) instead of the consumed-once RAM
  earNotes map; the RAM path survives only for venue-less notes.
- Migration v12 seeds rows from existing events at the global cursors: nothing
  re-delivers on upgrade.

P1b flips delivery itself onto the per-conversation watermarks and moves her own
posts into the event stream; P2 retires the PR #14 gate for the rendered-set bounce.
Net-code trend: this table is what makes resident_cursor, ear_cursor,
thread_participation, earNotes, unsentDrafts, and both digests deletable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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