Skip to content

fix(jobs): wait for a fresh pane's prompt before typing a command - #39

Merged
tomfordweb merged 1 commit into
mainfrom
fix/job-launch-prompt-race
Jul 29, 2026
Merged

fix(jobs): wait for a fresh pane's prompt before typing a command#39
tomfordweb merged 1 commit into
mainfrom
fix/job-launch-prompt-race

Conversation

@tomfordweb

Copy link
Copy Markdown
Owner

Fixes the flaky job-log integration test (sidemux-pbg) at its real cause — a launch race that also affects production.

Root cause

Launching a job into a pane created moments earlier races the shell's first prompt. The pty buffers the typed command and the kernel's canonical echo lands in the raw output stream before the prompt; when the shell finally prints it, the prompt glues itself to the job's first output line:

(set -o pipefail) ... seq 1 200; printf ...
sh-5.3$ 1        <-- late prompt + first output line, fused
2
3

This pollutes reads and per-job logs, and the prompt lands inside the job's output slice because baselineLines is captured before it prints. Under CPU load the integration fixture lost this race in 2 of 3 full-suite runs (expected [ …(203) ] to include 1).

Fix

JobManager.launch() now waits for the pane to stop being completely blank — cursor at origin, zero history — before typing, polling every 25ms with a 2s cap (a shell that prints no prompt still proceeds). cursor_x is added to PANE_STATE_FORMAT so a one-line prompt on row 0 is detectable.

Verification

  • Reproduced the flake deterministically first: a 30-iteration stress test under 4-core busy load failed on iteration 1 with the exact glued-prompt raw bytes.
  • Same stress run after the fix: 30/30 green.
  • New unit test: launch polls pane state until the first prompt appears, then types exactly once.
  • Full local gate green: typecheck, lint, test, build.

Closes sidemux-pbg

Launching into a pane created moments earlier raced the shell's first
prompt. The pty buffers the typed command, so the kernel's canonical
echo lands in the output stream BEFORE the prompt; when the shell
finally prints it, the prompt glues itself to the job's first output
line ("sh-5.3$ 1"). That polluted reads and per-job log files, and
the prompt landed inside the job's output slice because baselineLines
was captured before it printed.

This was the root cause of the flaky job-log integration test
(sidemux-pbg): under CPU load the fixture's fresh pane lost the race
2 of 3 full-suite runs, and readJobLog's first output element became
'sh-5.3$ 1' instead of '1'. Reproduced deterministically with a
30-iteration stress run under load; zero failures after this fix.

launch() now polls pane state until the pane stops being completely
blank (cursor at origin, no history) before typing, capped at 2s for
shells that print no prompt at all. cursor_x is added to
PANE_STATE_FORMAT so 'prompt printed' is detectable on row 0.

Closes sidemux-pbg
@tomfordweb
tomfordweb merged commit ad1b5d1 into main Jul 29, 2026
1 check 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