What happened
CI failed on PR #4090, a change whose diff touches only packages/ui/src/chat-turn.tsx and packages/ui/src/styles.css. The failing job was the core test job, in the [cli] workspace:
✖ same-server credential retirement stays inside the shared config transaction (40.337964ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(condition())
at waitFor (file:///.../packages/cli/dist/__tests__/tui-mcp-control.test.js:710:12)
at async TestContext.<anonymous> (file:///.../packages/cli/dist/__tests__/tui-mcp-control.test.js:471:5)
main was green at the time, and the same test passed in #4062's own merge run. @maka/cli does not depend on @maka/ui; the cli suite ran because the affected-surface planner validates the merged delta, which included #4062's files.
Root cause
tui-mcp-control.test.ts (added by #4062) defines a local waitFor:
async function waitFor(condition: () => boolean): Promise<void> {
for (let attempt = 0; attempt < 1_000 && !condition(); attempt += 1) {
await new Promise<void>((resolve) => setImmediate(resolve));
}
assert.ok(condition());
}
The budget is an iteration count, not a wall-clock duration. 1000 setImmediate spins burn out in tens of milliseconds — the reported test duration was 40.3 ms — while the predicate being awaited (leftOrder.includes('forget:docs')) needs a real filesystem config transaction in a mkdtemp directory to complete. On a loaded runner running three workspaces in parallel, the spin budget expires before the I/O lands, and the failure surfaces as an opaque assert.ok(condition()) with no description of what was being waited on.
This is the mirror image of #2221: that issue fixed a fixed 250 ms wall-clock deadline that flaked under CI load, and #2304 established the repo's settled pattern in tui-terminal-mock.ts — WAIT_BUDGET_MS = 250 ms local / 5 000 ms CI, overridable via MAKA_TEST_WAIT_BUDGET_MS, with a failure message that names what was being awaited. #4062's new file reinvented a local helper that repeats the same class of mistake in the opposite direction: where a fixed short wall-clock budget is load-sensitive, an iteration budget is even more so.
The helper has 28 call sites in this one file; any of them can lose the same race.
Why it matters
Same cost as #2221: the failure is indistinguishable from a real regression at the point of reading CI. An unrelated ui-only PR showed a red test check, and telling the two apart required pulling the job log and re-running. Every PR whose merged delta includes packages/cli pays this until the helper is fixed.
Suggested direction
Not prescribing a fix, but the established seam already exists: reuse the #2304 pattern — import the shared time-budgeted waitFor from tui-terminal-mock.ts (or extract it into a shared test-wait module if the harness dependency reads wrong), so every TUI wait shares one budget, one env override, and descriptive expiry messages. An iteration-count budget should not come back: it measures event-loop enthusiasm, not elapsed time.
Repro
Not deterministically reproducible by design — it is a load-dependent race. Observed on the core test job in run 33177032826; main was green at the same commit range.
Filed with AI assistance (Maka): the agent traced the failing run, the planner output, and both waitFor implementations; the human contributor reviewed this report before filing.
What happened
CI failed on PR #4090, a change whose diff touches only
packages/ui/src/chat-turn.tsxandpackages/ui/src/styles.css. The failing job was the coretestjob, in the[cli]workspace:mainwas green at the time, and the same test passed in #4062's own merge run.@maka/clidoes not depend on@maka/ui; the cli suite ran because the affected-surface planner validates the merged delta, which included #4062's files.Root cause
tui-mcp-control.test.ts(added by #4062) defines a localwaitFor:The budget is an iteration count, not a wall-clock duration. 1000
setImmediatespins burn out in tens of milliseconds — the reported test duration was 40.3 ms — while the predicate being awaited (leftOrder.includes('forget:docs')) needs a real filesystem config transaction in amkdtempdirectory to complete. On a loaded runner running three workspaces in parallel, the spin budget expires before the I/O lands, and the failure surfaces as an opaqueassert.ok(condition())with no description of what was being waited on.This is the mirror image of #2221: that issue fixed a fixed 250 ms wall-clock deadline that flaked under CI load, and #2304 established the repo's settled pattern in
tui-terminal-mock.ts—WAIT_BUDGET_MS= 250 ms local / 5 000 ms CI, overridable viaMAKA_TEST_WAIT_BUDGET_MS, with a failure message that names what was being awaited. #4062's new file reinvented a local helper that repeats the same class of mistake in the opposite direction: where a fixed short wall-clock budget is load-sensitive, an iteration budget is even more so.The helper has 28 call sites in this one file; any of them can lose the same race.
Why it matters
Same cost as #2221: the failure is indistinguishable from a real regression at the point of reading CI. An unrelated ui-only PR showed a red
testcheck, and telling the two apart required pulling the job log and re-running. Every PR whose merged delta includespackages/clipays this until the helper is fixed.Suggested direction
Not prescribing a fix, but the established seam already exists: reuse the #2304 pattern — import the shared time-budgeted
waitForfromtui-terminal-mock.ts(or extract it into a shared test-wait module if the harness dependency reads wrong), so every TUI wait shares one budget, one env override, and descriptive expiry messages. An iteration-count budget should not come back: it measures event-loop enthusiasm, not elapsed time.Repro
Not deterministically reproducible by design — it is a load-dependent race. Observed on the core
testjob in run 33177032826;mainwas green at the same commit range.Filed with AI assistance (Maka): the agent traced the failing run, the planner output, and both waitFor implementations; the human contributor reviewed this report before filing.