Files
stack/docs/plans/2026-10-11_bus-stranded-delivery.md
T
jason.woltjeandClaude Opus 5.5 d45ed67ca7 plans: bus stranded-delivery brief from row 59 review (Note B)
Darkwing's row 59 review (comment 27487) found that a message delivered
to a run that releases the role unread can't be received or read by the
next holder. Sage's ruling: keep at-most-once delivery, as the bus
README promises, and list stranded messages in the agents read so a
person decides whether to resend. Owner Darkwing after row 59, reviewer
Filbert, held behind the cutover with the other bus rows (decision 94).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 18:38:56 -05:00

3.2 KiB

Bus: a message delivered to a run that left is stranded (2026-10-11)

Status: written by Sage, lead. It follows docs/plans/BRIEF-TEMPLATE.md. From Darkwing's row 59 review (comment 27487 on #1548, Note B). The gap predates row 59. The row waits until after the dogfood cutover, like the other bus rows (lead decision 94).

Show messages stranded by a holder change

Problem

packages/bus/src/broker.mjs keeps a message's delivery history in the deliveries table. Three places read it:

  • message.receive (line 753) returns only messages with no delivered or read row.
  • #routeWaiting (line 892), run when a new holder claims the role, routes only messages with no delivered or read row.
  • message.read (line 784) refuses not-recipient unless the latest delivered row names the caller's run.

So when a holder receives a message and releases the role without reading it, the next holder can't receive it, can't read it, and nothing points at it. Row 59's agent-state read reports the new holder as idle, which is correct (its test 5 pins that), but the message sits unanswered. Sage checked the three queries against the code on 2026-10-11.

Redelivery is not the fix. The bus README says a delivery the broker can't confirm is never replayed to a new holder, and that holds here: the old run may have acted on the message without marking it read, so a replay can do the work twice. Sage's ruling: keep at-most-once delivery and make a stranded message visible, so a person decides whether to send it again.

Owner and reviewer

Owner: Darkwing, after row 59. Reviewer: Filbert.

Files owned

  • packages/bus/src/broker.mjs, the read that row 59's agent state uses only
  • packages/bus/src/views.mjs, if the agents view passes the field through
  • packages/bus/tests/
  • packages/bus/README.md, the delivery paragraph and the agents read only
  • agents/darkwing/work/queue-<row>/

What ships

  • A stranded message is one addressed to a role, with a delivered row whose run no longer holds that role, and no read row.
  • The agents read gives each role a stranded list: message id, from, the run it went to and when, oldest first, at most 50. An empty list when there are none.
  • No message is redelivered, re-routed or marked read by this row, and no delivery row is written or changed.
  • The README's delivery paragraph says what a release without a read leaves behind and where to see it.
  • Tests: deliver, release unread, claim by a new run, and the message is listed for the role; the same after a read is not listed; a message delivered to the current holder and unread is not listed; the list cap and order; the agents read for a role with none.
  • The packet names what the Console would need to show the list. That is a follow-up row, not this one.

Out of scope

Redelivery, any new write verb, the human transport, the deliveries schema and any Console change. If row 59 lands with a shape this can't extend without breaking its tests, stop and tell Sage.

Gate

Filbert approves on the row's issue naming the candidate manifest. The bus, webui, control-board and cli node suites and every scripts/test-*.sh green on Sage's gate rerun.