fix(docs): resync content manifest with meta.json and guard the IA against drift#316
Merged
Merged
Conversation
…ainst drift `content.manifest.ts` is the source of truth for the docs sidebar, but pages were added straight into the per-folder `meta.json` files for months, so the manifest silently rotted. `gen:docs` is manual (not wired into CI), so the drift never fired — but running it would have SILENTLY DELETED real, committed pages (the whole `recipes/` section, `guides/testing/`, the root `description`, and six individual pages) from the sidebars. This makes the generator reproduce the committed `meta.json` byte-for-byte again, then locks the door so it can't rot: - Re-sync the manifest: add the `recipes` section (15 pages), the `guides/testing` section, the root `description`, and the dropped pages (`agents/adopt-in-your- project`, `guides/authoring/hooks`, `guides/data/url-shaping`, `guides/ resilience/accept-status`, `reference/surfaces`, `reference/conformance-kits`). Descriptions are the real frontmatter sentences, not placeholders. - Add `HAND_MAINTAINED_SECTIONS` (single-sourced in the manifest, consumed by the generator and the test). `integrations` joins it: its 24 pages are added per-PR as each `@stitchapi/*` package ships, so it curates its own `meta.json` and the generator skips it while keeping its sidebar slot. - Give the generator a Prettier-stable `meta.json` serializer (objects break, the `pages` array stays inline when it fits in 80 cols) so codegen output matches the committed, Prettier-governed files exactly — `gen:docs` is now a true no-op on a synced tree. Export the pure helpers and run the FS work only when the file is executed directly. - Write the two-way-sync test the manifest header always promised but never had (`apps/docs/test/content-manifest.spec.ts`, vitest): fails when any `.mdx` under `content/docs` is missing from the manifest or vice versa, with the hand-maintained exemption. Wire vitest into `apps/docs`. - Add a CI gate (mirrors the `gen:completions` pattern): re-run `gen:docs` and `git diff --exit-code apps/docs/content/docs`, so byte-level drift fails the build instead of waiting for someone to run the generator by hand. After this, `pnpm gen:docs` followed by `git diff apps/docs/content/docs` is empty. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
apps/docs/content.manifest.tsis the source of truth for the docs sidebar IA, but pages had been added straight into the per-foldermeta.jsonfiles for months, so the manifest silently rotted.gen:docsis manual (not wired into CI), so the drift never fired — but running it on a clean tree would have silently deleted real, committed pages from the sidebars:recipes/section (15.mdx+ itsmeta.json)guides/testing/sectionmeta.jsondescriptionagents/adopt-in-your-project,guides/authoring/hooks,guides/data/url-shaping,guides/resilience/accept-status,reference/surfaces,reference/conformance-kitsRoot cause: the manifest header claimed "a test will enforce" two-way sync, but no such test was ever written.
What
Re-sync the manifest so the generator reproduces the committed
meta.jsonfiles byte-for-byte:recipesandguides/testingsections, the rootdescription, and the six dropped pages. Descriptions are the real frontmatter sentences.HAND_MAINTAINED_SECTIONS(single-sourced in the manifest, consumed by both the generator and the test).integrationsis excluded — its 24 pages are added per-PR as each@stitchapi/*package ships, so it curates its ownmeta.json; the generator skips it but keeps its sidebar slot.meta.jsonserializer (objects break; thepagesarray stays inline when it fits in 80 cols, else one item per line) so codegen output matches the committed, Prettier-governed files exactly. Exported the pure helpers and gated the FS work behind a direct-run check.Lock the door so it can't rot again:
apps/docs/test/content-manifest.spec.ts(vitest) — the two-way-sync test the header always promised: fails when any.mdxundercontent/docsis absent from the manifest or vice versa, plus a formatting-independent "committedmeta.jsonmatches the manifest" check. Hand-maintained sections are exempted. Wired vitest intoapps/docs(testscript + devDep).verify.yml(mirrors the existinggen:completionspattern): re-runsgen:docsthengit diff --exit-code apps/docs/content/docs, so byte-level drift fails the build.AUTHORING.md.Verification
pnpm gen:docsthengit diff apps/docs/content/docs→ empty ✅pnpm --filter @stitchapi/docs test→ 7 passing; a stray.mdxmakes it fail with an actionable message, removing it restores green (negative-tested) ✅check:format,eslint,tsc --noEmit(fullcheck:types) all clean ✅🤖 Generated with Claude Code