Skip to content

fix(spec): stop emitting meta.json for a reference category with no pages (#7303) - #7622

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7303-contracts-category-emit-guard
Aug 11, 2026
Merged

fix(spec): stop emitting meta.json for a reference category with no pages (#7303)#7622
os-help merged 1 commit into
mainfrom
claude/issue-7303-contracts-category-emit-guard

Conversation

@os-help

Copy link
Copy Markdown
Collaborator

Fixes#7303

What

gen:docs wrote content/docs/references/<category>/meta.json for every category it iterated, including one whose page list came out empty. The result was a directory holding a single { "title": …, "pages": [] } and nothing else: no reference page, no index.mdx (§2.5 already skipped that), and no entry in the root meta.json — so the route could not resolve and the link checker never saw it. Its one measurable effect was on whoever enumerated the tree, human or agent, who counted one category more than the docs publish. Which is exactly how #7303 came to be filed.

The meta.json emit is now guarded on the page list being non-empty, mirroring the guard the category index.mdx emit already carries.

content/docs/references/: 15 category directories → 14. Emitted files: 231 → 230.

Why the delete happens at the generator

content/docs/references/** is routed merge=os-regen — the repo's own declaration that the tree is generated output. A hand delete reds the gate and the next regeneration undoes it, measured twice before this PR:

$ rm -rf content/docs/references/contracts && pnpm --filter @objectstack/spec check:docs
✗ content/docs/references/ is out of date with packages/spec:
+ content/docs/references/contracts/meta.json (missing — spec adds it)

That is also this PR's positive control that the gate can see the file at all.

contracts is the standing case

It holds TypeScript service interfaces rather than .zod.ts schemas, so gen:schema creates packages/spec/json-schema/contracts/ and leaves it empty. conversions/migrations have no schema directory at all and groupSchemasByPage skips them outright; contracts fell through to the emit with zero pages. All three now emit nothing and produce no directory:

contracts references/: (absent)
conversions references/: (absent)
migrations references/: (absent)

The ⚠ Skipping clean of contracts/ … warning that every build printed is gone too.

The guard is on the page count, not on that schema-directory asymmetry, so a future category in either shape lands the same way.

One mechanical note worth a reviewer's eye

contracts/ is not manageDir'd — step 1 returns before claiming it, because it has no JSON Schema to regenerate from. So the generator cannot delete the file it no longer writes, and the tracked copy is dropped in this commit as the one-time reconciliation. The removal is durable because the guard exists: gen:docs was run four times after the guard landed and the directory never came back, and check:docs is green at 230 files.

category-title.ts — measured, not assumed

contracts: 'Contracts Protocol'stays. resolveCategoryTitles is total over the directories under packages/spec/src/, and packages/spec/src/contracts/ has 84 live source files. Removing the declaration was tested and is build-stopping:

$ pnpm --filter @objectstack/spec gen:docs # with the line deleted
✗ … Every reference page title, sidebar label and root-index row for a module is this string.
Exit status 1

conversions is the in-repo precedent: declared, emits nothing, and that is the supported steady state.

⚠️ Caveat carried forward from the issue, not resolved by this PR

contracts is a declared category with 84 live source files, and that empty meta.json was the only committed trace in the docs tree that the category exists. After this change the references tree silently stops representing it. That is the ruled outcome. The Contracts Protocol prose documentation is unaffected — it lives at content/docs/kernel/contracts/ (7 files) and is not generated from this tree.

Also changed

  • The now-stale contracts row in the quick-reference Categories Without a Section table, plus the sentence above it ("two more category directories" → "one more"). Reverse-controlled: leaving the row in place reds the gate with [coverage] `contracts` is declared as having no section, but content/docs/references/contracts/ does not exist.
  • The same category in scripts/check-quick-reference-counts.mjs's docblock prose, which would otherwise state something now false. The gate's synthetic GOOD_CATALOG/GOOD_PAGE self-test fixtures are untouched — they do not read the real tree and still exercise a reachable parse case.

Gates run (with invocation scope)

GateResult
pnpm --filter @objectstack/spec check:docs✅ 230 generated files in sync
pnpm --filter @objectstack/spec gen:docs ×4✅ directory never restored
pnpm --filter @objectstack/spec test✅ 376 files / 9867 tests
pnpm --filter @objectstack/spec typecheck✅ (incl. check:scripts-typecheck, check:test-typecheck)
pnpm run check:quick-reference-counts✅ self-test 22/22; 14 categories, all sectioned or declared
pnpm run check:doc-authoring✅ 375 files clean
pnpm run check:docs-audit-scope✅ 179 hand-written docs in sync
pnpm run check:empty-changeset
pnpm run check:nul-bytes✅ 7056 files
pnpm run check:adr-anchors / check:adr-links
npx eslint on the two touched sources

Changeset included (@objectstack/spec: patch).

Not verified

No heading or link fragment was touched, so no github-slugger check was needed. The docs site was not booted — this PR changes no published page. CI has not been consulted; that is the PM's.


Generated by Claude Code

…ages (#7303)
`gen:docs` wrote `content/docs/references/<category>/meta.json` for every
category it iterated, including one whose page list came out empty. The result
was a directory holding a single `{ "title": …, "pages": [] }` and nothing else:
no reference page, no `index.mdx` (§2.5 already skipped that), and no entry in
the root `meta.json`, so the route could not resolve and the link checker never
saw it. Its one measurable effect was on whoever enumerated the tree — human or
agent — who counted one category more than the docs publish. That is exactly
how #7303 came to be filed.
`contracts/` was the standing case: it holds TypeScript service interfaces
rather than `.zod.ts` schemas, so `gen:schema` creates `json-schema/contracts/`
and leaves it empty. `conversions`/`migrations` have no schema directory at all
and `groupSchemasByPage` skips them outright, while `contracts` fell through to
the emit with zero pages. The `meta.json` emit is now guarded on the page list
being non-empty, mirroring the guard the category `index.mdx` emit already
carries, so all three now emit nothing and produce no directory.
The guard is on the page count, not on that schema-directory asymmetry, so a
future category in either shape lands the same way.
The directory is generated output (`content/docs/references/**` is routed
`merge=os-regen`), so it is removed here as the generator's output rather than
by hand: a hand delete alone reds `check:docs` with `+ .../contracts/meta.json
(missing — spec adds it)` and the next `gen:docs` restores it byte-for-byte.
The emitted file count goes 231 → 230 and `content/docs/references/` goes from
15 category directories to 14. `contracts/` is not `manageDir`'d (it has no
JSON Schema to regenerate from, so step 1 returns before claiming it), which is
why the generator cannot delete the file it no longer writes and the tracked
copy is dropped in this commit.
`contracts` stays declared in `scripts/lib/category-title.ts`: measured, not
assumed — `resolveCategoryTitles` is total over the directories under
`packages/spec/src/`, and removing the declaration stops the build while the
module's 84 source files exist.
Also drops the now-stale `contracts` row from the quick-reference
Categories-Without-a-Section table (leaving it reds
`check:quick-reference-counts` with "`contracts` is declared as having no
section, but content/docs/references/contracts/ does not exist") and the same
category from that gate's docblock prose. The Contracts Protocol pages
themselves are untouched — they live at `content/docs/kernel/contracts/`.
Fixes#7303
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Mqbd23qE51QfBjgJv3Zsp
@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 11, 2026 9:13am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] content/docs/references/contracts/ 是一个只剩 meta.json 的空目录,build-docs.ts 已不再产出它

2 participants

@os-help@claude