Skip to content

metadata-fs: the FileSystemRepository chokidar watcher is inert in the production layout — its own ignored dotfile regex matches the .objectstack segment of the root path #7150

Description

@os-zhuang

Found while implementing #7000 (PR for the metadata half of #6743's dry-run property). Out of that card's scope — #7000 is about the repository creating its root at attach time — so this is filed separately and unassigned.

Duplicate search over open issues: chokidar ignored watcher, FileSystemRepository watcher metadata-fs, in:title watcher, ".objectstack/metadata" — 0 hits other than #7000 / #6743 themselves.

Observation

FileSystemRepository.startWatcher() passes ignored: [/(^|[\\/])\../] with the comment "skip dotfiles incl. .objectstack" (packages/metadata-fs/src/repository.ts). chokidar applies that matcher to the watched root path itself, not only to entries discovered underneath it. MetadataPlugin attaches the repository at REPO_SUBDIR = '.objectstack/metadata' under the project root (packages/metadata/src/plugin.ts:386-398), so the root path contains a dot segment and the whole watch is ignored.

Measured on chokidar 5.0.0, the version the package depends on, with the repository's own watch options (ignoreInitial: true, depth: 2, awaitWriteFinish, usePolling: true, interval: 1000). Two identical trees, differing only in whether the root sits under a dot-directory; in both, view/a.json is rewritten and view/b.json is added after ready:

A plain root          | root= metadata
   getWatched keys: [ '', 'metadata', 'metadata/view' ]
   events: [["add","view/b.json"],["change","view/a.json"]]
B root under dot-dir  | root= .objectstack/metadata
   getWatched keys: []
   events: []

Case B is the production layout.

Why it may matter

The watcher is not decoration. handleFsChange translates external edits into MetadataEvents with source: 'fs', and MetadataManager.setRepository() subscribes to repo.watch({}) and re-emits every event through notifyWatchers, invalidating the registry entry and the list() cache for the affected type (packages/metadata/src/metadata-manager.ts:2560-2640). So there is a live consumer wired to a source that, in the layout the plugin actually uses, can never fire. Hand edits, a git checkout that brings metadata JSON in, or any out-of-process writer under .objectstack/metadata/ are therefore invisible until the next start().

Whether a user hits this today depends on whether anyone edits files under .objectstack/metadata/ out of process, which I did not measure — I am recording both readings rather than grading it myself:

  • read as observation-class: a shipped mechanism that has never been exercised in the production layout, with no traced user-visible failure;
  • read as a concrete defect: a capability that is wired end to end and silently does nothing, which is exactly the shape that survives review because everything looks connected.

Left unlabeled for the triage round to grade.

Not caused by #7000's fix

Pre-existing on origin/main, and unchanged by that PR in both directions: before it, start() created the root and armed a watcher that was then ignored; after it, ensureRoot() arms a watcher that is still ignored. The #7000 PR's watcher-arming pin deliberately uses a root that is not under a dot-directory, precisely so it measures the arming rather than this.

Possible directions (not a ruling)

  • Scope the matcher to paths relative to the root instead of absolute paths (chokidar's matcher receives absolute paths, so this needs an explicit function matcher).
  • Or drop the regex and ignore only the repository's own .objectstack/ bookkeeping subtree, which is what the comment says the intent is — parseItemPath already rejects anything under .objectstack, so the matcher may be redundant with the parse guard.

Either shape is a contract-ish decision about what the watcher promises to see; it should be ruled, not guessed.

Related


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions