Summary
test/e2e/homepage.spec.ts → Homepage hero: scroll-driven parallax (variant C) › [inplace] ?hero-scene=N forces the scene → door dest + navbar highlight match is flaky on the dev project. The final assertion expect(page.locator('[class*="navbarSceneActive"]')).toHaveCount(1) intermittently sees 0 (or momentarily 2) lit navbar items.
Evidence (probe)
Run in isolation with --retries=0 on clean master (before the pin-scroll PR #193):
- Run 1: FAIL · Run 2: pass · Run 3: FAIL · Run 4: FAIL → ~3/4 fail per attempt.
So it is pre-existing, not introduced by #193 (with #193's changes it fails ~1/3 — no worse). CI's retries: 2 (playwright.config.ts) absorbs it, which is why it hasn't blocked merges, but it's real test flake.
Root cause (likely)
The test navigates through 4 forced scenes ([0,2,5,6]) rapidly, each with waitForLoadState('networkidle'). useNavbarSceneHighlight (src/pages/index.tsx) adds/removes the navbarSceneActive class from an effect gated on an IntersectionObserver visibility flag. For the inplace model (no pinned spacer), the hero's on-screen visibility + the class-swap effect can race the assertion, leaving a transient window with 0 (during swap) lit items.
Concrete fix options
- Poll instead of asserting an instantaneous count:
await expect.poll(() => page.locator('[class*="navbarSceneActive"]').count()).toBe(1) (small change, absorbs the swap window).
- In
useNavbarSceneHighlight, apply the new highlight in the same effect tick it clears the old one (avoid a 0-lit frame) — a product-side hardening that also improves the real UX during fast scene changes.
Originating skill: maintain-homepage-hero (audit while building PR #193, 2026-07-06). No screenshot (timing/e2e, not a visual defect).
Summary
test/e2e/homepage.spec.ts→Homepage hero: scroll-driven parallax (variant C) › [inplace] ?hero-scene=N forces the scene → door dest + navbar highlight matchis flaky on thedevproject. The final assertionexpect(page.locator('[class*="navbarSceneActive"]')).toHaveCount(1)intermittently sees 0 (or momentarily 2) lit navbar items.Evidence (probe)
Run in isolation with
--retries=0on clean master (before the pin-scroll PR #193):So it is pre-existing, not introduced by #193 (with #193's changes it fails ~1/3 — no worse). CI's
retries: 2(playwright.config.ts) absorbs it, which is why it hasn't blocked merges, but it's real test flake.Root cause (likely)
The test navigates through 4 forced scenes (
[0,2,5,6]) rapidly, each withwaitForLoadState('networkidle').useNavbarSceneHighlight(src/pages/index.tsx) adds/removes thenavbarSceneActiveclass from an effect gated on anIntersectionObservervisibility flag. For the inplace model (no pinned spacer), the hero's on-screen visibility + the class-swap effect can race the assertion, leaving a transient window with 0 (during swap) lit items.Concrete fix options
await expect.poll(() => page.locator('[class*="navbarSceneActive"]').count()).toBe(1)(small change, absorbs the swap window).useNavbarSceneHighlight, apply the new highlight in the same effect tick it clears the old one (avoid a 0-lit frame) — a product-side hardening that also improves the real UX during fast scene changes.Originating skill:
maintain-homepage-hero(audit while building PR #193, 2026-07-06). No screenshot (timing/e2e, not a visual defect).