Skip to content

fix(app): a cursor key before Enter ends codex's paste classification, so injected messages actually submit under load - #620

Open
ebigunso wants to merge 1 commit into
fujibee:mainfrom
ebigunso:fix/app-codex-inject-submit
Open

fix(app): a cursor key before Enter ends codex's paste classification, so injected messages actually submit under load#620
ebigunso wants to merge 1 commit into
fujibee:mainfrom
ebigunso:fix/app-codex-inject-submit

Conversation

@ebigunso

@ebigunso ebigunso commented Aug 4, 2026

Copy link
Copy Markdown

Problem

Fixes #619. On a stalled or loaded machine, Codex reads pty_inject's text and trailing \r in one batch, classifies the run as a paste, and swallows the Enter — the kickoff sits in the composer unsubmitted. The old fixed 300ms gap is open-loop and cannot survive a stall that spans it (measured: 2/5 under full CPU load; 0/5 with the gap widened to 1000ms). Retries sent while the stall persists coalesce into the same wake batch and are absorbed too (text + 6 Enters in one read: six newlines, nothing submitted) — only a writer-side sequence that is safe within a single batch can be deterministic.

Fix

Submission becomes text → 300ms → Right-arrow → 50ms → Enter (write_submit). A non-char key makes codex force-flush the burst buffer and clear the Enter-suppress window before the key is handled, so the Enter that follows submits even when the whole sequence arrives in one batched read. A Right-arrow at the end of composer text is a cursor no-op in every TUI the app spawns. Result on the failing reproductions: 11/11 submitted (stall / full load / gap-0 single-batch) with no idle or mid-task regression.

Two guards ride along, both motivated by the same investigation:

  • Per-pane inject serialization (inject_lock): two messages arriving close together no longer interleave their text/Enter writes.
  • State-watching backstop: after submitting, sample the pane's detected state; if it never leaves Idle, re-submit (same arrow+Enter sequence) at spaced checkpoints — validated 3/3 as a recovery path on its own, and harmless at an idle empty composer. Working = submission took; Blocked = an approval dialog owns the keyboard — never touched. Verification is by state transition, deliberately not by screen content: codex drops non-ASCII on redraw (the kickoff's own em dash) and its title-escape churn (Desktop app (Windows): input cursor flickers with Codex CLI output in the web-based terminal pane #383) ages injected text out of the 8KB tail buffer within seconds, so a screen-scrape check false-negatives exactly when it matters.

Validation

Files

  • app/src-tauri/src/pty.rswrite_submit, verify_action, inject_lock, doc comments recording the mechanism
  • app/README.md — delivery description

🤖 Generated with Claude Code

…, so injected messages submit under load

pty_inject wrote text, slept 300ms, then wrote \r — but codex's paste
heuristic runs on its own read timeline, so any event-loop stall spanning
the gap batches text+Enter into one read, classifies it as a paste, and
swallows the Enter as a newline; the kickoff sits in the composer
unsubmitted. Measured on a real Windows machine: 2/5 submitted under full
CPU load, 0/5 with the gap widened to 1000ms, and Enters that land while
the stall persists are accumulated into the burst (text + 6 Enters in one
wake batch: six newlines, nothing submitted).

Submission is now text -> 300ms -> Right-arrow -> 50ms -> Enter
(write_submit): a non-char key makes codex force-flush the burst buffer
and clear the Enter-suppress window before the key is handled, so the
Enter submits even when the whole sequence arrives in one batched read —
11/11 across the stalled/loaded/one-batch reproductions that failed
0-for-all before, verified on codex-cli 0.145.0 and 0.146.0.

Also serializes concurrent injections per pane (interleaved text/Enter
writes from two near-simultaneous messages) and adds a state-watching
backstop: re-submit at spaced checkpoints while the pane never leaves
Idle; never touch a Blocked pane (an approval dialog owns the keyboard).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant