Skip to content

[Bugfix #1110] Fix flaky artifact-canvas D2 watch-failure test (missing waitFor)#1111

Merged
amrmelsayed merged 7 commits into
mainfrom
builder/bugfix-1110
Jun 28, 2026
Merged

[Bugfix #1110] Fix flaky artifact-canvas D2 watch-failure test (missing waitFor)#1111
amrmelsayed merged 7 commits into
mainfrom
builder/bugfix-1110

Conversation

@amrmelsayed

Copy link
Copy Markdown
Collaborator

Summary

Fixes a CI-load flake in the artifact-canvas test surfaces a synchronous FileAdapter.watch() failure via onError without throwing (D2).

Root Cause

The test awaited onError firing, then asserted the DOM synchronously on the next line:

await waitFor(() => expect(onError).toHaveBeenCalled());
expect(document.querySelector('p[data-line]')).not.toBeNull(); // bare read — race

ArtifactCanvas's async read() render can resolve after onError fires. Under CI load the render is delayed enough that the synchronous DOM query runs before the paragraph is in the tree, so the assertion intermittently fails. The sibling test directly below (Disposable.dispose is safe to call more than once) already uses the correct waitFor-wrapped pattern.

Fix

Wrap the second assertion in waitFor to match the sibling test's pattern:

await waitFor(() => expect(onError).toHaveBeenCalled());
await waitFor(() => expect(document.querySelector('p[data-line]')).not.toBeNull());

Semantics are unchanged (the assertion still must eventually hold); only the timing tolerance widens. One-line change, no production code touched.

Test Plan

  • pnpm --filter @cluesmith/codev-artifact-canvas test → 56/56 pass.
  • Stress-ran the D2 test in isolation 5× → 5/5 pass.
  • Full workspace pnpm build + pnpm test green (verified via porch checks: build ✓, tests ✓).

Regression Coverage

This is a test-timing fix to a race, so the corrected D2 test is the regression coverage: with the bare synchronous read it flakes under load; with the waitFor wrapper it is deterministic. An additional standalone test would only re-assert the same behavior.

Fixes #1110

The test 'surfaces a synchronous FileAdapter.watch() failure via onError
without throwing (D2)' asserted document.querySelector('p[data-line]')
synchronously right after awaiting onError. The async read() render can
resolve after onError fires; under CI load the DOM read landed before the
paragraph was in the tree, flaking the test.

Wrap the assertion in waitFor to match the sibling idempotent-dispose test's
pattern. Semantics unchanged; only timing tolerance widens.

Fixes #1110
…itFor

CMAP (claude) flagged the identical bare-assertion-after-waitFor race in the
sibling test 'surfaces adapter errors via onError and does not throw; failed
list keeps prior markers (D2)'. Same root cause: onError can fire before the
async read() render flushes. The issue invited fixing such patterns as a
ride-along since the file is small. One-line, semantics unchanged.
@amrmelsayed

Copy link
Copy Markdown
Collaborator Author

CMAP review (3-way)

Model Verdict
Gemini (agy) APPROVE
Codex APPROVE
Claude APPROVE

All three approved the one-line fix. Claude's review flagged the identical bare-assertion-after-waitFor race in the sibling test surfaces adapter errors via onError and does not throw; failed list keeps prior markers (D2) (line 133). The issue explicitly invited fixing such patterns as a ride-along ('the file is small'), so I applied the same one-line waitFor wrap there too (commit 846b53d). 56/56 tests pass.

@amrmelsayed

Copy link
Copy Markdown
Collaborator Author

Architect Review

Low-risk BUGFIX. Two-line diff in packages/artifact-canvas/src/components/__tests__/artifact-canvas.test.tsx, exactly as scoped:

  • Line 161 — the D2 watch-failure test originally reported. expect(document.querySelector(...)).not.toBeNull() is now wrapped in waitFor(() => ...).
  • Line 130 — the sibling D2 adapter-error test with the identical race. Same wrap. This is the ride-along the issue's Out-of-scope section explicitly invited ("could ride along if the fixer notices any") — picked up via the CMAP claude lane.

Three-way CMAP unanimous APPROVE (gemini + codex + claude). 56/56 tests pass. No production code changes; semantics preserved (the assertion still eventually succeeds, just with retry tolerance matching the sibling test's pattern).

APPROVE. Ready for the pr gate.


Architect review

@amrmelsayed amrmelsayed merged commit 0065189 into main Jun 28, 2026
6 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

1 participant