fix(spec): render @example captions and drop @category on reference pages - #15443
Merged
Conversation
…ce pages `renderFileDescription` emitted a module docblock's prose verbatim, so a JSDoc block tag written on a prose line reached the published page as literal tag text: 18 lines across 14 customer-facing reference pages. The verdict is per tag, on the payload rather than the spelling: - `@example CAPTION` (12 lines, 10 pages) captions the block directly beneath it, so it is REWRITTEN into that caption in bold — the shape `@see` beside it already had. - a bare `@example` (2 lines) has no payload, which is the `@module` case exactly, so it is DROPPED before classification alongside the `os:check` marker it sits against. - `@category Security` (4 lines, 4 pages) is dropped. Measured: one value on four pages, and no consumer anywhere in the repo — no typedoc, no api-extractor, no search index, no gate. Pins assert on the rendered fragment, never on the emitted `.mdx`: `check:docs` compares the artifact to the source and reproduced all 18 tag lines faithfully. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
18 tag lines across 14 pages: 12 `@example CAPTION` become bold captions, 2 bare `@example` and 4 `@category Security` come off. The card's own repro (`grep -rn '^@example\|^@category' content/docs/references/`) now returns zero, and no other page in the 230-file regeneration moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
…c-tag-payload-render
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Sep 4, 2026
os-sales
marked this pull request as ready for review
September 4, 2026 16:20
os-sales
enabled auto-merge
September 4, 2026 16:20
This was referenced Sep 4, 2026
This was referenced Sep 4, 2026
Merged
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.
Fixes #14455
A module docblock is JSDoc, so its header carries block tags, and
renderFileDescriptionemitted a tag written on a prose line verbatim. 18 such lines reached 14 customer-facing reference pages. #13796 removed@moduleand deliberately left these two open, because a blanket^@\w+line filter would take reader prose off the page and orphan the fences below it.The verdict is per tag, and the axis is the payload
@example CAPTION**CAPTION**@seebeside it already had@examplebare@modulecase exactly@category VALUEBold rather than a heading.
withHeadingsAtSectionLevelhas already renumbered the block by the time the rewrite runs, so a heading emitted here would carry a level chosen blind of the page and would not move with the rest — on top of adding 12 entries to 8 pages' tables of contents and putting a caption in reach ofcheck:docs-single-h1. Strong emphasis cannot touch a page's heading structure at all.Bold rather than
Example: CAPTION, the literal parallel ofSee also:.@seeneeds its label because a bare URL says nothing about why it is on the page; a fenced block under a caption is already visibly an example, so the label only restates the fence (Example: Endpoints).@categoryis a seat ruling, and it rests on a measurement rather than on taste. Nothing in this repo reads the tag: no typedoc or api-extractor (neither is used here), no search index, no gate — the only matches outsidepackages/spec/srcwere the pin that asserted the defect, the renderer's own comment, and a CHANGELOG line. Four pages, one value (Security), under asystem/section that already says as much. Routing it into page frontmatter instead would publish a field with no consumer, which is the declared-but-unenforced shape ADR-0049 refuses. It is dropped from the rendered PAGE only —@categorystays in the source, where it is a legitimate JSDoc tag, and the day something reads it,CATEGORY_MARKERis what gets revisited rather than the four docblocks.Measurements
The card's own repro, re-run before editing (the card's counts were from 2026-09-02): unchanged at 18 lines —
grep -rn '^@example\|^@category' content/docs/references/gave 14 + 4, the identical set. It now returns zero. One correction: the card and its title say 13 pages; the two disjoint sets are 10 + 4, so the real figure is 14.Blast radius. The renderer moves, so
gen:docsregenerates corpus-wide: 230 files regenerated, exactly 14 changed — the 14 the repro names and no others. The whole corpus diff is 12 line rewrites and 6 deletions, every one of them one of the 18 lines. No page moved for an unrelated reason.Reverse verification. Restoring
origin/main'sfile-description.tsand re-running turns 8 cases red: 5 of the 7 new unit cases, both new corpus assertions, and the retriaged#10924case. The two that stay green assert the ABSENCE of over-reach (a tag inside a fence, a mid-sentence mention), which the pre-fix renderer also satisfied by doing nothing — the asymmetry is the point, and those two are guards against a future over-broad filter rather than against the old state. No dist is involved in either leg: the test imports./lib/file-descriptionby relative path inside the same package, so there is nothing to rebuild between them. The mutation was confirmed on disk by blob hash (differs from the HEAD blob, andEXAMPLE_CAPTION/CATEGORY_MARKERboth at 0 occurrences); the restore by hash equality with the HEAD blob plus an emptygit diff HEADon the path.Two fixtures were retriaged, not re-spelled
Both pinned the branch this card removes, and both kept passing precisely because the tag was still being published:
#13796→leaves every other block tag aloneasserted@example Basic field mappingand@category Securityreached the page. Rewritten to assert what#13796actually owns — its drop is scoped to its own tag and the caption prose beside it is not collateral.#10924→drops a marker line above a fenceasserted the bare@exampleabove theos:checkmarker survived. Rewritten to assert the fence attaches directly to the prose above it, with the reason for the flip named in place.Pins
Assert on the rendered fragment, never on the emitted
.mdx:check:docscompares the artifact against the source and reproduced all 18 tag lines faithfully, so it was green through every one of them. Two corpus assertions re-derive from the realpackages/spec/srctree — an invariant (no rendered line opens with either tag) plus its necessary other half (every@examplecaption still reaches its page, so a fix that got the partition backwards cannot pass by deleting the prose), with a vacuity guard on the caption count.Verification
Union re-run on the final head
954d10c76, after mergingmainthroughscripts/pm/os-regen-merge.shand re-regenerating the whole chain (regeneration came back byte-identical, so nothing was silently dropped by theos-regenmerge driver).pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2— 470 files / 12,649 tests passed, 1 skipped, 0 failedpnpm --filter @objectstack/spec typecheck— exit 0.tsconfig.scripts.jsonverifiably reads both edited files (--listFilescounts 1 each, not 0), so the green verdict covers thempnpm lint(repo-wideeslint . --no-inline-config) — exit 0pnpm --filter @objectstack/spec check:docs—230 generated files in sync with packages/specnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(17 paths, unchanged across the merge); the implicated families all exit 0:check:skill-refs·check:skill-docs·check:docs-single-h1·check:doc-anchors·check:doc-authoring·check:docs-redirects·check:corpus-claim-drift·check:quick-reference-counts·check:page-declaration-shape·check:docs-audit-scope·check:nul-bytes·check:cross-package-test-inputs·check:test-source-alias·check:changeset-gate-self-tests·check:published-filesOut of scope
Filed separately as #15440:
api/automation-apiandapi/package-apiwrite their endpoint listing unfenced, so it publishes as a run-on paragraph. This card rewrites the caption line above those listings and deliberately does not touch what is beneath it; the fix belongs in the two sources.🤖 Generated with Claude Code
https://claude.ai/code/session_01G4138K1EG7kQ81FNba5Kp4
Generated by Claude Code