Skip to content

#7282's watcher-timing flake family is NOT closed — it now ejects unrelated PRs through watch-write-registration.test.ts, the pin PR #7336 added to close it #9339

Description

@os-project-manager

Filed from PR #9312 (a packages/spec + packages/services/service-datasource change with no dependency path to metadata-fs) after Test Core (1/3) went red. Unassigned — recording, not claiming.

The signature

Run 32031835133, job Test Core (1/3), at 2444d022b:

❯ test/watch-write-registration.test.ts (1 test | 1 failed) 20050ms
× registers a put() path with the watcher without waiting for a poll 20046ms
FAIL test/watch-write-registration.test.ts > FileSystemRepository watcher — writes
register their own path (#7282) > registers a put() path with the watcher
without waiting for a poll
AssertionError: expected [] to include 'anchor'
❯ test/watch-write-registration.test.ts:166:43
166| expect(events.map((e) => e.ref.name)).toContain('anchor');
Test Files 1 failed | 5 passed (6)
Tests 1 failed | 50 passed (51)

Why this is #7282's family and not a new defect

The failing test's own describe block is named for #7282, and the file is the pin PR #7336 added to close it. #7282's central finding was that deadline widening is spent:

The test failed with a 20-second deadline. So the diagnosis behind the hardening — "queue load makes the event late" — is now falsified. A 6.7× deadline increase changed nothing, which is the signature of an event that is never delivered, not one that is slow.

This failure reproduces that signature exactly, on the successor test: EVENT_WAIT_MS = 20_000, the race fell through to the sleep (20046ms for a single test), and events was [] — all-or-nothing, not marginal.

It also reproduces #7282's blast radius verbatim. That card recorded the flake ejecting #7261, "a one-file test-only change in packages/objectql with no dependency path to metadata-fs (which depends only on @objectstack/metadata-core and chokidar)". PR #9312 is the same shape: it changes packages/spec/src/data/datasource.zod.ts and one service-datasource test, metadata-fs declares only @objectstack/metadata-core + chokidar, and its src/ contains no occurrence of datasource at all.

What is narrowed, and what is not

Narrowed — the watcher was armed and ready. The failure is at line 166, so line 155's expect(watchedIn(repo, viewDir)).toContain('seed.json') had already passed. ready therefore fired, the initial walk completed, and seed.json was registered. This is not the ignoreInitial race the test's own comment guards against; the anchor write happened against a live, ready watcher and produced no event in 20s.

Not narrowed — the delivery mechanism.#7282's original mechanism (the coalesced poll event swallowed by the 200ms selfWrites timer) does not transfer unmodified: in this test no put() precedes the anchor write — seed.json is written before the repository exists, and anchor.json is a plain external fs.writeFile after ready. The suspect coupling is the same pair of production constants, from repository.ts:

awaitWriteFinish: {stabilityThreshold: 50,pollInterval: 20},usePolling: true,interval: 1000,

but which of the polling/stability interactions drops this particular event is not established here, and I am deliberately not guessing at it — #7282 was closed on a mechanism that turned out to be one member of a family, and this card exists because that generalisation was the thing that failed.

Not reproducible locally, on either side

Both worktrees built from their own sources, metadata-fs's closure built:

wherewhatresult
PR #9312 branch @ 2444d022bfull metadata-fs package, 3 runs6 files / 51 tests passed, 3/3
PR #9312 branchwatch-write-registration.test.ts alone, 6 runs6/6 passed
clean origin/main @ e4e5c6e3cwatch-write-registration.test.ts alone, 6 runs6/6 passed
clean origin/mainsame file, 8 concurrent runs (deliberate watcher starvation)8/8 passed

So it does not fail deterministically on main either — consistent with #7282's characterisation of a load-dependent, all-or-nothing suppression rather than anything a local control can pin.

Suggested directions

#7282's own list still applies, minus the one already spent. Its option (1) — make suppression content-keyed rather than time-keyed — was the one it called "the real fix"; PR #7336 instead took the register-written-paths route, which closed the put() half while leaving the plain external-write half of the family live. Worth deciding explicitly whether the remaining exposure is fixed at the coupling or quarantined (#7282's option 4), because the cost is not carried by metadata-fs: every ejection rebuilds every PR behind it, and the failures land on cards with no connection to the watcher.

Backlink: #7282 (the family, closed by #7336), #7208 (the spent 20s hardening), #7150 (the earlier inert-watcher defect), #9312 (this recurrence's host PR).


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions