Repository navigation
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
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 —parseItemPathalready 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
- cli/metadata:
os migrate planstill creates.objectstack/metadata/on a fresh project (the residual half of #6743's dry-run write side effect) #7000 / its PR — the attach-time root creation this was found under - cli/driver-sql:
os migrate plan自称 dry-run,却仍会在全新项目上创建空数据库文件(#6469 的残余写副作用) #6743 — the dry-run property both halves serve
Generated by Claude Code
Activity
os-zhuang commentedon Aug 10, 2026
Triage:
pm:queue+domain:metadata.MetadataManager.setRepository()subscribing torepo.watch({})and re-emitting throughnotifyWatchers) is live code whose source can never fire.packages/metadata-fs/src/repository.ts— verified onorigin/main@3e8e669:ignored: [/(^|[\\/])\../]with the "skip dotfiles incl. .objectstack" comment is live atrepository.ts:398, andREPO_SUBDIR = '.objectstack/metadata'atpackages/metadata/src/plugin.ts:57(joined at:389) puts the watched root under a dot segment.packages/metadata*⇒domain:metadata.parseItemPathalready rejects.objectstackpaths gives the implementer the measurement to pick between them. Record the choice and its rationale in the PR.chokidar watcher) — only this card; cli/metadata:os migrate planstill creates.objectstack/metadata/on a fresh project (the residual half of #6743's dry-run write side effect) #7000 / cli/driver-sql:os migrate plan自称 dry-run,却仍会在全新项目上创建空数据库文件(#6469 的残余写副作用) #6743 are the origin cards and explicitly exclude this scope.target:v17: the mechanism has never worked in this layout, so no shipped behaviour regresses; whether any user edits.objectstack/metadata/out of process today is unmeasured (the filer says so). Class ① unproven ⇒ default not blocking. Re-grade if a user-visible stale-metadata report ties back to this.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-help commentedon Aug 10, 2026
Claim: PM loop round 2 (spec-surface seat, cross-lane under maintainer directive)
Session:
session_016R9de1FqP7NvwKvqXi92GhBranch:
claude/issue-7150-watcher-dot-rootWorktree:
objectstack-issue-7150Domain:
domain:metadataFile surface:
packages/metadata-fs/src/repository.ts+ its tests,.changeset/(stop on breach; explain in the report).packages/metadata/src/plugin.tsis read-only context (REPO_SUBDIR), not expected to change.Container & model: M card,
mode:subagent,model: opusSerial constraints cleared: origin card #7000's PR explicitly excludes this scope (its watcher-arming pin deliberately avoids a dot-rooted path); no open PR touches
metadata-fsper triage's premise check @3e8e669(2026-08-10T01:22Z). Nothing else in flight inpackages/metadata*.Instruction provenance (cross-lane dispatch): maintainer, this session's chat, 2026-08-10 ~01:5xZ — 「所有队列卡片都派发,v17 优先派发」(v17 pair dispatched first this round; this is the general-queue continuation).
Generated by Claude Code
os-help commentedon Aug 10, 2026
ACCEPT — PR #7208 (review of record, session
session_016R9de1FqP7NvwKvqXi92Gh).What shipped: the watcher's
ignoredmatcher becomes a function evaluated against the path relative to the watch root — the root's own dot segments are never judged, dotfiles under the root (including.objectstack/bookkeeping) stay ignored. 4 files, +295/−2, changeset patch, the consumer-side test file being the end-to-end assertion the dispatch asked for (test-only, disclosed in the scope note — in scope).Verified against GitHub: draft targeting
main, first lineFixes #7150; CI converged atc47cc79e0— 26 checks, zero failures; ESLintsuccess(02:51:47Z) and TypeScript Type Checksuccess(02:59:34Z) personally confirmed.Review credits:
parseItemPathmakes the matcher redundant") was falsified: the guard rejects the single literal.objectstack, so.cache/x.jsonparses as type.cacheandview/.scratch.jsonas an item named.scratch, and Shape B would put the repo's own log in the poll set. The follow-up defence-in-depth gap is filed as metadata-fs:parseItemPath's dot guard rejects only the literal.objectstack, not dot segments generally — narrower than its callers read #7216 (observation-class) rather than folded in.toEqual([])stays green on an inert watcher, which is why it carries a liveness control — and on revert it was the control that failed, as predicted.distafter reverting onlysrc— the [skill] 新 worktree 里第一次验证之前必须先 build 依赖闭包 —— AGENTS.md §9 的陈旧产物陷阱当日连咬三个 dev(假红 + 假绿两个方向) #6371 trap, self-caught, rebuilt, re-measured red. This is the stale-artefact discipline working at the dev, not the reviewer..swpis a useless discriminator here (chokidar'satomicfilters.*.sw[px]before any matcher runs).Also recorded: the GitHub body sanitizer ate a bare angle-bracket token in the PR body's first revision — the dev caught it on read-back and re-spelled it. Consistent with the seat's G-notes.
Next: ready → merge queue.
Generated by Claude Code
test(metadata-fs,metadata): widen the watcher event deadlines for ful…
fix(metadata-fs): scope the watcher's dotfile ignore to paths relativ…