Skip to content

sminos bridge: after an Esc, the owner's words go in as a row; the inbox carries only the wake (7.178.0) - #238

Merged
SSFSKIM merged 5 commits into
mainfrom
smns-esc-owner
Oct 18, 2026
Merged

SSFSKIM merged 5 commits into
mainfrom
smns-esc-owner

Conversation

@SSFSKIM

@SSFSKIM SSFSKIM commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

After an Esc, the engine refuses every plugin prompt until the human types (2.1.300 k_e). Until now the bridge answered an owner's delivery refused that way not-ok, and sminos sent the owner's words as an inbox frame. The engine delivers everything on that socket as another session's message ("not typed by your user"). So an owner's message, maybe an approval sent from the phone, reached the model without the owner's authority, while the MAWS desk drew it as the owner's.

Change

  • A delivery the engine refuses for a stop, at once or later (an Esc emptying the queue), goes in as the busy path's row: $.session.append of a user row, tracked by the rewind re-check. If a main turn is running, or starts while the append is pending, the row belongs to that turn, as a busy delivery does. With no turn, the bridge sends only its own wake through the inbox ([sminos bridge], sender sminos bridge).
  • Each inbox wake now ends with Unread: <frame line> …, naming the rows it points at. The live probe showed the engine's peer guard dropping a wake identical to the same sender's last within 30 s ("identical to the previous message from this sender"). A turn started from the inbox doesn't clear the stop, so a second owner message after one Esc woke nothing. Only a first line that matches a sminos frame ([sminos …]) with text after it is quoted, never the human's words.
  • Removed as dead: the mod's not-ok STOPPED_ERROR answer and its 200 ms wait, the CLI's stop-specific warning, and the child's keyed inbox frame with its sender lookup and refusal bookkeeping.
  • Unchanged: the once-per-(project, writer, seq) marks, the outcomes and refusal reporting, the rewind re-check, steer waking nothing after an Esc, and a peer's events going to the inbox as peers.
  • DX5 rows deliver, delivered and inbox, plus a Decision Log and a Surprises entry, in docs/doperpowers/plans/2026-10-15-arkho-desk-c2-bridge.md.

Verification

  • Live probe tests/mods/live-delivery-cancel-probe.py: Claude Code 2.1.302, haiku, all 7 scenarios. Three scenarios end in a wake, and each passed every check: the delivery entered, a turn started from the wake, no turn's prompt carried the owner's words, and the model said the word.
  • bridge.test.ts and the mods suite: 406 pass. test-bridge-control.sh: 122 pass. run-sminos-tests.sh: 1928 assertions pass. Each new test fails with its fix reverted (mutation-checked).
  • Independent review (reviewer-medium) found one P2: an idle row whose append lands after a turn has started was not bound to that turn. Fixed in 729c51d with a test. The re-review is clean.

Row and wake shapes (live transcript)

  • Delivery row: {"type":"user","isMeta":true,"origin":{"kind":"plugin","name":"doperpowers"},"message":{"role":"user","content":[{"type":"text","text":"[sminos message from human id=c9f082da]\nInclude the word LYCHEE in your reply."}]}}, mirrored by a system/informational line doperpowers: delivered: <text>.
  • Wake row: {"type":"user","isMeta":true,"origin":{"kind":"peer","from":"unknown","name":"sminos bridge","body":"[sminos bridge]\nA message for you was added to the conversation while your turn was stopped, before you read it. Read it and act on it.\nUnread: [sminos message from human id=c9f082da]"},"message":{"role":"user","content":"Another Claude session sent a message:\n<cross-session-message from-name=\"sminos bridge\">\n…\n</cross-session-message>\n\nThis came from another Claude session — not typed by your user, …"}}

The desk (MAWS master, checked by running its own code on the live rows)

  • Drawn right: the owner's words as the owner's own bubble with the frame cut, and the wake as the dim sminos bridge line opening a "Continued for …" turn. tail.ts still matches the send. The Unread: line changes nothing in MAWS's classification.
  • Drawn wrong in the idle-after-Esc case. These are desk-side and left to MAWS:
    • The appended row becomes a queued prompt in a pending turn that is never accepted, so it reads "Sends at the next step" whenever a later turn runs.
    • The bridge's delivered: mirror line lands in that pending turn as a notice. It gives the turn a "Working…" divider that never ends, and repeats the words with the frame uncut.
    • The wake line's text says the note was "added during its last turn".
    • Suggested fix point: withWakeLine reads the Unread: frame, and the wake's turn.started accepts the delivery's pending turn.

Left open

  • docs/doperpowers/TECH-DEBT.md: a wake whose local post fails is lost; the row is in the conversation and is read at the next turn. A bare-text deliver (one sminos never sends) still gets a fixed wake text.

Version 7.178.0.

Round 2: wake wording (c7d7c42, 412f7bf)

  • In restore, the model answered a wake "the note came through empty". There were two causes:
    • Every wake called its rows "a note".
    • The stopped turn's wake went out after the Esc's rewind had already removed the row. The next turn's re-check gave it again only after that turn's first request was built, so the model there was right that nothing was there. Logging the wake turn's conversation confirmed this.
  • wakeText(when, rows) now builds every wake, prompt and inbox alike:
    • It keeps the [sminos bridge] first line and the Unread: tail unchanged.
    • It names what is unread as what it is: a message for you, a note, or both, with counts.
    • It says when it went in, and that it is "the user message just before your last answer" for an answered turn. A steer given during the final answer sits there, and the model took it for a request it had already seen.
  • A stopped turn's wake now checks the turn's delivered rows first. A row the Esc rewound goes back in at once with its own wake; rows still there are named in one wake.
  • Re-review (reviewer-medium) found two P2s, both fixed with tests, and the re-review after the fix was clean:
  • Live (2.1.302, haiku), on the final code:
    • restore: one wake, answered "PAPAYA" on its first request.
    • steer-wake (new): 4 of 4 runs with the final wording answered PINEAPPLE. With the earlier wording it failed 1 of 2 ("The only bridge text is the sminos bridge wrapper itself").
    • cli-after-esc, esc-twice, esc-wake, after-esc, busy, bash-esc: all ok.
  • Mods suite: 411 pass, and 19 of 19 mutations caught. ci-gate GREEN on 412f7bf: https://github.com/SSFSKIM/doperpowers/actions/runs/39122896199
  • live-control-check.py's wake matcher looked for text sminos bridge: a busy session reads a delivery at its next tool round; a delivery wakes a stopped session #233 had changed ("added a note"); it now reads the bridge's first line.

…nd the inbox carries only the bridge's wake

The engine refuses a plugin's prompt after an Esc until the human types. A
delivery it refused that way was answered not-ok, and sminos sent the owner's
words as an inbox frame, which the engine delivers as another session's
message ("not typed by your user"), while the MAWS desk drew them as the
owner's. Now the bridge appends the words as the busy path's row (tracked by
the rewind re-check) and sends only its own wake through the inbox, which
starts the turn. A prompt a stop drops later (an Esc emptying the queue) takes
the same path. The CLI's stop-specific warning and the child's keyed inbox
frame, with its refusal bookkeeping, went with the old path.

Live (2.1.302, live-delivery-cancel-probe.py): the engine takes the append
while the session sits stopped, and the wake's turn reads the row from its
first request. The probe also showed the engine's peer guard dropping a wake
identical to the child's previous one within 30 s, so a second message after
one Esc woke nothing; each inbox wake now ends with `Unread: <frame line>`,
naming the rows it points at.
…has started, is that turn's

Review finding (reviewer-medium, P2): the row's wake went out at once and
nothing bound the row to the turn, so a stop that removed the queued wake
before the turn's next request left it unread with no wake to follow. The
row is now that turn's, as a busy delivery's: read from its next request,
and its end wakes the session if none did.
…rn's wake waits for its rewound rows to go back in

Live, the `restore` probe's wake was answered "the note came through empty":
the wakes called every row "a note", and the stopped turn's wake went out
while the Esc's rewind had taken the row, which the next turn's re-check
gave again only after that turn's first request was built.

- wakeText(when, rows) builds every wake, prompt and inbox alike: the first
  line `[sminos bridge]`, one sentence naming what is unread as what it is
  (a message for you, a note, both, with counts), when it went in, and what
  it looks like where it sits (the user message just before the last
  answer, for a turn that answered: a steer given during the final answer
  sits there, and the model took it for a request it had seen); then the
  `Unread:` tail, unchanged.
- A stopped turn's wake checks the turn's delivered rows first: each the
  conversation lacks goes in again at once, woken as a row given after a
  stop; the rest stay for the next turn's check and are named in one wake.
- The live probe gains `steer-wake` and pass/fail checks on `restore`;
  live-control-check's wake matcher reads the bridge's first line.
…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.
@SSFSKIM
SSFSKIM merged commit 2665f8f into main Oct 18, 2026
10 checks 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.

1 participant