Workspaces 5/10: hold derived events in the transfer gap; drift cleanup - #618
Conversation
pty:data in the gap is dropped because the replay carries the bytes, but terminal:semanticEvents and terminal:protocolEvents are derived once at the sidecar's parse site and ride no replay, so a prompt mark, cwd change, or notification landing mid-transfer never reached the target. Rust now holds them per id, bounded, and emits them to the new owner right behind the replay that lifts the suppression; a hand-back or exit drops them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PkPyEFCxiPo5UFeju5Ya9u
- The three updater comments still described the reverted every-window grant; they now match capabilities/main-only.json, and the specs name the capability as written, updater:default. - layout.md claimed every Workspace verb has a dor counterpart; reorder, transfer, and tear-out do not yet. - A source test pins that main.js hands pty-core the shared sliceSince: without it recovery capture reads an empty buffer with no error. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PkPyEFCxiPo5UFeju5Ya9u
Deploying mouseterm with
|
| Latest commit: |
4a2e75f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://dfe403e5.mouseterm.pages.dev |
| Branch Preview URL: | https://workspaces-harden.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
Feedback on work in progress, not a merge verdict — mark it ready when you want the full review.
The routing change rests on the premise that these events are in no replay. For terminal:semanticEvents in the standalone host that isn't true: the replay carries the raw OSC bytes and tauri-adapter.ts re-derives the semantic events from them itself, so the flushed queue applies them a second time. Mechanism and the narrower fix are in the thread on routing.rs. The other threads are the held queue's lifecycle and two spec claims.
…replay The hold queued both derived streams on the premise that neither is in any replay. Semantic events are: the replay carries the raw bytes, OSCs included, and the target's `pty:replay` listener re-parses them. The flushed queue then re-applied `commandStart` on top of state the replay had just rebuilt, and `commandStart` is not idempotent, so a transfer that split a `commandLine` from its `commandStart` left the arriving window with a derived title for a command whose real line the replay had recovered. Semantic events now route to `Drop` while suppressed; only `terminal:protocolEvents`, which no replay path rebuilds, are held. What the replay path genuinely did not rebuild was the AlertManager's half, so both adapters' replay listeners now feed it too, and the tests pin a watched command coming back from a replay alone. `clear_suppression` and `mint` cleared `awaiting_replay` and left `held` behind, so a queue could survive until the shell exited and be flushed ahead of the next transfer's own gap. One helper, `lift_suppression`, now takes both halves together, and every site goes through it. The hand-back comment claimed the source saw the gap's bytes live; it did not, since suppression is by id and ownership had already moved. The comment and the Arrival queue spec now say the gap is lost on a hand-back, the one path nothing recovers on this branch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RChsJ5rMUMyfu22UZDfUus
Carries the review fixes from the earlier stages up the stack. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RChsJ5rMUMyfu22UZDfUus
dormouse-bot
left a comment
There was a problem hiding this comment.
Feedback on work in progress, not a merge verdict — mark it ready when you want the full review.
The semantic-events half of the fix doesn't survive the arrival path it was written for: planArrival calls alertSeed for every transferred pane right after collectLivePtys resolves, and AlertManager.seed nulls commandExitWatch, so the WATCHING state the replay just rebuilt is gone a turn later. Mechanism, and why this is a regression against the previous Hold, are in the thread on standalone/src/tauri-adapter.ts. The other thread is the hand-back comment against the spec line it cites.
Everything else from the last round reads as addressed — the routing split, the lift_suppression consolidation, the HELD_EVENTS_MAX bound in the table, and the layout.md reorder line.
# Conflicts: # scripts/spec-word-budgets.json
dormouse-bot
left a comment
There was a problem hiding this comment.
Feedback on work in progress, not a merge verdict — mark it ready when you want the full review.
The reorder does close last round's thread: alertSeed now runs before adopt_ready, so the replay listener's applyTerminalSemanticEvents is the last writer of alert state on the arrival path, and alert.md -> "Public State" already guarantees the pre-registration seed survives (Must retain host Activity before xterm initialization). One decision the reorder raises, plus the thread on workspace-move.ts.
The seed now runs before the point where planArrival can still throw. On the live.timedOut path the Workspace is handed back and handleArrivalFailed only clears the source's transferring mark, so this window keeps an AlertManager entry — and, through onStateChange -> setTerminalActivity, an activity-store entry — for panes that stay with the source and never mount here. Nothing renders them that I can find: getLivePersistedAlertState returns null for an unregistered id so they are not persisted, and a second arrival of the same ids seeds over them. So it is retained state rather than a wrong display, but it is new with this commit — previously the throw came first. Either clear it on the throw, or say in standalone.md -> "Arrival queue" that a refused arrival leaves its seeded alerts behind, so the next reader does not have to re-derive that it is harmless.
There was a problem hiding this comment.
nedtwigg's replies on the two open threads landed a few minutes before this review and
my snapshot predated them, so an earlier version of this body listed them as
outstanding. Withdrawn: both are deferred to #630 / 3c5fc72c, which does touch
standalone/src-tauri/src/lib.rs and standalone/src/workspace-move.ts.
The seed-before-throw point from the last pass — planArrival seeding before the
live.timedOut throw, so a refused arrival leaves an AlertManager entry, and through
onStateChange an activity-store entry, for panes that stay with the source — reads as
falling under the same consolidation. Say so if it does and it is closed with them.
That leaves the one new point inline, on the held queue's remaining escape hatch. The
verdict is comment rather than approval on that alone.
|
Both open points check out against #630's head ( Held queue — closed. Seed before throw — consolidated there too; the |
terminal:semanticEvents/terminal:protocolEventslanding while an id is suppressed are held in Rust and delivered behind the replay, instead of dropped. Stale updater comments and theupdater:*vsupdater:defaultspec mismatch corrected; a source test pins thatmain.jshands pty-core the sharedsliceSince.Based on
workspaces-dor.🤖 Generated with Claude Code
https://claude.ai/code/session_01PkPyEFCxiPo5UFeju5Ya9u