Skip to content

Fix the rail-j/k App.test flake: wait for Home's key listener, not just the rail - #11

Merged
ben-vargas merged 1 commit into
mainfrom
fix/roving-keys-home-listener-flake
Oct 10, 2026
Merged

ben-vargas merged 1 commit into
mainfrom
fix/roving-keys-home-listener-flake

Conversation

@ben-vargas

Copy link
Copy Markdown
Owner

Fixes the intermittent viewer failure seen on run 37967628995 (attempt 1, viewer suite · node 18.17.1, PR #7 — which touches no viewer code):

FAIL src/app/App.test.tsx > two roving lists, one keyboard (§2.7 × §3.6) > moves only the rail on the rail's j/k — Home's cursor stays where it was
AssertionError: expected '#/run/r_2f91c4a8' to be '#/run/r_a03d51e7'

The race

mountShell() waits for the rail's rows. The test then sends j to Home, and nothing waited for Home to be ready:

  • Home's table is a separate poll from the rail's, so it can commit a beat later.
  • Even after it commits, Home answers keys through a window listener that React re-subscribes in a passive effect, which runs after the commit that painted the rows.

A j that lands in either gap runs against runs = [], so Home's cursor stays on row 0, and the final Enter opens RUNS[0] (r_2f91c4a8). That's CI's exact failure.

The fix

One test-only change. Before pressing j, the test waits for Home's listener itself:

await waitFor(() => expect(fireEvent.keyDown(window, { key: 'Home' })).toBe(false))

Home claims Home (calls preventDefault) only when it holds rows. No other window listener handles that key. Home also leaves the cursor on row 0, where it starts anyway. All of the test's assertions are unchanged. The table is virtualized in the shell, so Home's cursor can't be read from the DOM there, and waiting for Home's rows to render wouldn't close the second gap.

Verification

Node 18.17.1 Node 24
Old test, Home's listing held 30 ms behind the rail's 25/25 fail (CI's message when Home's rows land between j and Enter, #/ when they land after Enter) 25/25 fail
Fixed test, same 30 ms lag 25/25 pass 25/25 pass
Fixed test, unmodified, repeated 50/50 pass (also 50/50 under 20 CPU hogs) 50/50 pass
Full viewer suite (vitest run, minus the local-only e2e) + typecheck green —
  • Delaying React's passive effects (Scheduler host callbacks), with no fetch lag, also reproduces CI's exact message. That confirms the second gap is real on its own.
  • The test still guards the bug it was written for. I re-broke the original hijack: removed the rail's stopPropagation, and Home's defaultPrevented and ownsShortcut checks. Both the old and the fixed test fail with the same message.

No CHANGELOG entry, since this is a test-only fix (same as the two test fixes in #8).

…st the rail

App.test.tsx "moves only the rail on the rail's j/k — Home's cursor stays where it
was" failed intermittently on CI (node 18.17.1: expected '#/run/r_2f91c4a8' to be
'#/run/r_a03d51e7'). mountShell waits for the RAIL's rows, then the test sends `j` to
HOME, whose readiness nothing awaited:

- Home's table is a separate poll from the rail's, and can commit a beat later.
- Even once it has, Home answers keys through a window listener that React
  re-subscribes in a passive effect, after the commit that painted the rows.

A `j` that lands in either gap runs against `runs = []`, Home's cursor stays on row 0,
and the final Enter opens RUNS[0] (r_2f91c4a8) — CI's exact failure. The test now
waits for the listener itself before pressing `j`: Home claims (preventDefault) `Home`
only when it holds rows, and `Home` leaves the cursor on row 0, where it starts anyway.
Every assertion is unchanged.

Reproduced by holding Home's listing 30 ms behind the rail's: the old test failed
25/25 on Node 18.17.1 and 25/25 on 24 (CI's message when Home's rows land between `j`
and Enter, `#/` when they land after Enter); the fixed one passes 25/25 on both.
Deferring React's passive effects instead also reproduces CI's message. The fixed test
still fails, with the same message, when Home is made to answer the rail's keys again
(the regression it guards). Unmodified, it passes 50/50 on 18.17.1 and on 24, and
50/50 on 18.17.1 under CPU contention; full viewer suite green on 18.17.1.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@ben-vargas
ben-vargas merged commit 681069b into main Oct 10, 2026
8 checks 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