Repository navigation
sminos bridge: a busy session reads a delivery at its next tool round; a delivery wakes a stopped session - #233
Merged
Merged
Conversation
SSFSKIM
force-pushed
the
bridge-midturn
branch
from
October 18, 2026 07:33
8063029 to
cfeab33
Compare
SSFSKIM
force-pushed
the
bridge-dedupe
branch
from
October 18, 2026 07:33
039c10b to
48c00a6
Compare
SSFSKIM
marked this pull request as draft
October 18, 2026 07:38
Owner
Author
|
Held until the desk reads its rows. #233's mid-turn deliveries are |
…; an Esc no longer loses one deliver() submitted every delivery with $.prompt.submit, a turn of its own that runs once the session is idle: a busy recipient read the message only when its turn ended (median 39 s, max 253 s on this machine), against sminos's contract of the next tool round. While the main turn runs, the delivery now goes in as a steer's row does ($.session.append, read at the loop's next request), with the steer's wake when the turn answers before reading it, and `entered` named as soon as the append returns. An Esc empties the queue of plugin prompts and refuses each new one until the human sends input (2.1.300 zBt / k_e). An idle delivery the engine drops for that reason now goes in as a row, which starts no turn; any other drop stays `refused`. An Esc before the model answered hands the prompt back and the human's resend goes in where it stood, which leaves a row given after it on a branch no request reads: each delivered row of a turn that did not answer (or appended idle) is checked against the conversation as the next turn starts, and given again when it is missing. The turn's first request carries the conversation as the turn began, so a row appended between turn.start and it no longer counts as read (steers included): a turn that answers in one request wakes once. Live: tests/mods/live-delivery-cancel-probe.py (busy, bash-esc, restore, after-esc).
…at ends under it, and a /clear Three review findings on the mid-turn delivery change: - A compaction of the main conversation lets every tracked delivered row go (bridge-rows.tsx, compacted: the bridge's compact, the compact tool's, and the session.compact hook all pass through it). The compacted conversation holds a row only in its summary, so the next turn's check read a row that was delivered and read as missing and gave it twice. A row a rewind left behind before the compaction is the accepted loss. - recheckDeliveries binds every due row's steer to its turn before its first await, the conversation read included, so a turn that ends while the check runs stamps them all and settleSteers owes the wake. Bound one at a time across the appends, a replay bound after the turn ended never woke. Each is let go as its row proves held, or settled as its append is. - A /clear while a check runs ends it (state.conversation, the counter a /clear moves): nothing is appended to the conversation that ended, and its steers wake nothing in the new one. An append pending across a /clear no longer records its row for the new conversation's check. Tests: one per finding in tests/mods/bridge.test.ts (deliver), each failing without its fix; the harness can now hold the conversation read.
After an Esc the engine refuses every plugin prompt until the human sends input (k_e), so an idle delivery used to go in as a row that started no turn. A message addressed to a session is meant to wake it, and k_e does not guard the session's messaging socket, so: - an idle delivery whose prompt the engine refuses for a stop (decided at submit; the submit is raced against STOP_DROP_WAIT_MS) is answered not-ok, its mark put back and nothing emitted; sminos_control.deliver returns False and push_live sends the inbox frame, which starts a turn (one-line warning naming the stop). A stop drop after the answer goes to the child as an `inbox` event carrying the text and key. - a delivered row a stopped turn left unread emits one `inbox` wake per turn; a turn that answered with one unread, whose STEER_WAKE is stop-dropped, wakes the same way. Steers are unchanged. - the child sends `inbox` events as frames to the session's own socket from its export thread; a keyed one no socket took is kept as a refusal. Live (2.1.302, haiku): an Esc'd session delivered to through `sminos send` starts a turn and reads the message; a row delivered mid-Bash and Esc'd before any request read it wakes the session, which reads it.
…a /clear ends pending wakes; a full queue keeps a keyed inbox frame as a refusal - A STEER_WAKE queued behind the human's prompt could be dropped by an Esc after that prompt's turn had read the delivered row, and the drop still sent the inbox wake on the turn's snapshot. The wake is now tracked (state.wakes) and marked read by the main loop's next request that carries its rows (any request, but the first of a turn already running when it was given); the inbox frame goes only if it is still unread when the drop settles. - A delivery's append pending through an Esc and a /clear settled into a steer that survived forgetLive, so DELIVERY_WAKE reached the new session's socket. forgetLive now lets every steer and pending wake go. - The child's _inbox kept a keyed frame a full export queue dropped only as a stderr line, its sender already told `submitted`. It is now kept as a refusal, as _send_inbox keeps one no socket took, so the daemon reports `failed`. Logged as debt: a late-drop keyed inbox event whose post fails before the child takes it is lost with no refusal (the resync carries no inbox work).
…script's reader tells it from the human's prompt
… round; a delivery wakes a stopped session
SSFSKIM
force-pushed
the
bridge-midturn
branch
from
October 18, 2026 09:10
cfeab33 to
b82f831
Compare
SSFSKIM
marked this pull request as ready for review
October 18, 2026 09:19
This was referenced Oct 18, 2026
SSFSKIM
added a commit
that referenced
this pull request
Oct 18, 2026
…d a compaction meanwhile gives none again Re-review findings (reviewer-medium, two P2): - The next turn's check (recheckDeliveries) left its rows tracked while it read the conversation, so a stopped turn's check that started meanwhile (its wake delayed by an append pending at the turn's end) could take the same row, and both gave it again. Each check now takes its rows out before it reads; an unreadable conversation puts them back. - A compaction that completed while either check read the conversation cleared the tracked rows, but the rows the check held escaped and were given again (at once, or put back for the next turn's check). A compaction counter now fences both checks as a /clear does. The next turn's check had this race since #233.
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.
An audit of every addressed sminos message on this machine (283 pairs, matched against the recipients' transcripts) found two bridge problems besides #229:
deliver()sent each message with$.prompt.submit. The engine queues plugin prompts at prioritylater, and the mid-turn fold skips that priority (2.1.300:y6e,Air). So a busy recipient read a message only when its turn ended: median 39 s, max 253 s, unbounded in principle. sminos documents "a busy one reads it at its next tool round", which is what the older inbox path did (median 4.8 s).zBt). After an Esc, the engine also refuses every new plugin prompt until the human types (k_e,tengu_plugin_input_refusal). doperpowerse3ce7feb:8→ CI-opt arrived in that window and was lost, although sminos settled it.Change (hooks/mods/bridge.tsx, sminos_bridge.py, sminos_control.py)
$.session.append), read at the next request. It uses steer's bookkeeping: if the turn answers before reading the row, it wakes once. Measured live: read 10.4 s after delivery, the rest of a 12 s Bash call.inboxevent, which the child writes to the same socket. Steer keeps waking nothing after an Esc./clearlets tracked rows go.[sminos bridge]:STEER_WAKEwas the human-voiced "I added a note…". A transcript's reader can now tell a wake from the human's own prompt; the MAWS desk draws it as a system line.deliver,delivered,steerand the newinboxrow are updated indocs/doperpowers/plans/2026-10-15-arkho-desk-c2-bridge.md.Verification
tests/mods/live-delivery-cancel-probe.py, Claude Code 2.1.302, haiku, run by hand. Busy delivery read at the next tool round. Delivery after an Esc starts a turn. A row a stopped turn left unread wakes the session. A row lost to a rewind is given again.bridge.test.ts: 449 pass.test-bridge-control.sh: 104 pass. Each new test fails with its fix reverted./clearduring re-check, stale wakes, a full child queue). One was logged indocs/doperpowers/TECH-DEBT.md: a late-drop inbox event lost when a local post fails. That needs two rare failures at once.hub@73f8271d), confirmed by the desk session before merge.Left open
sminos watch, but become an outcome only in hub projects.refused, not a silent loss.Version 7.176.0.
🤖 Generated with Claude Code