web/src/screens/LibrariesScreen.test.tsx → "stops scan polling and refreshes sources once when scans complete" was CI-load-flaky. It fired the poll tick via runPollTick but discarded the promise loadScanStatuses returns, so the progress-clear state update and the fire-and-forget loadSources() refetch it triggers landed on real microtasks afteract() had already resolved. The test out-waited that race with two waitFor(…, { timeout: 5000 }) calls, which were marginal on a starved CI VM (timed out on PR #509 / run 918); prior timeout bumps (#447) were diminishing whack-a-mole.
Fix
Drive the chain to completion deterministically instead of out-waiting it. runPollTick now, inside act(async () => …):
await Promise.all(intervalHandlers.map(h => h())) — settles the scan-status poll promise the handler returns (and its progress-clear setState);
await flushMicrotasks() (a single setTimeout(0) macrotask) — drains the entire microtask queue, including the fire-and-forget loadSources() refetch, which by spec has fully settled before any macrotask runs.
The mock fetches resolve synchronously on microtasks and setInterval is the only mocked timer (setTimeout stays real), so one macrotask turn deterministically completes both chains with no wall-clock delay. Both original assertions are preserved verbatim — progress label 75% gone, and exactly one extra /api/v1/media-sources fetch — and the two waitFor calls + the 20s test-level budget are dropped.
Verification
LibrariesScreen suite green 15/15 consecutive runs (npm test -- LibrariesScreen --run in a loop).
npm run lint clean; npm run build succeeds (only the pre-existing unrelated chunk-size advisory).
No production code changed — LibrariesScreen.tsx / libraries.ts untouched; the component was observable deterministically from the harness alone.
Done-when
The scan-complete test no longer depends on waitFor wall-clock timeouts to observe the poll chain
Test passes reliably (verified with a repeated-run loop locally — 15/15)
adversarial review passed
Pure web-test change — no server-state effect (independent cross-model review skipped per process.independent-review-rubric; covered by orchestrator cold-read of the diff + the 15/15 determinism loop).
Fixes #512.
## What
`web/src/screens/LibrariesScreen.test.tsx` → *"stops scan polling and refreshes sources once when scans complete"* was CI-load-flaky. It fired the poll tick via `runPollTick` but **discarded the promise `loadScanStatuses` returns**, so the progress-clear state update and the fire-and-forget `loadSources()` refetch it triggers landed on real microtasks **after** `act()` had already resolved. The test out-waited that race with two `waitFor(…, { timeout: 5000 })` calls, which were marginal on a starved CI VM (timed out on PR #509 / run 918); prior timeout bumps (#447) were diminishing whack-a-mole.
## Fix
Drive the chain to completion **deterministically** instead of out-waiting it. `runPollTick` now, inside `act(async () => …)`:
1. `await Promise.all(intervalHandlers.map(h => h()))` — settles the scan-status poll promise the handler returns (and its progress-clear `setState`);
2. `await flushMicrotasks()` (a single `setTimeout(0)` macrotask) — drains the entire microtask queue, including the fire-and-forget `loadSources()` refetch, which by spec has fully settled before any macrotask runs.
The mock fetches resolve synchronously on microtasks and `setInterval` is the only mocked timer (`setTimeout` stays real), so one macrotask turn deterministically completes both chains with no wall-clock delay. Both original assertions are **preserved verbatim** — progress label `75%` gone, and **exactly one** extra `/api/v1/media-sources` fetch — and the two `waitFor` calls + the 20s test-level budget are dropped.
## Verification
- `LibrariesScreen` suite green **15/15 consecutive runs** (`npm test -- LibrariesScreen --run` in a loop).
- `npm run lint` clean; `npm run build` succeeds (only the pre-existing unrelated chunk-size advisory).
- **No production code changed** — `LibrariesScreen.tsx` / `libraries.ts` untouched; the component was observable deterministically from the harness alone.
## Done-when
- [x] The scan-complete test no longer depends on `waitFor` wall-clock timeouts to observe the poll chain
- [x] Test passes reliably (verified with a repeated-run loop locally — 15/15)
- [ ] adversarial review passed
Pure web-test change — no server-state effect (independent cross-model review skipped per `process.independent-review-rubric`; covered by orchestrator cold-read of the diff + the 15/15 determinism loop).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The "stops scan polling and refreshes sources once when scans complete" test
fired the poll tick via runPollTick but discarded the promise loadScanStatuses
returns, so the progress-clear state update and the fire-and-forget
loadSources() refetch it triggers landed on real microtasks AFTER act() had
resolved. The test out-waited that race with two waitFor({ timeout: 5000 })
calls, which were marginal on a starved CI VM (timed out on PR #509/run 918);
prior timeout bumps (#447) were diminishing whack-a-mole.
Drive the chain to completion deterministically instead of out-waiting it:
runPollTick now awaits the promise the handler returns (settling the
progress-clear) and then yields to a single macrotask (setTimeout 0) to drain
the microtask queue — including the fire-and-forget loadSources() refetch —
all INSIDE act(async () => ...). The mock fetches resolve synchronously on
microtasks and setInterval is the only mocked timer, so one macrotask turn
completes both chains with no wall-clock delay. The two waitFor calls are
replaced by synchronous assertions (progress label gone; exactly one extra
/api/v1/media-sources fetch), and the 20s test-level budget is dropped.
Verified: LibrariesScreen suite green 15/15 consecutive runs; npm run lint and
npm run build clean. No production code changed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes#512
Adversarial review (orchestrator cold-read of the diff + determinism loop). Pure web-test leaf, no server-state effect → cross-model independent review skipped per process.independent-review-rubric, stated here. Checks: both original assertions preserved verbatim (progress 75% gone; exactly before + 1/media-sources fetch) — not weakened; runPollTick awaits the handler promises then drains one setTimeout(0) macrotask inside act(), which by spec settles the whole microtask queue (incl. the fire-and-forget loadSources() refetch) before the macrotask fires — race-free with no wall-clock wait; setInterval is the only mocked timer so setTimeout(0) stays real; no production code touched. 15/15 consecutive loop runs green; lint + build clean.
Adversarial review (orchestrator cold-read of the diff + determinism loop). Pure web-test leaf, no server-state effect → cross-model independent review skipped per `process.independent-review-rubric`, stated here. Checks: both original assertions preserved verbatim (progress `75%` gone; exactly `before + 1` `/media-sources` fetch) — not weakened; `runPollTick` awaits the handler promises then drains one `setTimeout(0)` macrotask inside `act()`, which by spec settles the whole microtask queue (incl. the fire-and-forget `loadSources()` refetch) before the macrotask fires — race-free with no wall-clock wait; `setInterval` is the only mocked timer so `setTimeout(0)` stays real; no production code touched. 15/15 consecutive loop runs green; lint + build clean.
Review-verdict: MERGEABLE @ b5596f55
⚠️ CI reds on this PR are infra, not code — the runner host hit No space left on device during the build step. Tracked in server-management#654. On the last run, Functional E2E + all PR-Gates went green here on b5596f5; only the disk-starved EF-migration/Build jobs fast-failed. Merge is pending a clean rerun once the runner-host capacity issue is resolved — no code change needed.
⚠️ CI reds on this PR are **infra, not code** — the runner host hit `No space left on device` during the build step. Tracked in **server-management#654**. On the last run, Functional E2E + all PR-Gates went green here on `b5596f5`; only the disk-starved EF-migration/Build jobs fast-failed. Merge is pending a clean rerun once the runner-host capacity issue is resolved — no code change needed.
timothy
merged commit d49d078264 into main2026-07-22 18:02:25 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #512.
What
web/src/screens/LibrariesScreen.test.tsx→ "stops scan polling and refreshes sources once when scans complete" was CI-load-flaky. It fired the poll tick viarunPollTickbut discarded the promiseloadScanStatusesreturns, so the progress-clear state update and the fire-and-forgetloadSources()refetch it triggers landed on real microtasks afteract()had already resolved. The test out-waited that race with twowaitFor(…, { timeout: 5000 })calls, which were marginal on a starved CI VM (timed out on PR #509 / run 918); prior timeout bumps (#447) were diminishing whack-a-mole.Fix
Drive the chain to completion deterministically instead of out-waiting it.
runPollTicknow, insideact(async () => …):await Promise.all(intervalHandlers.map(h => h()))— settles the scan-status poll promise the handler returns (and its progress-clearsetState);await flushMicrotasks()(a singlesetTimeout(0)macrotask) — drains the entire microtask queue, including the fire-and-forgetloadSources()refetch, which by spec has fully settled before any macrotask runs.The mock fetches resolve synchronously on microtasks and
setIntervalis the only mocked timer (setTimeoutstays real), so one macrotask turn deterministically completes both chains with no wall-clock delay. Both original assertions are preserved verbatim — progress label75%gone, and exactly one extra/api/v1/media-sourcesfetch — and the twowaitForcalls + the 20s test-level budget are dropped.Verification
LibrariesScreensuite green 15/15 consecutive runs (npm test -- LibrariesScreen --runin a loop).npm run lintclean;npm run buildsucceeds (only the pre-existing unrelated chunk-size advisory).LibrariesScreen.tsx/libraries.tsuntouched; the component was observable deterministically from the harness alone.Done-when
waitForwall-clock timeouts to observe the poll chainPure web-test change — no server-state effect (independent cross-model review skipped per
process.independent-review-rubric; covered by orchestrator cold-read of the diff + the 15/15 determinism loop).🤖 Generated with Claude Code
The "stops scan polling and refreshes sources once when scans complete" test fired the poll tick via runPollTick but discarded the promise loadScanStatuses returns, so the progress-clear state update and the fire-and-forget loadSources() refetch it triggers landed on real microtasks AFTER act() had resolved. The test out-waited that race with two waitFor({ timeout: 5000 }) calls, which were marginal on a starved CI VM (timed out on PR #509/run 918); prior timeout bumps (#447) were diminishing whack-a-mole. Drive the chain to completion deterministically instead of out-waiting it: runPollTick now awaits the promise the handler returns (settling the progress-clear) and then yields to a single macrotask (setTimeout 0) to drain the microtask queue — including the fire-and-forget loadSources() refetch — all INSIDE act(async () => ...). The mock fetches resolve synchronously on microtasks and setInterval is the only mocked timer, so one macrotask turn completes both chains with no wall-clock delay. The two waitFor calls are replaced by synchronous assertions (progress label gone; exactly one extra /api/v1/media-sources fetch), and the 20s test-level budget is dropped. Verified: LibrariesScreen suite green 15/15 consecutive runs; npm run lint and npm run build clean. No production code changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Fixes #512Adversarial review (orchestrator cold-read of the diff + determinism loop). Pure web-test leaf, no server-state effect → cross-model independent review skipped per
process.independent-review-rubric, stated here. Checks: both original assertions preserved verbatim (progress75%gone; exactlybefore + 1/media-sourcesfetch) — not weakened;runPollTickawaits the handler promises then drains onesetTimeout(0)macrotask insideact(), which by spec settles the whole microtask queue (incl. the fire-and-forgetloadSources()refetch) before the macrotask fires — race-free with no wall-clock wait;setIntervalis the only mocked timer sosetTimeout(0)stays real; no production code touched. 15/15 consecutive loop runs green; lint + build clean.Review-verdict: MERGEABLE @
b5596f55⚠️ CI reds on this PR are infra, not code — the runner host hit
No space left on deviceduring the build step. Tracked in server-management#654. On the last run, Functional E2E + all PR-Gates went green here onb5596f5; only the disk-starved EF-migration/Build jobs fast-failed. Merge is pending a clean rerun once the runner-host capacity issue is resolved — no code change needed.