Skip to content

fix(tui): harden split input and paste recovery - #699

Closed
yxlyx wants to merge 2 commits into
mainfrom
fix/537-reset-dropped-input-state
Closed

fix(tui): harden split input and paste recovery#699
yxlyx wants to merge 2 commits into
mainfrom
fix/537-reset-dropped-input-state

Conversation

@yxlyx

@yxlyx yxlyx commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

What changed

  • Preserve bounded terminal-sequence framing across split reads, including CSI, SS3, X10/SGR mouse, kitty CSI-u, OSC, and bracketed-paste markers.
  • Route paste input before global controls so pasted C0 bytes, mouse reports, and modifier sequences cannot quit, cancel, navigate, or mutate held-key state.
  • Clear stale modifiers on every abandoned-sequence path and make normal-turn/background thread-spawn failures report asynchronously instead of blocking the TUI loop.
  • Serialize /model mutation while background compaction owns the shared runtime context.
  • Add reached parser/simulator/runtime tests and an offline trajectory-proven PTY probe across idle, live-turn, compact, bash, and file-list trajectories.

Why

Problem / failure mode

The audit findings in #537 shared one state-machine failure: when terminal input was split, delayed, truncated, or filled the read buffer, the TUI could lose framing and reinterpret protocol bytes as user controls. That could cancel live work, quit during paste, erase a draft through stale Super state, fabricate wheel/click events, type protocol debris, or freeze the renderer if thread creation failed.

Reason for this approach

Recovery now distinguishes strong evidence (a dropped non-lone sequence head) from an ambiguous genuine Escape. Strongly evidenced heads retain exact bounded framing; mismatches fail closed and fresh human input wins. Paste markers restore paste state before same-read payload, while all runtime trajectories share the same guarded batch dispatcher. This fixes the root state transitions rather than adding per-sequence string filters.

Constraints and trade-offs

  • A lone Escape waits about 300 ms while idle and up to one second while cancellable work is live, because it is indistinguishable from the first byte of a delayed paste marker. Ctrl-C and kitty CSI-u Escape remain immediate.
  • Carry-expired ambiguous short CSI/SS3 tails become text; self-identifying framed protocols can still recover within the bounded window. Preserving human text is safer than guessing an arrow from [Alice].
  • Proven but oversized/misaligned mouse debris is dropped rather than fabricated into an action.
  • The test-count floor is 1860 because macOS deterministically reports the remaining platform test as one skip; non-skipped platforms still run 1861.

Rejected alternatives

  • Calling the global input reset on every drop would destroy paste/orphan recovery along with stale modifiers.
  • Broad late-tail guessing corrupted ordinary bracketed prose and fabricated mouse fields when a prefix was missing.
  • Running provider/tool work inline after Thread.spawn failure preserved completion at the cost of freezing all input and rendering.

Verification

  • scripts/eval-tier1.sh: green
  • main suite: 1860 passed, 1 platform skip
  • TUI suite: 505/505 passed
  • PTY/tuiguard: 18/18 passed, including scripts/test-tui-escape-split.py
  • formatting, 600-line ceiling, test reachability, invariants, and SDK drift: green

Closes #537.

yxlyx added 2 commits August 31, 2026 12:13
Dropped and delayed terminal sequences could cancel live work, execute pasted control bytes, retain stale modifiers, or leak protocol tails into the composer. Preserve bounded framing evidence across reads, fail closed on ambiguous debris, keep paste dispatch ahead of controls, and make turn/background spawn failures non-blocking across the fullscreen TUI runtime.\n\nAdd reached simulation, parser, batching, and offline PTY regressions for every affected idle, live-turn, compact, bash, and file trajectory.\n\nCo-Authored-By: Codegraff <blackfloofie@codegraff.com>
Rebasing the input-recovery fix onto v0.0.282 pushed two touched modules over the source ceiling. Move their existing test blocks into reached siblings and set the suite floor to the deterministic macOS pass count, where the remaining platform test is reported as a skip rather than a pass.\n\nCo-Authored-By: Codegraff <blackfloofie@codegraff.com>
@justrach

Copy link
Copy Markdown
Owner

Folded onto release/v0.0.283 (PR #703) as edfb065 + 0823491.

Kept 283's #704 tuiguard deadlines (ThreadPool + killpg) and the 1868 suite floor; added test-tui-escape-split.py as the 18th probe with a 90s budget. Did not take the 1860 macOS floor drop.

Tier 1 green on the push (TUI 505/505, tuiguard 18/18). #537 closed from the release branch. This PR can close as superseded once you are happy — leaving that to you.

@justrach

justrach commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Shipped in v0.0.283 via #703 (edfb065 / 0823491). Closing as superseded — not merging this branch to main.

@justrach justrach closed this Sep 1, 2026
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.

TUI escape/death audit round 2: 12 unverified findings

2 participants