Evidence
CI-Go on e510828 (#269 merge): TestApplyKeysApproveSheet fails on BOTH ubuntu and macos (loop_state_test.go:313: free-text answer = "no" and :319: empty answer note = "nothing selected"), while the identical test passes 30+ consecutive runs locally on the exact same SHA (-count=30, shuffle, 5× full suite). Environment-dependent, not timing-flaky.
Mechanism (evidence-backed — supersedes the earlier DecodeKeys guess in the edit history)
Both observed failure lines require pickPausedTask(l.snap) to return "" at the test's LATER g presses (rows 3/4), i.e. snap.Status.Ask is gone by then:
:319 note = "nothing selected": g opens no overlay → the following \r falls to the detail path → no selectable item → note "nothing selected" (loopimpl.go:536) — instead of the overlay's "approve: empty answer".
:313 answer = "no": row 3's free-text keystrokes are swallowed (no overlay), so lastApproveAnswer keeps row 2's "no".
So an earlier successful approval leaves the loop's snapshot WITHOUT Status.Ask by the time the next g lands. The clear is asynchronous somewhere in the submit path (submitOverlayLocked → countingTransport.PostJSON /approve, and/or a follow-up status poll overwriting snap.Status with a response that carries no ask) — slow CI runners lose the race between that update and the test's next keypress; fast local runs win, hence 30+ local greens on the same SHA.
Ruled out: DecodeKeys chunk-splitting / pendingInput carry-over (decoder is mechanical; it cannot produce overlay==nil at the empty-answer row).
Where to fix
The Ask lifecycle belongs to the /approve submit path (PR #268's area, author mid-flight on loopimpl.go). Options: (a) make the post-approval snapshot update synchronous w.r.t. submitOverlayLocked, (b) have countingTransport's canned /status keep ask set, or (c) pin the test's expectations against the intended lifecycle (does an answered ask stay visible to g?).
Repro
Fails on CI runners since #268/#269; passes locally on darwin. Full log: run 34334461588. Related: #262 (CI load flakes), PR #270 (the deterministic main-reds, merged).
Evidence
CI-Go on e510828 (#269 merge):
TestApplyKeysApproveSheetfails on BOTH ubuntu and macos (loop_state_test.go:313: free-text answer = "no"and:319: empty answer note = "nothing selected"), while the identical test passes 30+ consecutive runs locally on the exact same SHA (-count=30, shuffle, 5× full suite). Environment-dependent, not timing-flaky.Mechanism (evidence-backed — supersedes the earlier DecodeKeys guess in the edit history)
Both observed failure lines require
pickPausedTask(l.snap)to return "" at the test's LATERgpresses (rows 3/4), i.e.snap.Status.Askis gone by then::319note = "nothing selected":gopens no overlay → the following\rfalls to the detail path → no selectable item → note "nothing selected" (loopimpl.go:536) — instead of the overlay's "approve: empty answer".:313answer = "no": row 3's free-text keystrokes are swallowed (no overlay), solastApproveAnswerkeeps row 2's "no".So an earlier successful approval leaves the loop's snapshot WITHOUT
Status.Askby the time the nextglands. The clear is asynchronous somewhere in the submit path (submitOverlayLocked → countingTransport.PostJSON /approve, and/or a follow-up status poll overwritingsnap.Statuswith a response that carries noask) — slow CI runners lose the race between that update and the test's next keypress; fast local runs win, hence 30+ local greens on the same SHA.Ruled out: DecodeKeys chunk-splitting /
pendingInputcarry-over (decoder is mechanical; it cannot produce overlay==nil at the empty-answer row).Where to fix
The Ask lifecycle belongs to the /approve submit path (PR #268's area, author mid-flight on loopimpl.go). Options: (a) make the post-approval snapshot update synchronous w.r.t. submitOverlayLocked, (b) have countingTransport's canned /status keep
askset, or (c) pin the test's expectations against the intended lifecycle (does an answered ask stay visible tog?).Repro
Fails on CI runners since #268/#269; passes locally on darwin. Full log: run 34334461588. Related: #262 (CI load flakes), PR #270 (the deterministic main-reds, merged).