Skip to content

fix(osm-demo): the CI e2e race that has hidden since #256 - #262

Open
cs-util wants to merge 1 commit into
r465from
r466
Open

fix(osm-demo): the CI e2e race that has hidden since #256#262
cs-util wants to merge 1 commit into
r465from
r466

Conversation

@cs-util

@cs-util cs-util commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes the e2e race that has made CI red on every PR since #256, and never once locally.

  • fix(osm-demo): wait for the widening after enabling the cell layer

Summary by CodeRabbit

  • Documentation

    • Updated test timing reports with the latest recording, refreshed duration history, provenance details, and current totals: 642 unit tests, 43 end-to-end tests, and 685 tests overall.
  • Bug Fixes

    • Improved map layer test synchronization so progressive cell expansion and republishing complete before subsequent actions proceed, increasing reliability of end-to-end validation.

The e2e 'the cells it reveals are interrogable' has failed in CI on every
PR since #256 and never once locally. enableCellLayer waited only for the
first cells to appear — ring 2's — and returned while rings 3 and 4 were
still republishing and re-rendering every path on the map. Switching that
layer on is a refetch, not a redraw, so the whole progressive cycle runs.

The click therefore landed on a path about to be replaced, no dispatch
happened, and the details panel stayed hidden with no error anywhere —
the explain reply is dropped when the selection no longer matches, and
that branch is silent by design.

This is the third appearance of the failure waitForRefresh was written
for, and its own comment names this exact case. The helper now waits for
the widening, as every other refetching path already does.

Not a product bug: the demo was never broken. The gate was.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Playwright fixture now waits for cell-layer refresh completion. The timing report records the 2026-08-06 run, including updated durations, test counts, summaries, and JSON history.

Changes

Cell-layer refresh and timing records

Layer / File(s) Summary
Complete cell-layer refresh before continuation
GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js
enableCellLayer waits for waitForRefresh after cells first appear.
Update test timing records
GpsPlusSlamJs_OsmDemo/docs/test-timings.md
The report records 642 unit tests, 43 E2E tests, 685 total tests, updated durations, recent summaries, and JSON history.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Poem

A rabbit waits while map rings grow,
Then checks the refresh is complete below.
New timing numbers hop in line,
Six hundred eighty-five tests shine.
The report now records each run.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the OSM demo CI E2E race fix and matches the pull request objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch r466

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claude Bot commented Aug 6, 2026

Copy link
Copy Markdown

Claude finished @cs-util's task in 3m 30s —— View job


Review — PR #262

  • Read .github/claude-review-guidelines.md
  • Read the PR diff (origin/r465...HEAD)
  • Trace the fix against the app's status/render ordering
  • Check every enableCellLayer call site for new blocking risk
  • Post review

Verdict

The fix is correct, and the mechanism holds up under checking:

  • writeStatus() runs in the same synchronous subscriber as drawMap() (src/main.ts:885-898), so the moment path.affordance-cell appears, #status already carries widening… for a non-final ring — there is no window where the new waitForRefresh could pass spuriously right after the cell wait.
  • fetchStarted is dispatched once per cycle, not per ring (src/refresh-cycle.ts:151), so between ring 2 and ring 4 the status never reverts to a loading message that lacks widening. That was the one hole that would have made this fix as racy as the thing it replaces; it isn't there.
  • The failing test's click (osm-demo.spec.js:3066) is downstream of a #show-below toggle, which is a pure redrawFromSnapshot — so once the widening is done before line 3021, nothing can detach the located path mid-click. The diagnosis in the commit matches the failure.
  • No call site is made to block on something that cannot finish: all eight callers run under a plain stubNetwork, the refused-tile test at :2421 already avoids the helper deliberately, and the error path is covered by waitForRefresh's Failed|unavailable branch.

One finding, posted inline: the helper's JSDoc (fixtures.js:614) still says "waitForRefresh is the wrong tool for this" while the body now calls it, and the new comment's "Switching this layer on is a REFETCH, not a redraw" is broader than the actual rule — layersNeedingData returns [] when cells are already held, so an off→on flick within one position (osm-demo.spec.js:3662/:3687) is a plain redraw. Behaviour is right in both cases; only the stated reasons are off. A suggestion block is attached.

Not commented on per the repo guidelines: the regenerated docs/test-timings.md.

I did not run the e2e suite — dependencies and Playwright browsers are not installed in this checkout, and this is exactly the failure that only reproduces on a loaded CI runner, so CI is the meaningful signal here.
· branch r466

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gps-plus-slam 518fd7d Commit Preview URL

Branch Preview URL
Aug 06 2026, 02:49 AM

@cs-util

cs-util commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment on lines +626 to +640
// AND THEN WAIT FOR THE WIDENING, for the reason `waitForRefresh` spells out
// above: the first cells to appear are ring 2's, and rings 3 and 4 each
// REPUBLISH a larger snapshot, re-rendering every path on the map.
//
// Switching this layer on is a REFETCH, not a redraw — cells are data-gated
// since round 10 stage B — so the whole progressive cycle runs, and a caller
// that only waited for the first cells was racing it. That is the third
// appearance of the failure `waitForRefresh` was written for: a cell clicked
// in ring 2 gets re-rendered before the click lands, the dispatch never
// happens, and the details panel silently stays hidden.
//
// It surfaced as a CI-only failure of "the cells it reveals are
// interrogable", deterministic on a slower runner and never reproducible
// locally.
await waitForRefresh(page);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mechanism checks out (writeStatus runs in the same synchronous snapshot subscriber that calls drawMap, and fetchStarted fires once per cycle rather than per ring, so widening… is continuously on screen from the moment the first cells paint until the final ring) — but two comment claims above this block no longer match the code:

  1. The JSDoc at line 614 now reads as a contradiction with the body: "waitForRefresh is the wrong tool for this: it waits for the ABSENCE of 'widening' … so it returns immediately and the test races anyway." It is the wrong tool on its own; after the cell wait it is exactly right, which is what this new block does. Worth a two-word amendment so the next reader doesn't "fix" the apparent contradiction by deleting the new call.

  2. "Switching this layer on is a REFETCH, not a redraw" is only true when the held snapshot has no cells. layersNeedingData(off, on, { cells: 931 }) returns [] (src/layers.ts via src/main.ts:983), so an off→on flick within one position — which osm-demo.spec.js:3662/:3687 does — is a plain redraw with no widening at all. The helper still behaves correctly there (both waits pass instantly), but the stated reason is broader than the rule.

Suggested change
// AND THEN WAIT FOR THE WIDENING, for the reason `waitForRefresh` spells out
// above: the first cells to appear are ring 2's, and rings 3 and 4 each
// REPUBLISH a larger snapshot, re-rendering every path on the map.
//
// Switching this layer on is a REFETCH, not a redraw — cells are data-gated
// since round 10 stage B — so the whole progressive cycle runs, and a caller
// that only waited for the first cells was racing it. That is the third
// appearance of the failure `waitForRefresh` was written for: a cell clicked
// in ring 2 gets re-rendered before the click lands, the dispatch never
// happens, and the details panel silently stays hidden.
//
// It surfaced as a CI-only failure of "the cells it reveals are
// interrogable", deterministic on a slower runner and never reproducible
// locally.
await waitForRefresh(page);
// AND THEN WAIT FOR THE WIDENING, for the reason `waitForRefresh` spells out
// above: the first cells to appear are ring 2's, and rings 3 and 4 each
// REPUBLISH a larger snapshot, re-rendering every path on the map.
//
// Switching this layer on is a REFETCH rather than a redraw WHENEVER THE HELD
// SNAPSHOT HAS NO CELLS — they are data-gated since round 10 stage B, and
// `layersNeedingData` refetches only in that case — so the whole progressive
// cycle runs and a caller that only waited for the first cells was racing it.
// (After an off/on flick within one position the cells are already held, no
// refetch happens, and both waits below pass immediately.) That is the third
// appearance of the failure `waitForRefresh` was written for: a cell clicked
// in ring 2 gets re-rendered before the click lands, the dispatch never
// happens, and the details panel silently stays hidden.
//
// It surfaced as a CI-only failure of "the cells it reveals are
// interrogable", deterministic on a slower runner and never reproducible
// locally.
//
// The paragraph above about `waitForRefresh` being the wrong tool is about
// using it ALONE: after the cell wait its widening check is exactly right.
await waitForRefresh(page);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js`:
- Line 640: Update the refresh flow around waitForRefresh(page) to bind
completion to the asynchronous `#layer-cells` update rather than the generic
visible status. Require a layer-specific loading/status transition before
validating width completion, or remove this duplicate wait if that transition is
already awaited elsewhere.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 923fb605-4e4e-4123-adf3-55c301c76681

📥 Commits

Reviewing files that changed from the base of the PR and between d4fac8e and 518fd7d.

📒 Files selected for processing (2)
  • GpsPlusSlamJs_OsmDemo/docs/test-timings.md
  • GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js

// It surfaced as a CI-only failure of "the cells it reveals are
// interrogable", deterministic on a slower runner and never reproducible
// locally.
await waitForRefresh(page);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify that enabling the layer produces a refresh-specific status transition
# before the first cell path appears, and that completion follows the last ring.
rg -n -C 25 \
  'waitForRefresh|enableCellLayer|layer-cells|writeStatus|widening|snapshot\.radius|affordance-cell' \
  GpsPlusSlamJs_OsmDemo

Repository: cs-util-com/location-based-webxr

Length of output: 50389


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Relevant test helper definitions"
sed -n '550,660p' GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js

echo
echo "## Application source files that may contain layer/status/ring code"
git ls-files GpsPlusSlamJs_OsmDemo | rg '\.(js|ts|mjs)$' | sed -n '1,120p'

echo
echo "## Precise searches in source files"
rg -n -C 8 -g '*.js' -g '*.ts' -g '*.mjs' \
  'layer-cells|`#layer-cells`|writeStatus|status\.innerHTML|affordance-cell|snapshot\.radius|widening|fetchWorkingSet|refresh\(' GpsPlusSlamJs_OsmDemo | sed -n '1,260p'

Repository: cs-util-com/location-based-webxr

Length of output: 35199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Read relevant fixtures helper and nearby callers"
sed -n '550,720p' GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js

echo
echo "## Narrow application search with file filters"
rg -n -C 6 -g '*.js' -g '*.ts' -g '*.mjs' \
  'layer-cells|`#layer-cells`|writeStatus|innerHTML|affordance-cell|snapshot\.radius|widening|fetchWorkingSet|refresh\(\)' GpsPlusSlamJs_OsmDemo | sed -n '1,320p'

Repository: cs-util-com/location-based-webxr

Length of output: 33508


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## waitForRefresh definition"
sed -n '220,280p' GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js

echo
echo "## refresh-cycle relevant implementation"
sed -n '1,260p' GpsPlusSlamJs_OsmDemo/src/refresh-cycle.ts

echo
echo "## status/statusReady/status text writers"
rg -n -C 6 -g '*.ts' -g '*.js' 'statusReady|writeStatus|snapshot\.radius|radius\s*:' GpsPlusSlamJs_OsmDemo/src | sed -n '1,260p'

Repository: cs-util-com/location-based-webxr

Length of output: 36412


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## main.ts status/loading reducers and snapshot subscriber"
sed -n '240,310p' GpsPlusSlamJs_OsmDemo/src/main.ts
rg -n -C 8 -g '*.ts' 'snapshotReady|writeStatus|writingPhase|radius|isFinalRing' GpsPlusSlamJs_OsmDemo/src/main.ts GpsPlusSlamJs_OsmDemo/src/osm-store.ts GpsPlusSlamJs_OsmDemo/src/main.ts

echo
echo "## main.ts writeStatus implementation"
sed -n '765,830p' GpsPlusSlamJs_OsmDemo/src/main.ts

echo
echo "## osm-store slice around loading/snapshotReady"
sed -n '1,220p' GpsPlusSlamJs_OsmDemo/src/osm-store.ts

echo
echo "## layer toggles implementation"
rg -n -C 8 -g '*.ts' 'layer-cells|cells:' GpsPlusSlamJs_OsmDemo/src | sed -n '1,260p'

Repository: cs-util-com/location-based-webxr

Length of output: 50388


Bound the refresh wait to #layer-cells.

waitForRefresh(page) only waits for a visible final status and no "widening" text. Since enabling #layer-cells is asynchronous, that count can reflect the initial refresh. The helper should require a cell-layer-specific loading/status change before waiting for the width completion, or skip this duplicate wait.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@GpsPlusSlamJs_OsmDemo/playwright-tests/fixtures.js` at line 640, Update the
refresh flow around waitForRefresh(page) to bind completion to the asynchronous
`#layer-cells` update rather than the generic visible status. Require a
layer-specific loading/status transition before validating width completion, or
remove this duplicate wait if that transition is already awaited elsewhere.

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