Repository navigation
docs(references): the generated security category index omits misc, the one page with no .zod.ts behind it #11260
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentation
on Aug 23, 2026 Triage: lands in
packages/spec/scripts/build-docs.ts(spec docs generator — tooling that orbits the spec contract) ⇒domain:spec, typeBug(the generated index breaks the invariant its own comment claims, and a routed page is unreachable from its category overview), →pm:queue.PM-suggested route (measure first, the card's shape is sound): card the pages
meta.jsondeclares, keep thewasEmittedguard — the stated invariant becomes true by construction and closes the class, not justmisc. In-card edge to verify: a category that is all-miscmust not emit an empty grid.Serial/fold note for the spec seat: #10834 is an open
findingon a different defect in this same generated index — before dispatching, answer fold-or-serial against it explicitly (five gates; same file, but only fold if it is the same defect shape with the same fix, which the card says it is not — expect serial with declared regions). Regenerated output must come from the generator run, never hand-edited.Size/model suggestion:S–M, opus for the generator-path change.
Generated by Claude Code
Claim: PM seat
session_01T9cDbY2NBiVJWYx3BpWfH2(/pm-dispatch, spec lane) — dispatching to an os-dev subagent.
Session: session_01T9cDbY2NBiVJWYx3BpWfH2
Branch:claude/issue-11260-security-index-misc
Worktree:../objectstack-11260(created by the dev agent, per-task)
Domain: domain:spec
File surface:packages/spec/scripts/build-docs.ts(card emitter path, ~line 806) + regeneratedcontent/docs/references/**/index.mdxoutput (generator-run only, never hand-edited).
Container & model: os-dev subagent, opus (S–M, generator-path judgment per triage).
Clause-②: no — docs generator + regenerated docs; no contract acceptance change, nopackages/spec/src/**diff expected (stop-and-report if the fix somehow requires it).
Fold-or-serial answer of record (vs #10834, same generated index): serial — #10834 is a different defect shape in the same file, currently unclaimed and not in any queue, so there is no live overlap; this card fixes only themisc-omission class (card the pagesmeta.jsondeclares, keepwasEmitted), and #10834 stays an independent finding. The dev must not fold it.
Serial constraints vs in-flight: none (#11255 in platform-objects; #11250/#11449/#11458 already in landing).
Generated by Claude Code
- added a commit that references this issue
on Aug 23, 2026 os-dev-report
{ "issue": 11260, "status": "done", "branch": "claude/issue-11260-security-index-misc", "pr": "https://github.com/objectstack-ai/objectstack/pull/11483", "premise_still_valid": true, "summary": "Premise re-verified at merge base b372318836 before editing: security/meta.json still declared 5 pages including misc, index.mdx still carded 4, and the emitter still iterated zodFiles. Fixed by construction rather than by special-casing misc: build-docs.ts 2.5 now iterates the page list 2 wrote into each category's meta.json (handed over in a new categoryMetaPages map, the same way categoryPageSchemas is handed to the root index) instead of re-deriving one from the .zod.ts files on disk, and KEEPS the wasEmitted guard - so a page that produced no reference file still cannot be carded into a dangling 404 (9 such slugs across 4 categories are still correctly excluded), while a declared page the run did not emit now STOPS the build instead of silently thinning the grid, which is exactly how misc was lost. The rule was extracted to packages/spec/scripts/lib/category-index.ts (the move #7658 and #4912 made for the same reason) so the all-misc edge could be asserted at all. All-misc edge answer: no such category exists in the tree (security is the only one declaring misc, and it does so alongside 4 zod-derived pages), but it was reachable in TWO broken shapes, both now closed - (a) a category with .zod.ts files whose every published schema falls to the catch-all would have rendered a literally EMPTY <Cards> grid, every zod slug filtered by wasEmitted and misc never considered; (b) a category with NO .zod.ts files but published schemas (security proves plain-.ts declarations reach the catch-all) hit the old 'if (zodFiles.size === 0) return' and got NO index.mdx at all while 2 still wrote its meta.json and pages. The guard is now keyed off the same declared list, so meta.json exists iff index.mdx exists, and an empty grid is unreachable from a non-empty declaration by assertion, not by argument. Docs came out of the generator run (gen:docs), never hand-edited. Diff touches no packages/spec/src/** - generator script, its new lib + test, the regenerated page, and a changeset.", "tests": "REVERSE VERIFICATION, both legs from committed source. BEFORE: `pnpm --filter @objectstack/spec check:docs` on the unmodified generator, exit 0, printing '229 generated files in sync with packages/spec' with the security grid at 4 cards - so the omission is live generator behaviour, not stale committed output. AFTER: `pnpm --filter @objectstack/spec gen:docs` exit 0, printing 'Generated 229 files'; `git diff --stat -- content/docs` = 'content/docs/references/security/index.mdx | 1 + / 1 file changed, 1 insertion(+)' - the whole 14-category post-regen sweep in one measurement, only security moved, every other grid byte-identical. The added line is '<Card href=\"/docs/references/security/misc\" title=\"Misc\" />' with NO description attribute, correct because misc has no source file to point at. Post-regen a probe over all 14 categories shows cards == declared pages everywhere (was 13/14). No ablation/mutation leg was needed or run: the change is in a tsx-executed generator script with no dist on the path under test, so there is no build-staleness trap to defeat here; the equivalent 'can it fail' proof is the unit test's `() => false` case, which drives the undelivered branch directly. BUILD/TYPES/TESTS: `pnpm --filter @objectstack/spec build` exit 0 (full dts, not OS_SKIP_DTS, since check:generated reads the built dist); `pnpm --filter @objectstack/spec typecheck` exit 0 = tsc --noEmit + check:scripts-typecheck + check:test-typecheck, covering both new files; full `pnpm --filter @objectstack/spec test` exit 0, 'Tests 11171 passed (11171)'; the new scripts/category-index.test.ts 7 passed. GATES: list derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (exit 0; 26 path-matched + the test-file convention family + the 5 changeset families; derived from merge base b37231883, not recalled). 37 distinct gate invocations green, including check:docs, check:generated (14/14 artifacts current, after the build), check-section-landing-index, check-doc-frontmatter, check:doc-anchors, check:doc-authoring, check:docs-audit-scope, check:docs-redirects, check:quick-reference-counts, check:role-word, check:published-readme-links, check:published-files, check:merge-driver, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check:cross-package-test-inputs, check-ci-filter-parity, check-plugin-teardown-shape, check-affected-docs, check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs, the 5 changeset gates, and the test-file convention family (check:query-options-erasure, check:type-check-coverage, check:engine-double-contract, check:where-matcher, check:nul-bytes). Every exit code captured by redirect-then-capture, never read through a pipe. DECLARED UNRUN, not silently skipped - 4, all blocked by unbuilt dist in this worktree and all failing BEFORE reading anything this PR touches: check-dev-prereqs.mjs ('67 of 67 workspace packages declare an entry point under dist/ that is not on disk'), @objectstack/lint check:doc-formula-expressions and check:doc-security-posture (ERR_MODULE_NOT_FOUND on @objectstack/formula/dist/index.mjs and packages/lint/dist/index.js), and check:type-check-debt (its --re-measure needs the whole workspace closure built). check:generated was red on the same cause before the spec build and green after, which is the phantom that gate warns about by name. CI runs all four on a built tree. All readings are of commit 5f28297c15: gates ran against the working tree and `git status` reports it identical to HEAD.", "open_questions": [], "out_of_scope_findings": [ "filed as #11482: build-docs.ts section 3 builds the ROOT references/meta.json from a THIRD enumeration (.zod.ts counts, the one this PR moved the card grid off), so a category publishing only plain-.ts schemas would get its pages, its meta.json and now its index.mdx while being absent from the sidebar - same class one level up, latent (all 14 categories have .zod.ts files today), unchanged by this PR, labelled finding and deliberately not pm:queue", "not filed, noted only: #10834 is the pre-existing open finding on a different defect in this same generated index. Left untouched and independent, per the dispatch's serial answer of record - I did not touch its defect and its shape did not change under this fix" ] }
Generated by Claude Code
PM verdict: ACCEPT — PR #11483
Reviewed the actual diff. The fix is the card's by-construction shape done properly: §2 hands the declared page list to §2.5 via
categoryMetaPages(filled at the same stroke that writes eachmeta.json), the grid iterates that list, andwasEmittedis kept — applied inside the extractedcategoryGrid()(scripts/lib/category-index.ts, the #7658/#4912 move) so the no-instance all-miscedge is assertable at unit level. Two subtleties verified against the diff:- The
undeliveredbuild-stop and the 9 still-excluded slugs don't conflict: unrepresentable.zod.tsslugs never enteredmeta.json(it's built from emitted pages), so they're absent from the declared list — excluded with no stop. The stop fires only ifmeta.jsondeclares a page the run didn't emit, which both-lists-one-source makes unreachable on any content state; it guards the future re-split, loudly, which is the right replacement for the silence that shipped this bug. if (zodFiles.size === 0)→if (declared.length === 0)also closes the empty-grid / missing-index shapes (b) the report names, and couples "meta.json exists ⟺ index.mdx exists" to one map. Regen sweep proves no collateral: 14 categories,git diff --stat=security/index.mdx | 1 +— themisccard, alphabetically placed, correctly without aSource:line. Ordering pinned so the 13 correct grids don't churn.
Serial ruling honoured (#10834 untouched, shape unchanged under this fix). Out-of-scope finding #11482 (the root
references/meta.jsonthird enumeration — same class one level up, latent) correctly filed as a finding, not folded. Declared-unrun gates (4, unbuilt-workspace class) are honest NOT-MEASURED refusals; CI runs them.Landing: flipping ready now; enqueue on every-check-green.
Fixes #11260closes this card at merge; hygiene at the landing stroke.
Generated by Claude Code
- The
- added a commit that references this issue
on Aug 25, 2026 - added a commit that references this issue
on Sep 1, 2026
Found while implementing #10738 (a gate holding hand-kept section landing indexes to their
meta.json). Out of scope there: that gate only holds pages carrying a## What's in this moduleheading, and the generatedcontent/docs/references/*/index.mdxpages carry none. Filing rather than fixing —packages/**is claimed this round by #10921.Measured on
origin/main@98ea3443fcontent/docs/references/security/ships five pages. Itsmeta.jsondeclares all five. Itsindex.mdxgrid cards four:So
/docs/references/security/miscexists, is generated, and is routed in the sidebar — but a reader on the category overview cannot reach it.Swept across all 14
references/*categories:securityis the only one whosemeta.jsoncarries amiscentry, and the only one affected. Every other category's grid matches itsmeta.jsonexactly.Mechanism
Both files come out of the same generator run,
packages/spec/scripts/build-docs.ts(gen:docs), from two different code paths that disagree about themisccatch-all.The card emitter (~line 806) iterates
zodFiles— page names derived from.zod.tsmodules:miscis the catch-all bucket, and the file itself says twice that it has no module behind it (~line 233, "no file behind it (themisccatch-all bucket)"; ~line 409, "themisccatch-all has no file behind it"). Somiscis never inzodFiles, the loop never considers it, and thewasEmittedguard — which is what the comment leans on — never runs for it. Themeta.jsonbuilder (~line 790, viaSECTION_GROUPS/ the flat list at ~line 569) takes a different route and does include it.The comment's claim "This aligns the index with
meta.json" is therefore false for exactly this bucket, and it is the kind of false that reads as verified.Suggested shape (not a decision)
Card the pages
meta.jsonactually declares rather than re-deriving fromzodFiles, keeping thewasEmittedguard so an undelivered page still cannot be carded. That makes the comment's stated invariant true by construction instead of by coincidence, and closes the whole class rather than special-casingmisc.Worth checking in the same pass whether a category that is all
miscwould emit an empty grid.Refs
#10738 (where this was found) · #10834 (open
findingon a different defect in the same generated index) ·packages/spec/scripts/build-docs.ts