Skip to content

fix: stop three CI flakes from treating clocks as readiness - #861

Merged
tt-a1i merged 1 commit into
mainfrom
cursor/ci-flake-root-causes-32e2
Oct 16, 2026
Merged

tt-a1i merged 1 commit into
mainfrom
cursor/ci-flake-root-causes-32e2

Conversation

@tt-a1i

@tt-a1i tt-a1i commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Required CI on PR #857 failed three times in a row, each time on a different test that the PR does not touch, while recent main runs were green. The same class of failure is already tracked in #829.

  1. native extension graphs cycle management strips through the composed editor — Script execution timed out after the workflow tool reported failure in 1s. Runtime Node (Windows parallel 2/2), run 38848560632, job 116804466501. The script was export const meta = { name: "navigation workflow" }; return 1;.
  2. tests/web/composer-context-navigation.spec.ts — Vitest Test timed out in 5000ms on the first test. UI ubuntu-latest Node 22.19.0, run 38850034958, job 116809000734. The same run passed on Node 24, Node 26, and Windows.
  3. tests/web/session-sidebar.spec.ts — during a project drag, toggleWorkspace was called once with ["/project-1"]. UI windows-latest Node 22.19.0, run 38850838416, job 116811465931. The same file's 43 tests passed on Ubuntu. This is the empty pin-drop flake already noted on Flaky CI tests: session-history E2E anchor, Windows git-info refresh, Windows setup-config lock timeout #829.

The same Windows Node job as (1) also failed a reused PID does not impersonate the dead lock owner (setup-config.test.ts). That case stays on #829; this PR does not change it.

Value

These three checks are required for merge. Each failure asks for a rerun of an unrelated PR. The protections stay: a non-yielding workflow script is still killed at 1s, a drag's trailing click still does not toggle a workspace, and a mismatched session path still does not open file reference or replace the draft.

Approach

The three failures share a cause class — a fixed clock used as a readiness signal — but not one implementation. A shared wait helper or a shard-concurrency change would not fix them. No retries, skips, or widened deadlines.

Workflow sandbox (product bug). SANDBOX_SYNC_TIMEOUT_MS (1s) is the contractual deadline for non-yielding script (while (true) {}). The parent armed that watchdog before child.send(init), so process startup was charged against it. On a busy Windows runner, spawning node --permission exceeded 1s and a trivial script was reported as Script execution timed out. The child now registers its IPC handler and sends { kind: "listening" } first. The parent arms the 1s watchdog only then, and only then sends init. Exit and spawn errors still finish the run. A child that never listens and never exits waits until the caller aborts, which matches the existing rule that a workflow has no wall-clock deadline. Tests pass startDelayMs on that one call (not a process-global hook, because node:test can run tests in a file concurrently). The 1s constant is unchanged.

Sidebar trailing click (product bug). Suppression was Date.now() + 250 after drop, and Number.POSITIVE_INFINITY during the drag. The contract is: the drag's trailing click (detail !== 0, no new pointerdown) must not toggle; a keyboard click (detail === 0) must; a later pointerdown starts a new gesture and that click must toggle. Under load the post-drop render plus the test's waitFor exceeded 250ms, so the synthetic trailing click was no longer inside the window. Suppression is now a flag set by the drag, consumed by that one click, or cleared by a pointerdown while no drag is active. The 250ms window is removed, not widened. The regression waits 300ms after drop before the detail: 1 click, so the old window fails deterministically.

Composer source picking (unstable test). The first test awaited findByRole for a file-reference menuitem that the flushed update() render already produces. CI spent about 5.5s on that test and died on Vitest's 5s timeout at the it() line, not on Testing Library's ~1s asyncUtilTimeout (TestingLibraryElementError). Later tests in the same file were only about 2× local. A cross-file vi.useFakeTimers() leak did not reproduce with Vitest isolate or --no-isolate. The assertion is now getByRole after update(). A missing menu fails immediately. testTimeout is unchanged.

Validation

Before the product fixes, on this machine:

  • Sandbox: a 1500ms busy-wait before the child IPC listener made return 1 reject with Script execution timed out at 1005ms (Timeout.<anonymous> in sandbox.ts).
  • Sidebar: a 300ms delay after drop, then fireEvent.click with detail: 1, failed with toggleWorkspace called once with ["/project-1"] (same assertion as job 116811465931).

After the fixes:

  • node --test --experimental-strip-types tests/extensions/workflows/sandbox.test.ts — 36 passed. return 1 with startDelayMs: 1500 resolved in 1561ms. while (true) {} with the same delay and an 8s test abort rejected /Script execution timed out/ in 2549ms (startup plus the 1s deadline, not Workflow was aborted). The existing undelayed while (true) {} tests still failed the script at about 1030ms.
  • Vitest on session-sidebar.spec.ts and composer-context-navigation.spec.ts — 57 passed. The pin-drop test, including the 300ms gap, passed in 628ms.
  • bun run check passed (config, docs, discipline, web build, format, lint, typecheck).
  • bun run test — Node 2642 tests, 2633 passed, 0 failed, 9 skipped; UI 98 files, 1520 passed. This VM's global git config sets core.fsmonitor and core.untrackedCache. With those on, tests/web/session-worktree.test.ts ("forks source HEAD detached…") fails in isolation at the source-index byte compare, because git worktree add rewrites the index with FSMN/UNTR extensions. That assertion passes when those two settings are off, which is how the full run above was executed. The test is not part of this change. The composer 5s stall was not reproduced locally (findByRole returned in about 21ms here).

CI on 20a7b98 completed with no failed checks. Required workflow run 38861005953: every job succeeded, including the three jobs that failed on #857 (Windows parallel 2/2, UI ubuntu Node 22.19.0, UI windows Node 22.19.0). Not merged.

Other flakes, not changed here

From #829 and main reruns since the #831 flake fix (10 completed, non-cancelled main CI runs through 38847154103):

In that 10-run window, 4 runs were not green on the first attempt (3 later succeeded after rerun, 1 stayed red: 38736671833, UI windows). Across the last 100 main CI runs, 76 were not cancelled; 7 of those went green only after a rerun (~9%), and 6 stayed red. Primary failures were Windows UI, Windows Node, and one Web E2E shard. The "Require all test jobs" / "Background terminals (Windows)" failures in those runs are gate steps that fail because a dependency failed. Final main success hides those reruns. This sample is small and mixed with real failures, so it is an estimate of the gate, not a rate to target.

A shared wait helper is not warranted: the clocks live in Node IPC, pointer events, and a React assertion. Lowering shard concurrency would not correct a watchdog that starts too early or a click window that is the wrong signal.

Impact

  • User-visible: after a sidebar drag, the trailing click does not toggle the workspace even if it arrives later than 250ms. A keyboard activation still toggles. A new pointerdown, then a click, toggles. Suppression is no longer a 250ms blackout of every later click.
  • Model-visible context/tools: none. startDelayMs is a test option on runWorkflowSandbox, not a model tool.
  • Runtime/lifecycle: workflow sandbox startup is outside the 1s synchronous-script deadline. Non-yielding script is still terminated at 1s. A child that never reaches listening and never exits is no longer misreported as a script timeout at 1s; cancellation and process exit still end the run.
  • Persisted config/data: none.
  • Compatibility or risk: committed web/dist/app.js matches the sidebar change. The 1s script deadline and the drag/keyboard/new-gesture click contract are covered by regression tests.
Open in Web Open in Cursor 

The workflow sandbox armed its one-second sync watchdog before the child
was listening, so a slow spawn reported a trivial script as timed out.
Sidebar drag-click suppression expired after 250ms, so a slow post-drop
render let the trailing click toggle the workspace. The composer
source-picking test polled for a menu the flushed render had already
produced and hit Vitest's suite timeout.

Refs #829

Co-authored-by: tt-a1i <tt-a1i@users.noreply.github.com>
@github-actions github-actions Bot added the area:workflows Workflow engine, capability, skills, or tests label Oct 16, 2026
@tt-a1i
tt-a1i marked this pull request as ready for review October 16, 2026 06:43
@tt-a1i
tt-a1i merged commit db114bb into main Oct 16, 2026
20 checks passed
cursor Bot pushed a commit that referenced this pull request Oct 16, 2026
Bring in the CI flake fixes from #861 (db114bb): workflow sandbox
watchdog, sidebar post-drag click, and composer source menu.

Co-authored-by: tt-a1i <tt-a1i@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:workflows Workflow engine, capability, skills, or tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants