Repository navigation
test(cli): give the PTY harness a real window size - #393
Merged
Merged
Conversation
…ght hang) PR #372's `pty_tty_e2e.rs` opened its PTY with `openpty(None, None)`, i.e. a 0x0 window. Its own two tests drive one-shot `generate` and are unaffected, but the `run` REPL renders through ratatui's inline viewport, whose `insert_before_no_scrolling_regions` infinite-loops at 0 rows (`to_draw = min(h, 0) = 0`, never makes forward progress). The next author to add a type-ahead / streaming test on top would hang the harness for the 30s `drain_pty_master` timeout and then time out CI, with no obvious cause. Set a real 24x80 `Winsize` in `open_pty()` and document at the call site both why a non-zero window is mandatory and the companion CR-not-LF gotcha (a terminal sends `\r` for Enter; feeding `\n` never completes the line and hangs `read_line`). Both are "false swallow" traps for anyone extending this harness. Confirmed neutral: `stats_single_line_when_stderr_is_piped_and_stdout_is_tty` passes at both 0x0 and 24x80; `tty_stdout_gets_exactly_one_trailing_newline_ after_streaming` behaves identically at both sizes (its outcome depends on the drain timeout vs model-load time, not the window size). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #393 +/- ##
==========================================
+ Coverage 80.59% 81.38% +0.78%
==========================================
Files 311 311
Lines 121012 121100 +88
Branches 121012 121100 +88
==========================================
+ Hits 97530 98555 +1025
+ Misses 19504 18558 -946
- Partials 3978 3987 +9
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
🔴 Benchmark Regression DetectedComparison of criterion micro-benchmarks: PR head vs merge-base, measured on the same runner in the same job (base first → PR second).
Visual flags: Host infoWhat this cannot catch
|
`drain_pty_master` had a bare 30s total budget. The child is silent on the PTY
until its first token (model load logs go to stderr), so on a slow machine the
budget can expire during load and return an empty read — which then trips the
trailing-newline assertion and masquerades as a rendering defect. On the slowest
machine measured (a debug build over WSL 2 `/mnt/c` under load), cold
load-to-first-token was ~48s, i.e. the old 30s budget already lost by 18s; it
was a coin flip that had been landing the right way. A magic constant like that
is exactly what RULES.md rule 2 exists to catch.
Make it an idle timeout instead: each poll() waits up to DRAIN_IDLE_TIMEOUT for
the *next* byte, so the clock resets on every read. A slow start or a long
stream is tolerated; only a genuinely stuck child (no byte for the whole window)
fails, and it fails promptly rather than hanging to the job limit. The budget is
120s, justified in a comment against the measured ~48s worst case (~2.5x
headroom for a cold CI runner), not picked to feel safe.
Draining now returns `DrainOutcome { bytes, timed_out }`. Test 2 checks
`timed_out` before asserting on content and reports a timeout distinctly — "this
is a stuck or too-slow child, NOT a trailing-newline defect" with an ordered
checklist — so a timeout can never wear the content assertion's clothes
(RULES.md rule 1).
Validated on WSL 2 (the machine that exposed the flake): both pty_tty_e2e tests
now pass with the 120s budget where 30s returned empty; clippy -D warnings is
clean there (the file is #[cfg(unix)], never linted on Windows).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
justinchuby
pushed a commit
that referenced
this pull request
Jul 29, 2026
Recovered decision records from agents' per-worktree inbox files, which were deleted before Scribe ran. Substance survived in history.md and PR descriptions; durable rules now appended to decisions.md: From Rachael (PR #372): - Probe the stream you are writing to, not a convenient nearby one - Run the gate command CI runs, verbatim — local subset validation fails - Terminal behaviour requires PTY-driven tests; piped I/O cannot cover it From Zhora (PR #393): - Type-ahead is not lost during generation on Unix or Windows - PTY-harness hazards that present as swallowed input - Prefer an idle timeout to a total one when child is silent before first output From Leon (PRs #367/#385/#392/#395): - DeepSeek "repeats thinking" is not a backend bug, but greedy-decoding degeneration - Model-declared generation defaults are canonical; our constants are fallback, not override - CUDA driver API ships with display driver, not toolkit; inferred bad facts must be verified From Coordinator: - Standing Operational Rule: worktree lifecycle and decision merging — do not delete a worktree before Scribe has merged its decision inbox Before: 27,360 bytes | After: 34,662 bytes | Added: 7,302 bytes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
A hazard in merged code
PR #372's PTY harness calls
openpty(None, None)— a 0x0 window. ratatui'sinsert_before_no_scrolling_regionsinfinite-loops at zero rows, so the harness as merged is one type-ahead test away from hanging a CI job, and the next person to extend it would have had no idea why.The window is now a real 24x80, with the reason at the call site — a bare
24, 80reads as arbitrary and someone would eventually "simplify" it back. The CR-not-LF gotcha is cross-referenced there too: terminals send\r, and feeding\nmeans the line never completes, which presents as a hang that looks exactly like swallowed input.Both artifacts were found the expensive way, so they now live in the code a harness author will read, not only in a decision record.
Confirmed neutral rather than assumed: test 1 passes at both 0x0 and 24x80; test 2 behaves identically at both. Test 2's empty read under WSL is not a window-size effect — it is the harness's own 30s
drain_pty_mastertimeout against a ~48s debug model load over the slow/mnt/cmount. A byte-for-byte replica with a 90s window returns the expectedtok22tok22tok20\r\n. Native CI Linux loads fast enough that both pass.Context: three negatives on the type-ahead report
This branch began as an investigation into
runswallowing typed-ahead input. Input is not being lost. Each hypothesis was refuted with evidence rather than argument:cursor::position()routes user keystrokes toskipped_eventsand drains them back into the queue; ratatui 0.30's inline viewport never reads stdin outside init/resize. Verified by withholding the DSR reply 250ms and injecting/helpinto the open window — it survived.ReadConsoleInputWreads raw records unaffected byENABLE_LINE_INPUT;SetConsoleModedoes not flush the queue; there is noFlushConsoleInputBufferanywhere. InstrumentedGetConsoleMode(0x01f7) and pendingINPUT_RECORDcounts across prompt boundaries confirm it, including for a partial line with no Enter.qwen2.5-0.5bstreaming ~19s, with/helpinjected pasted, delayed, and character-by-character at human speed. Type-ahead survived every time.Raw bytes argue against the erase hypothesis:
/helpappears at the banner and at the next prompt — never mid-stream. ConPTY does not echo type-ahead during generation, so there is nothing for a repaint to overwrite.No speculative fix shipped. The remaining untested path is native-conhost immediate echo, which no automated harness on a headless box can produce — plausibly why it is visible in an ordinary session and not here. Fixing an unobserved mechanism would look like it worked and would not.