fix(x-mode): commit the offer marker only after its wake is emitted, and answer each request once - #69
Merged
Conversation
added 7 commits
July 30, 2026 10:29
The poll claimed state/x-context/<request_id>.offered.json before printing the x-mention wake line, and the marker is what makes every later relay re-offer silent. A poll that stopped in that gap left a marker with no wake: the mention stayed pending in the inbox, every re-offer was discarded, and it was never surfaced again for the rest of the retention window. Claim, emit, then commit. The claim still atomically gates concurrent polls but now records wake_emitted:false, only an emitted marker suppresses a later offer, and reaching the claim with an existing marker means an interrupted offer to recover. An interruption now costs at most one repeated wake instead of a lost mention. A marker with no wake_emitted field counts as emitted, so an upgrade cannot replay an answered mention into a public reply.
The poll offers a mention at least once: an offer interrupted before its wake is re-offered rather than silenced, because a repeated wake is recoverable and a silenced mention is not. That makes a repeated wake reachable, and nothing local stopped it from becoming a second public reply - only the relay's own 409 stood between a duplicate wake and a duplicate post. Enforce exactly-once where the public action happens. The initial answer path atomically claims state/x-context/<request_id>.answered.json before posting and refuses with exit 10 when the request was already answered or an earlier attempt's outcome is unknown. The claim is released whenever the answer definitely did not land, so an ordinary failure stays retryable, and follow-ups keep their relay-side cap instead.
quinnbot-ai
added a commit
that referenced
this pull request
Aug 10, 2026
…and answer each request once (#69) * fix(x-mode): commit the offer marker only after its wake is emitted The poll claimed state/x-context/<request_id>.offered.json before printing the x-mention wake line, and the marker is what makes every later relay re-offer silent. A poll that stopped in that gap left a marker with no wake: the mention stayed pending in the inbox, every re-offer was discarded, and it was never surfaced again for the rest of the retention window. Claim, emit, then commit. The claim still atomically gates concurrent polls but now records wake_emitted:false, only an emitted marker suppresses a later offer, and reaching the claim with an existing marker means an interrupted offer to recover. An interruption now costs at most one repeated wake instead of a lost mention. A marker with no wake_emitted field counts as emitted, so an upgrade cannot replay an answered mention into a public reply. * fix(x-mode): post one public answer per request_id The poll offers a mention at least once: an offer interrupted before its wake is re-offered rather than silenced, because a repeated wake is recoverable and a silenced mention is not. That makes a repeated wake reachable, and nothing local stopped it from becoming a second public reply - only the relay's own 409 stood between a duplicate wake and a duplicate post. Enforce exactly-once where the public action happens. The initial answer path atomically claims state/x-context/<request_id>.answered.json before posting and refuses with exit 10 when the request was already answered or an earlier attempt's outcome is unknown. The claim is released whenever the answer definitely did not land, so an ordinary failure stays retryable, and follow-ups keep their relay-side cap instead. * no-mistakes(review): Hold ambiguous X answer claims against duplicate posts * no-mistakes(review): Confirm X answers and surface unresolved claims * no-mistakes(review): Align X responder guidance with 409 handling * no-mistakes(document): Align X-mode durability documentation * no-mistakes(lint): Suppress intentional inner-shell expansion warnings --------- Co-authored-by: QuinnBot <quinnbot@proton.me>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes a durability defect in the X-mode relay poll, found during a fork sync review of upstream kunchenguid#745.
The defect
bin/fm-x-poll.shclaimed the one-wake markerstate/x-context/<request_id>.offered.jsonand only then printed thex-mention <request_id>line that becomes the watcher's wake payload.That marker is the poll's authority for silence: the check above it returns early for any request that already has one, so every later relay re-offer is discarded without output.
A poll that stopped between those two steps left a marker with no wake - the mention stayed pending in the inbox, every re-offer was swallowed, and it was never surfaced again for the rest of the seven-day retention window.
The window is reachable in normal operation: the watcher terminates a check's whole process group on shutdown and on its per-check timeout.
The fix
Poll side - claim, emit, then commit.
The claim still comes first, but it records
wake_emitted:false, and only a marker whose wake was actually written suppresses a later offer.An interrupted offer is therefore re-offered rather than silenced, costing at most one repeated wake.
A marker written before this field existed counts as emitted, so upgrading cannot replay an already-answered mention.
Consumer side - the wake is at-least-once by design, so exactly-once is enforced where the public action happens.
bin/fm-x-reply.shclaimsstate/x-context/<request_id>.answered.jsonbefore posting an initial answer and confirms it only on a 2xx.A confirmed marker refuses a duplicate with exit 10; an unconfirmed one exits 11 and escalates rather than looking answered.
The claim is released only when the answer provably did not land, and follow-ups are excluded because they are legitimately repeated.
Design note
An earlier iteration kept the wake exactly-once with a poll-side recovery subsystem and was removed deliberately.
The private-artifact helpers offer create-exclusive publication but no atomic take-over, so each variant moved the concurrency race rather than removing it.
Preferring at-least-once wakes plus a consumer-side guarantee needs no new concurrency primitive and puts the guarantee where a duplicate would actually cause harm.
A residual delivery window remains outside the poll and is documented rather than hidden: the watcher reads the poll output, deletes the capture, and only then appends the durable wake record.
Closing it means changing the watcher's check dispatch and is tracked separately.
Tests
Colocated in
tests/fm-x-mode.test.sh, hermetic on the existing fake-curlharness.The poll crash window is reproduced by a poll whose wake cannot be delivered and by the durable state a killed poll leaves behind.
The consumer guarantee is proven by a duplicate answer refusing with exactly one relay post, an unconfirmed claim escalating instead of posting, a definite failure staying retryable, and the claim sparing follow-ups and dry-run previews.
Both new behaviors were verified to fail without their fix.
Validated through the no-mistakes pipeline: review, tests, documentation, and lint all green.