Skip to content

[finding] node-metadata-manager's watcher has the same one-shot delivery structure #9339 just repaired, with no reconciliation behind it #9701

Description

@os-sam

Found while implementing #9339 (PR #9656). Filed unassigned — recording, not claiming. ⛔ Not fixed in that PR: its declared file surface was packages/metadata-fs/src/repository.ts, and this is a different package with a different gate surface.

Open-issue search run before filing: no hits for node-metadata-manager + watcher.

The observation

packages/metadata/src/node-metadata-manager.ts:58 runs its own chokidar watcher over a project root:

usePolling: true, interval: 1000, ignoreInitial: true

That is the identical structure #9339 measured and repaired one package over — and it has no reconciliation behind it.

#9339's finding, which transfers verbatim: under usePolling, chokidar re-reads a directory only when its stat strictly advances. An external write advances the directory's mtime once, so the file gets exactly one delivery attempt; polls 2..N compare an unchanged stat and can never rediscover it. #9339 measured at least six independent one-shot gates sitting on that single attempt, spanning three layers (kernel timestamp, chokidar's readdir throttle and readdir snapshot, chokidar's emit gates), each producing a byte-identical observable: no event, ever, for that path.

⇒ An externally created metadata file whose single directory-poll attempt is lost is never announced, permanently, for the life of the process.

What is NOT claimed here

⚠️ No reproduction was attempted against this site. #9339's own bound applies and is worth restating: the six gates are forced fault injections, not the CI mechanism, which was never identified and may be a seventh. What transfers is the structure — one attempt, no second one — not a measured failure at this call site.

⚠️ This also does not claim the consequence is equally severe. metadata-fs had a heads index whose divergence from disk is a well-defined thing to detect; this class announces file events and has no such index, so what "lost" costs here is a different question that this note does not answer.

Why the bounded in-place exemption does not cover it

Two independent reasons, either sufficient:

  1. The repair is not mechanical here.#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's fix is a content-keyed reconciliation sweep comparing disk against heads. This class has no heads index to compare content against, so the fix shape differs — it is a design question, not a transcription.
  2. Different package, different gate surface.

Deliberately excluded, so the next reader does not re-derive it

packages/metadata/src/plugin.ts:498 also polls, but watches a single file path rather than a directory. It therefore gets a fresh attempt per edit and is materially less exposed. Judged, not left unexamined.

Related

#9339 (the repaired sibling, PR #9656) · #7282 (the family; its "the signature of an event that is never delivered, not one that is slow") · #7336 (the put() half, closed by routing around the fragile path) · #8845 (the gate blind spot — ⚠️ note check:durability-log-level's READ vocabulary is anchored to IDataDriver's find/findOne/count, so every filesystem-backed read seam in the repo is outside that rule by construction, not by scan scope; this site included).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions