Skip to content

docs(adr): ADR-018 D6.1/D6.2 — a broadcast NO_REPLY must release the claim - #952

Open
lilyshen0722 wants to merge 1 commit into
mainfrom
docs/adr-018-d6-amendment
Open

docs(adr): ADR-018 D6.1/D6.2 — a broadcast NO_REPLY must release the claim#952
lilyshen0722 wants to merge 1 commit into
mainfrom
docs/adr-018-d6-amendment

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

Answers Sam's question about claim/queue behaviour, and closes a hole the fleet demonstrated on itself today.

D6.1 — a broadcast NO_REPLY must release the claim

D6 says "a claim is the right to decide, not a duty to reply." That's right for a targeted wake — the claimer is the addressee, and its silence is the answer. It's wrong for a broadcast, because there the claim was won by a race, not by relevance.

Observed today, not hypothetical. A review request posted to four seats in Sharpen:

sprint-impl     won the claim on message 53136
pod-architect   "already claimed by sprint-impl — standing down"
sprint-review   "already claimed by sprint-impl — standing down"
ux-lead         "already claimed by sprint-impl — standing down"
sprint-impl     spawning codex → "no wrapper-post (NO_REPLY)"

The implementer won a race for a review request, declined, and the three seats that could have answered had already excluded themselves. The request vanished with no signal to anyone — from outside, indistinguishable from "still thinking."

Nobody evaluates whether a message is for them until after the exclusive claim is taken. So D6's "right to decide" quietly becomes "the first mover decides on everyone's behalf."

The fix: on message.posted, a NO_REPLY verdict releases the claim for a bounded second pass instead of consuming the message. chat.mention keeps D6 unchanged.

The asymmetry that already exists is correct and worth recording — a direct mention overrides a peer's claim ("proceeding peer-aware — this seat was directly addressed"). Targeting is respected; only broadcast gambles. This makes the gamble recoverable.

D6.2 — serial event processing is an invariant

The wrapper polls up to 10 events and runs them strictly serially (cli/src/lib/poller.js, for … await). Each message with a messageId is its own claimable event, so a burst of three messages is three claims and three sequential turns.

That ordering is what makes bursts survivable. A turn builds context at spawn and cannot see mid-turn arrivals — but the next turn rebuilds context and sees the newer messages plus its own previous reply, which is exactly what lets it recognise the ground is covered and decline.

Processing events concurrently would break this silently: parallel turns would each answer an overlapping question unaware of the others, and no prompting fixes it. It looks like an obvious throughput win, which is precisely why it's now a decision rather than a property of the current code.

Review note

The fleet reviewed D6 by hitting it. The claim mechanics worked exactly as specified — the gap was stating D6 without distinguishing broadcast from targeted. Correct and incomplete, and only live traffic showed which half was missing.

Same failure family as the at-cap decline returning status: 'succeeded' (found in the ADR-022 review): a message disappears while every component reports success.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8

…claim

D6 ("a claim is the right to decide, not a duty to reply") is right for a
TARGETED wake and wrong for a broadcast, because on a broadcast the claim was
won by a race rather than by relevance.

Observed 2026-08-14 in Sharpen, not hypothetical: a review request went to
four seats, sprint-impl won the claim, pod-architect / sprint-review / ux-lead
all logged "already claimed — standing down", and sprint-impl returned
NO_REPLY. The implementer won a race for a REVIEW request, declined, and the
three seats that could have answered had already excluded themselves. The
request vanished with no signal to anyone — from outside, indistinguishable
from "still thinking."

D6.1: on `message.posted`, a NO_REPLY verdict RELEASES the claim for a bounded
second pass instead of consuming the message. `chat.mention` keeps D6
unchanged — there the claimer IS the addressee and its silence is the answer.
Records the asymmetry that already exists and is correct: a direct mention
overrides a peer's claim ("proceeding peer-aware — this seat was directly
addressed"), so targeting is respected and only broadcast gambles.

D6.2: serial event processing is an INVARIANT, not an implementation detail.
The wrapper polls up to 10 events and runs them strictly serially. A turn
builds context at spawn so it cannot see mid-turn arrivals — but the NEXT turn
sees the newer messages plus its own previous reply, which is what lets it
recognise the ground is covered and decline. Processing events concurrently
would break that silently: parallel turns would each answer an overlapping
question unaware of the others, and no prompting fixes it. It looks like an
obvious throughput win, which is why it is now a decision rather than a
property of the current code.

Review note added: the fleet reviewed D6 by hitting it. The claim mechanics
worked exactly as specified; the gap was stating D6 without distinguishing
broadcast from targeted. Correct and incomplete — only live traffic showed
which half was missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeUH4HVDsDHYPsHJthXjB8
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