Skip to content

docs(spec): record the eager glob-discovery disposition beside filePatterns - #12575

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-12165-filepatterns-note
Aug 26, 2026
Merged

docs(spec): record the eager glob-discovery disposition beside filePatterns#12575
huangyiirene merged 1 commit into
mainfrom
claude/issue-12165-filepatterns-note

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#12165

Route (a) of the two the triage admitted (comment 5409942502). The 24-line note is a pure insertion into the JSDoc of the filePatterns field in packages/spec/src/kernel/metadata-plugin.zod.ts. No schema shape, no .describe() text, no other file.

Why a note rather than close-as-covered

The triage's actual question was whether a spec-level declaration should carry a consumer-side observation at all. Three measured things say yes here:

  1. The pointer only runs one way.finding: no objectstack.config.ts in this repo reaches MetadataPlugin._loadFromFileSystem — the registry filePatterns glob has no measured consumer, which qualifies the invisibility rationale #11071 rests on #12075's CLI-side note states in its own words that it was written on the CLI's read "rather than beside the registry declaration itself — that lives in packages/spec/src/kernel/metadata-plugin.zod.ts, which this package only reads." A reader who arrives at the CLI note gets the whole story; a reader who arrives at the declaration had nothing pointing at it. Closing as covered leaves the half the triage on finding: no objectstack.config.ts in this repo reaches MetadataPlugin._loadFromFileSystem — the registry filePatterns glob has no measured consumer, which qualifies the invisibility rationale #11071 rests on #12075 asked for permanently unwritten.
  2. The genre is already this file's.metadata-plugin.zod.ts records dispositions with measured evidence and issue references throughout — the DEFAULT_METADATA_TYPE_REGISTRY docblock (additionalTypes on MetadataPluginConfig is authorable, documented as THE way a plugin declares a metadata type, and has no reader — a plugin cannot declare a kind at all #8586), the field entry (meta-field-write-inert: an accepted field PUT never reaches the object — a runtime-created field is stored valid=true and is absent from fields forever #7893), the capability and api entries. packages/spec carries a no-business-logic rule, not a no-comments rule, and a comment changes no accept/reject behaviour.
  3. The declaration is the site that invites the over-read. The field's own second line says the patterns are "Used to discover metadata files on disk" — and discovery turns out to be the one use of this field nothing in the tree exercises.

What was re-measured, on origin/main at 09b4f4e

Measured here rather than copied from #12075:

  • MetadataPlugin._loadFromFileSystem (packages/metadata/src/plugin.ts:873, called at :395) is the only reader that globs these patterns, and _bootstrap reaches it on exactly one branch — eager (the default) andsrc unset. artifact-only and lazy never glob; eager with a source goes to _loadFromLocalFile.
  • Both non-test new MetadataPlugin(...) sites set artifactSource: { mode: 'local-file', … } unconditionally: packages/runtime/src/standalone-stack.ts:725 and packages/cli/src/commands/serve.ts:2388.
  • git ls-files '*objectstack.config.ts' → 9 tracked configs, all declaring metadata in code.

So the glob pass has no end-to-end dogfood, and the recorded disposition is that none is being minted for it — startup focus.

The second paragraph is the load-bearing one

Stating only "no measured consumer" a few hundred lines above the capability and api entries would have contradicted this file's own comments, which lean on codeOnlySourceHint reading filePatterns[0] back as the prescription in a 403 not_creatable refusal. The field's values have live readers that never glob — that one, plus the CLI deriving every scaffolded file name from them (#11071, pinned by packages/cli/test/generate-file-name-registry-parity.test.ts), plus metadata-manager.ts:2486 re-projecting them. Discovery is the unexercised use; name derivation and refusal prescriptions are not. The note says so explicitly, so the observation cannot be quoted later as "the field is inert".

The generated docs tree did not move

The note sits inside an object literal, and packages/spec/scripts/lib/file-description.ts publishes a block only when it is top-level (column 0), in the header zone, and documents no declaration — all three fail here, by two counts. Verified rather than reasoned: check:docs reports 229 generated files in sync. Nothing under content/docs/references/** changed, so the module-header renumbering that landed in #12551 (dd4fc6c) is not engaged. The prose sits before the @example tag, since a TSDoc block tag would otherwise absorb it.

Checks — all at the final commit 4c5d68e, tree clean

Exit codes captured before any pipe; verdicts quoted from each gate's own output.

checkverdict
pnpm --filter @objectstack/spec buildcommand-exit 0, 34/34 declarations emitted
pnpm --filter @objectstack/spec typecheckexit 0 (incl. check:scripts-typecheck, check:test-typecheck)
pnpm --filter @objectstack/spec check:docs✅ 229 generated files in sync with packages/spec
pnpm lint (full repo, eslint . --no-inline-config)command-exit 0 — not narrowed
check:authorable-surface · check:liveness · check:empty-state · check:strictness-ledger · check:variant-docsexit 0
check:doc-authoring · check:overlay-whitelist-table · check:spec-parsed-alias · check:doc-anchorsexit 0
check-comment-mask-adoption · check-nul-bytes · docs-audit/check-affected-docsexit 0

The gate list was re-derived from the actual change set with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (it reads the merge-base diff itself), then run at head after the commit.

⚠️One gate is NOT MEASURED, not green:pnpm check:i18n exits 1 with PREREQUISITE NOT MET — the workspace CLI is not built ("Nothing was checked: no bundle was compared and no config was parsed"). It needs a built @objectstack/cli, which a comment-only spec diff cannot move a bundle through; CI runs it with the CLI built. Recorded as unmeasured rather than passed.

No changeset: comment-only, releases nothing — skip-changeset.

Draft, per dispatch: no auto-merge, PM reviews and lands.

Generated by Claude Code


Generated by Claude Code

…tterns
`filePatterns` declares a discovery contract whose glob pass nothing in this
tree reaches: `MetadataPlugin._loadFromFileSystem` is entered only on an
`eager` bootstrap with no `options.artifactSource`, and both non-test
`new MetadataPlugin(...)` sites set one unconditionally. The recorded
disposition — no end-to-end dogfood is minted for that path — had been
written only on the CLI's own read of the field, because the dev that
measured it held `packages/spec` read-only. This is the half that was
deliberately left unwritten: a reader of the declaration itself had no
pointer to the measurement.
The note also fences the misreading the bare fact invites. The field's
VALUES have live readers that never glob — the CLI derives every scaffolded
file name from them, and `codeOnlySourceHint` reads `filePatterns[0]` back
as the prescription in a 403 `not_creatable` refusal, which the `capability`
and `api` entries of `DEFAULT_METADATA_TYPE_REGISTRY` already lean on.
"Discovery is unexercised" is not "the field is inert", and stating only the
first a few lines above those entries would have set up the second.
Comment-only: no schema shape, no `.describe()` text, and the note sits
inside the object literal, which `build-docs.ts` selection rules exclude
from the module description — `check:docs` confirms all 229 generated files
stay in sync.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 9bed0b0fe6fe3606fb23eba595bd897e5f320e8cpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 7077a9ee3cbd793f5d444fca191d33ad74877511 — the merge of head 4c5d68ee378e3a7c69e3993b414ff5554cb6fb7d into base 9bed0b0fe6fe3606fb23eba595bd897e5f320e8c, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 7077a9ee3cbd793f5d444fca191d33ad74877511 && git checkout 7077a9ee3cbd793f5d444fca191d33ad74877511
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9bed0b0fe6fe3606fb23eba595bd897e5f320e8c 4c5d68ee378e3a7c69e3993b414ff5554cb6fb7d && git checkout -B drift-repro 9bed0b0fe6fe3606fb23eba595bd897e5f320e8c && git merge --no-ff 4c5d68ee378e3a7c69e3993b414ff5554cb6fb7d
node scripts/docs-audit/affected-docs.mjs --json 9bed0b0fe6fe3606fb23eba595bd897e5f320e8c

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@huangyiireneClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM review — ACCEPT (session_01E5LFCYBJ3q2s6yW6oMLxwy, domain:spec seat R7, contract review performed at the review tier as the compensating control for an opus-built PR).

Contract verdict: Clause-② re-checked against the real diff — no on both limbs, and the comment-only surface the claim promised is verified: a pure JSDoc insertion inside the filePatterns field docblock (before the @example, so no TSDoc block tag absorbs it), no schema shape, no .describe() text, no registry values. check:docs reporting the 229 generated files in sync proves the docs pipeline is not engaged — verified, not assumed, which matters one merge after #12551's renumbering landed.

Route (a) over close-as-covered — the dev's three grounds hold on my read: the existing pointer ran one way only (the CLI-side note explicitly says the declaration site carries nothing); recording dispositions with issue refs is already this file's genre (#8586, #7893 in the same registry); and the field's own "Used to discover metadata files on disk" line is precisely what invites the over-read. The correction the note carries — "no measured consumer" is true only of the GLOB path, while the values have live non-globbing readers (#11071's scaffold parity pin, codeOnlySourceHint's 403 prescription) — makes the fencing paragraph load-bearing: without it the note would contradict the capability/api entry comments a few hundred lines below.

Facts re-measured on origin/main09b4f4e rather than copied from the card; disclosures (check:i18n honestly NOT MEASURED; the label-fallback correction that the local refusal was the harness classifier, not an HTTP 403 — a platform-fact refinement worth keeping; the PR-number/issue-read labels asymmetry) — noted.

Landing: waiting for every check green on 4c5d68e, then ready-flip + auto-merge, queue-entry verified by ls-remote.


Generated by Claude Code

@huangyiirene
huangyiirene marked this pull request as ready for review August 26, 2026 12:00
@huangyiirene
huangyiirene added this pull request to the merge queueAug 26, 2026
Merged via the queue into main with commit b307bfdAug 26, 2026
38 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-12165-filepatterns-note branch August 26, 2026 12:18
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@huangyiirene@claude