Skip to content

Fix routing misdelivery of buffered steering (#70) - #5

Merged
frgmt0 merged 1 commit into
mainfrom
beckett/task-70-routing-audit
Jul 20, 2026
Merged

Fix routing misdelivery of buffered steering (#70)#5
frgmt0 merged 1 commit into
mainfrom
beckett/task-70-routing-audit

Conversation

@0xbeckett

Copy link
Copy Markdown
Collaborator

Audit: agent-service routing path

Follow-on to #58. Scope: the routing path in the engine — how messages / context / steering reach the right running seat (the "live-agent registry + invoke lane"). I enumerated every routing surface, gave each a real / not-real verdict, and fixed the real ones with tests. Nothing here needed to touch beckett core; the fixes live entirely in bored's engine/run layer.

Routing surfaces enumerated

# Surface Verdict Action
1 Buffered steering loses its target node REAL Fixed + test
2 Steer dropped by a reaped-but-still-registered seat is lost REAL Fixed + test
3 Fanout: a buffered steer folds into only the first arm to spawn real but minor / arguably by-design Noted, not fixed
4 nudge() broadcast returns only the last seat's receipt not real (cosmetic) Noted
5 No way to address a single fanout arm (only the node) not real (out of scope; API shape) Noted
6 routeSignal / routeArmSignal deliver a done-signal to the wrong cursor/arm not real Verified correct
7 deliverWorkerEvent processes late events for already-resolved seats not real Verified guarded
8 A node-targeted steer for a node that never runs again is never delivered acceptable trade-off Noted

The two real fixes

1 — Node-targeted steering lost its target when buffered.
nudge(ref, text, node) addressed to node A, buffered because A had no live seat, was queued carrying only its text. The fold then drained the entire pendingSteers buffer on the next seat_spawned of any node, folding it into that seat's brief. So steering meant for lane A could land in lane B's brief — a genuine misdelivery, exactly the class of bug the invoke-lane / live-agent-registry work exposes.

Fix: the queued nudge_delivered event and pendingSteers now carry the target node. The fold drains — and the seat brief collects — only steers addressed to the spawning node or to no node in particular (s.node == null || s.node === node), using the same predicate on both sides so what leaves the buffer is exactly what the seat reads. Untargeted steers (the common case) behave exactly as before. Backward-compatible with pre-fix event logs (missing node ⇒ untargeted ⇒ old behavior).

2 — A steer a reaped seat dropped was silently lost.
The in-memory live-seat registry can still list a seat whose child has already exited (the finished event is in flight but not yet folded). handle.nudge() returns "dropped", and the engine recorded the drop without buffering — the operator's steering vanished even though a replacement seat was about to spawn.

Fix: when every targeted live seat drops the steer, the engine re-queues it (as a queued nudge_delivered) so the replacement seat picks it up. If any seat accepts it, behavior is unchanged.

Tests

test/engine-worker.test.ts, in the steering (§5.5) block:

  • a node-targeted steer never leaks into a different node's seat — a review-addressed steer buffered while an implement seat is live does not fold into the implement retry seat, stays buffered, and reaches the review seat when it finally spawns.
  • re-buffers a steer that a reaped seat drops — a steer dropped by a registered-but-exited seat is re-queued and carried by the retry seat.

Full suite: 171 passed, typecheck clean.

Notes on the non-issues (so they aren't re-litigated)

Leave this PR unmerged per the ticket.

🤖 Generated with Claude Code

@frgmt0
frgmt0 merged commit 2441f6f into main Jul 20, 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.

2 participants