Skip to content

Commit 814db6d

Browse files
os-helpclaude
andauthored
fix(spec): stop emitting meta.json for a reference category with no pages (#7303) (#7622)
`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 Claude-Session: https://claude.ai/code/session_017Mqbd23qE51QfBjgJv3Zsp Co-authored-by: Claude <noreply@anthropic.com>
1 parent fe49d88 commit 814db6d

5 files changed

Lines changed: 59 additions & 14 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(spec): stop emitting a `meta.json` for a reference category that publishes no page (#7303)
6+
7+
`gen:docs` wrote `content/docs/references/<category>/meta.json` for every category
8+
it iterated, including one whose page list came out empty. The result was a
9+
directory holding a single `{ "title": …, "pages": [] }` and nothing else: no
10+
reference page, no `index.mdx` (§2.5 already skipped that), and no entry in the
11+
root `meta.json`, so the route could not resolve and the link checker never saw
12+
it. Its one measurable effect was on whoever enumerated the tree — human or
13+
agent — who counted one category more than the docs actually publish.
14+
15+
`contracts/` was the standing case. It holds TypeScript service interfaces
16+
rather than `.zod.ts` schemas, so `gen:schema` creates `json-schema/contracts/`
17+
and leaves it empty; `conversions`/`migrations` have no schema directory at all
18+
and are skipped outright, while `contracts` reached the emit with zero pages.
19+
The `meta.json` emit is now guarded on the page list being non-empty, mirroring
20+
the guard the category `index.mdx` emit already carries, so all three behave
21+
alike. `content/docs/references/` goes from 15 category directories to 14.
22+
23+
The guard is on the page count, not on that schema-directory asymmetry, so a
24+
future category in either shape lands the same way.
25+
26+
No published page changes: the emitted file count goes 231 → 230, and the one
27+
file that stops being written is the empty `meta.json`. The Contracts Protocol
28+
prose documentation is unaffected — it lives at `content/docs/kernel/contracts/`
29+
and is not generated from this tree. `contracts` also remains a declared
30+
category in `scripts/lib/category-title.ts`; `resolveCategoryTitles` is total
31+
over the directories in `packages/spec/src/`, so that declaration is mandatory
32+
while the module exists.

content/docs/getting-started/quick-reference.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,14 @@ Testing and quality assurance.
233233

234234
## Categories Without a Section
235235

236-
`content/docs/references/` holds two more category directories that deliberately get no
236+
`content/docs/references/` holds one more category directory that deliberately gets no
237237
section above. Curation happens at the category level as well as inside each table, and
238238
this is where it is stated. The same gate reads this table, so a new category directory —
239-
or a page landing in one of these — goes red until this page is updated.
239+
or a page landing in this one — goes red until this page is updated.
240240

241241
| Category directory | Pages | Why it has no section |
242242
|:---|---:|:---|
243243
| [`studio`](/docs/references/studio) | 3 | Designer-facing metadata (`flow-builder`, `object-designer`, `plugin`) — Studio's own authoring surfaces, not protocols an app declares. Reach them from the [reference index](/docs/references). |
244-
| `contracts` | 0 | Publishes no reference page at all; the directory holds only a `meta.json` left over from an earlier layout. There is nothing to link. |
245244

246245
---
247246

content/docs/references/contracts/meta.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

packages/spec/scripts/build-docs.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,9 +837,27 @@ PAGES_BY_CATEGORY.forEach((zodFileSchemas, category) => {
837837

838838
// Generate Category Meta. Group into fumadocs `---Section---` separators when the
839839
// category has a SECTION_GROUPS entry; otherwise a flat sorted list (see #1880).
840+
const pages = buildCategoryPages(category, Array.from(zodFileSchemas.keys()));
841+
842+
// A category that published no page gets no `meta.json` — and so no directory
843+
// at all — the same way §2.5 below skips the `index.mdx` of one. What this
844+
// guard removes is a folder holding a single `{ "pages": [] }`: no page, no
845+
// `index.mdx`, and no entry in the root `meta.json`, so it is unroutable, and
846+
// invisible to the link checker. Its one measurable effect was that anyone
847+
// enumerating the tree counted one category more than exists — which is how
848+
// #7303 came to be filed.
849+
//
850+
// `contracts/` is the case: it holds TypeScript service interfaces rather than
851+
// `.zod.ts` schemas, so `gen:schema` creates `json-schema/contracts/` and
852+
// leaves it empty; unlike `conversions`/`migrations` (no schema directory at
853+
// all, so `groupSchemasByPage` skips them outright) it reaches this loop with
854+
// zero pages. The guard is on the pages, not on that asymmetry, so any future
855+
// category in either shape lands the same way.
856+
if (pages.length === 0) return;
857+
840858
const meta = {
841859
title: CATEGORIES[category],
842-
pages: buildCategoryPages(category, Array.from(zodFileSchemas.keys()))
860+
pages
843861
};
844862
emit(path.join(categoryDir, 'meta.json'), JSON.stringify(meta, null, 2));
845863
});

scripts/check-quick-reference-counts.mjs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@
7777
*
7878
* ## Curation also happens one level up
7979
*
80-
* `references/studio/` (3 pages) and `references/contracts/` (0 pages) have no
81-
* section on the page at all. Left implicit, that is the same unwatched drift
82-
* one level up, so the page carries a `## Categories Without a Section` table
83-
* and this gate reads it: every category directory under
84-
* `content/docs/references/` must either be a section's derived category or be
85-
* declared there with its real page count — never both, never neither.
80+
* `references/studio/` (3 pages) has no section on the page at all. Left
81+
* implicit, that is the same unwatched drift one level up, so the page carries
82+
* a `## Categories Without a Section` table and this gate reads it: every
83+
* category directory under `content/docs/references/` must either be a
84+
* section's derived category or be declared there with its real page count —
85+
* never both, never neither.
8686
*
8787
* ## Absence must be loud
8888
*

0 commit comments

Comments
 (0)