Skip to content

[finding] MetadataPlugin.watch documents its default as false but implements true, attaching a project-root polling watcher for external consumers #9770

Description

@os-elon

Found while characterizing #9701. Filed unassigned — recording, not claiming. Not fixed there: that card's declared file surface was packages/metadata/src/node-metadata-manager.ts, and this is a different defect class (a documented-vs-actual default on a public export).

Open-issue search run before filing: no hits for the MetadataPlugin watch default, and no hits for startWatching reachability.

The contradiction

MetadataPluginOptions.watch documents its own default at packages/metadata/src/plugin.ts:144:

Default: false (post PR-10e — was previously true).

The constructor implements the opposite (plugin.ts:253-263):

this.options={watch: true, ...options};// ...consteffectiveWatch=bootstrapMode==='artifact-only' ? false : (this.options.watch??true);

Both spellings resolve to true: the object literal supplies watch: true when the caller omits the key, and the ?? true fallback catches an explicit undefined.

Measured on origin/main @ 2065e31, constructing the plugin with no options and reading the manager's private watcher field:

DEFAULT watcher defined? = true
DEFAULT options.watch = true
UNDEF watcher defined? = true

So the documented default and the implemented default disagree, and the implemented one attaches the watcher.

Why the drift went unnoticed

PR-10e appears to have been applied at the call sites rather than in the constructor. Both — and the only two — non-test construction sites pass watch: false explicitly:

  • packages/runtime/src/standalone-stack.ts:697 — "Source-file scanner OFF ... Scanning would also recursively watch the project root (incl. node_modules), which is expensive and prone to EMFILE."
  • packages/cli/src/commands/serve.ts:1806 — "Mirror the standalone stack's dev config exactly ... no source-file scanner (redundant + EMFILE-prone)."

So no in-repo configuration exercises the default, and the drift is invisible to every test and gate.

Why it still matters

MetadataPlugin is a public export (packages/metadata/src/index.ts:14, node.ts:11). External code constructing it without naming watch — the documented-safe thing to do — gets exactly the configuration both internal call sites go out of their way to avoid: a recursive polling chokidar watch over the whole project root (usePolling: true, interval: 1000, node_modules excluded only via the default ignored).

It also switches on the exposure characterized in #9701: with watch: true the eager boot path primes every filesystem item into the TTL-free registry, and a lost watcher delivery then makes an edited or deleted metadata file serve its boot-time content for the life of the process. That characterization concluded the exposure was acceptable because no shipped configuration reaches the watcher — this default is the one thing that makes it reachable for an external consumer.

Suggested resolution

Flip the constructor default to match the documentation (watch: false), since that is the intent PR-10e recorded and the posture both call sites already assert. The two internal call sites pass watch: false explicitly and are unaffected either way.

Alternatively, if the true default is deliberate, correct the doc comment — but note it would then contradict the EMFILE rationale both call sites cite.

Related

#9701 (the characterization that surfaced this) · #9339 / PR #9656 (the sibling one-shot delivery repair in packages/metadata-fs)

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions