What happens
RouteAnnouncer > announces again when two paths share a title fails intermittently, and only when the whole web suite runs.
Measured on feat/next-audit-time (a31b235 plus that branch), on an 8-core machine:
| how it was run |
failures |
pnpm --filter tabstop-web test (full suite) |
1 in 6 |
| the same, five further consecutive runs |
0 in 5 |
vitest run src/screens/components/RouteAnnouncer alone |
0 in 25 |
So it needs the parallel workers and the CPU contention that come with the full suite. Nothing reproduces it in isolation.
What is known and what is not
The test asserts on MutationObserver records rather than on final text — deliberately, and the comment above it explains why: two paths that share a title would otherwise be announced by silence, and the final text is identical whether or not the region was cleared in between.
expect(seen).toContain('');
expect(seen.at(-1)).toContain('example.com');
The assertion message was not captured. The run that failed was summarised to a FAIL line and the detail is gone, so it is unknown which of the two assertions broke — whether the clear was never observed, or the refill never landed inside the window. That is the first thing to record next time it happens.
One hypothesis, tested and rejected
The announcer subscribes to onDocumentTitleSet inside its own effect, while the screen fires documentTitleSet() from useDocumentTitle, deeper in the tree. React flushes passive effects child-first, so it looked like the screen's first signal after a navigation must arrive before the announcer has re-subscribed — leaving the announcement dependent on the screen re-rendering again, which for /pages/:id is driven by query timing.
That is wrong. A probe with a screen that renders exactly once — no state, no queries, nothing to trigger a second pass — is announced correctly. The first signal is received. So whatever the race is, it is not that.
Why this is worth fixing rather than retrying
The test guards a real defect that has been reintroduced before: setting the announcement to the string it already holds is a no-op to React, so the region never mutates and the second navigation is announced by silence. A flaky guard on that is one bad CI day away from being marked skip.
Suggested first steps
- Keep the full failure output in CI rather than the summary, so the failing assertion is known the next time it happens.
- Consider whether the test can assert the same guarantee without racing a real
MutationObserver across two navigations — for instance by observing the announcer's state transitions directly, or by driving the two navigations through a router of its own the way the neighbouring "names itself late" test already does to avoid exactly this kind of interference.
- If it stays unreproducible,
vitest --sequence.shuffle or a pinned worker count may say whether it is contention or cross-file interference.
Acceptance criteria
What happens
RouteAnnouncer > announces again when two paths share a titlefails intermittently, and only when the whole web suite runs.Measured on
feat/next-audit-time(a31b235 plus that branch), on an 8-core machine:pnpm --filter tabstop-web test(full suite)vitest run src/screens/components/RouteAnnounceraloneSo it needs the parallel workers and the CPU contention that come with the full suite. Nothing reproduces it in isolation.
What is known and what is not
The test asserts on MutationObserver records rather than on final text — deliberately, and the comment above it explains why: two paths that share a title would otherwise be announced by silence, and the final text is identical whether or not the region was cleared in between.
The assertion message was not captured. The run that failed was summarised to a
FAILline and the detail is gone, so it is unknown which of the two assertions broke — whether the clear was never observed, or the refill never landed inside the window. That is the first thing to record next time it happens.One hypothesis, tested and rejected
The announcer subscribes to
onDocumentTitleSetinside its own effect, while the screen firesdocumentTitleSet()fromuseDocumentTitle, deeper in the tree. React flushes passive effects child-first, so it looked like the screen's first signal after a navigation must arrive before the announcer has re-subscribed — leaving the announcement dependent on the screen re-rendering again, which for/pages/:idis driven by query timing.That is wrong. A probe with a screen that renders exactly once — no state, no queries, nothing to trigger a second pass — is announced correctly. The first signal is received. So whatever the race is, it is not that.
Why this is worth fixing rather than retrying
The test guards a real defect that has been reintroduced before: setting the announcement to the string it already holds is a no-op to React, so the region never mutates and the second navigation is announced by silence. A flaky guard on that is one bad CI day away from being marked
skip.Suggested first steps
MutationObserveracross two navigations — for instance by observing the announcer's state transitions directly, or by driving the two navigations through a router of its own the way the neighbouring "names itself late" test already does to avoid exactly this kind of interference.vitest --sequence.shuffleor a pinned worker count may say whether it is contention or cross-file interference.Acceptance criteria