SPEC-0021: Docusaurus Skill Page Generation
Overviewโ
Formalizes ๐ ADR-0029 by defining how the docs-site build pipeline auto-generates a per-skill Docusaurus page and a hero-tile index from each skills/\{name\}/SKILL.md (the same file Claude Code loads at runtime), with an opt-in editorial override hatch protected by a build-time SHA-256 pin. This spec extends SPEC-0004 (Documentation Site Generation): it adds a third transform (transform-skills.js) alongside the existing ADR and OpenSpec transforms, a skillsSidebar and "Skills" navbar entry, and a /skills/ route group. It also requires installation and configuration of @docusaurus/plugin-client-redirects so the staged migration of docs-site/content/guides/commands.mdx can preserve every external inbound link.
The spec defines: (1) the SKILL.md โ MDX schema and section-ordering algorithm (canonical sections in fixed positions, non-canonical H2 sections appended verbatim in source order); (2) the skills/_index.json manifest schema and bidirectional consistency check; (3) the override file format and the Governing-SKILL: <path>@<sha256> pin; (4) silent vs. fail-build behavior for every edge case enumerated in ๐ ADR-0029; (5) governing-comment aggregation, dedup, and pill rendering; (6) the staged commands.mdx migration and per-anchor redirect requirements; (7) integration into the existing build-docs.js orchestrator without any change to .github/workflows/deploy-docs.yml.
This is a docs-site spec: there is no auth surface, no user input, and no JavaScript executed beyond what the existing Docusaurus pipeline already mitigates. The security-by-default injection from SPEC-0016 does not apply; see design.md ยง Security Posture for the rationale.
Requirementsโ
Requirement: Per-Skill Page Generationโ
The docs-site build pipeline MUST emit exactly one MDX file per directory under skills/ containing a SKILL.md. The output path MUST be docs-generated/skills/\{name\}.mdx and MUST resolve at the route /skills/\{name\} after Docusaurus build. The transform script docs-site/scripts/transform-skills.js MUST own this generation and MUST NOT be inlined into other transforms.
Scenario: every skill produces one pageโ
- WHEN
npm run buildruns indocs-site/andskills/contains directoriesadr/,work/,graph/, andprime/, each with aSKILL.md - THEN
docs-generated/skills/MUST containadr.mdx,work.mdx,graph.mdx,prime.mdx, andindex.mdx - AND the deployed routes
/skills/adr,/skills/work,/skills/graph,/skills/prime, and/skills/MUST all resolve
Scenario: editing one SKILL.md regenerates only that pageโ
- WHEN the contents of
skills/work/SKILL.mdchange andnpm run buildruns - THEN
docs-generated/skills/work.mdxMUST reflect the change - AND the other skill MDX files MUST be byte-identical to the prior build (modulo any unrelated transforms)
Requirement: Source-File Schema Mappingโ
transform-skills.js MUST extract the following inputs from each skill and place them at the documented output positions:
- Frontmatter
nameโ page H1, sidebar label, URL slug. - Frontmatter
descriptionโ page subtitle, hero-tile description,<meta>description. - Frontmatter
argument-hintโ "Usage" code block. - Frontmatter
allowed-toolsโ collapsed "Required Tools" detail block. - Frontmatter
disable-model-invocation(when present and truthy) โ a "Manual-Invocation Only" badge near the page title. - Any other frontmatter key โ ignored for rendering, preserved in the source file.
- Body intro paragraph (text after H1, before the first H2) โ "Overview" section.
- Body
## Processsection โ "Process" section, with header levels demoted by one so the page has a single H1. - Body
## Rulessection โ "Rules" section, header levels demoted by one. - Any other H2 section in the SKILL.md body โ appended verbatim (modulo
mdx-escape.js) in source order between Rules and Reference, header levels demoted by one. - The H2 extractor MUST be fence-aware: H2 lines (
## ...) that appear inside a fenced code block (delimited by```or~~~) MUST NOT be hoisted as top-level skill sections, and MUST be preserved verbatim within their enclosing fence. Implementations MUST track fence state using theisCodeFencehelper exported bydocs-site/scripts/transform-utils.js(the same helpertransformSpecReferencesandtransformAdrReferencesalready use). - Sibling
references/*.mdfiles in the skill directory โ "Reference" appendix, one collapsible<details>per file. evals/triggers/\{name\}.jsonentries withshould_trigger: trueโ "Example Invocations" code block, capped at 5 entries (the first 5 in file order).- All
<!-- Governing: ... -->and<!-- Implements: ... -->comments anywhere in the file (excluding YAML frontmatter) โ "Governing Artifacts" pill list rendered above the Overview.
Scenario: frontmatter fields populate the canonical positionsโ
- WHEN
skills/adr/SKILL.mdhas frontmattername: adr,description: "Create a new Architecture Decision Record (ADR)...",argument-hint: "[topic]", andallowed-tools: [Read, Write, AskUserQuestion] - THEN
docs-generated/skills/adr.mdxMUST render an H1 derived fromname, a subtitle line containing the description, a "Usage" code block containing/sdd:adr [topic], and a collapsed details element listingRead, Write, AskUserQuestion
Scenario: a skill with disable-model-invocation: trueโ
- WHEN
skills/\{name\}/SKILL.mdfrontmatter containsdisable-model-invocation: true - THEN the generated page MUST display a "Manual-Invocation Only" badge near the title
- AND the absence of the field (or a falsey value) MUST omit the badge
Scenario: H2 lines inside a fenced code block are not hoisted as sectionsโ
- WHEN
skills/adr/SKILL.mdcontains a## MADR TemplateH2 followed by a fenced```markdownblock whose body includes the inner H2 lines## Context and Problem Statement,## Decision Drivers,## Considered Options,## Decision Outcome,## Pros and Cons of the Options,## Architecture Diagram, and## More Information - THEN the generated
adr.mdxMUST treat## MADR Templateas a single non-canonical H2 section whose body contains the entire fenced block verbatim - AND the seven inner H2 lines MUST NOT produce additional top-level skill sections in the output
- AND the seven inner H2 lines MUST appear inside the rendered code fence exactly as authored, with no header demotion or hoisting applied to lines inside the fence
Requirement: Section Orderingโ
The generated page MUST emit sections in this fixed order: (1) H1 Title, (2) Subtitle, (3) "Governing Artifacts" pill list (if any), (4) Usage, (5) Required Tools, (6) Overview, (7) Process, (8) Rules, (9) every non-canonical H2 from the source file in source order, (10) Reference, (11) Example Invocations. Canonical sections (Title, Subtitle, Usage, Required Tools, Overview, Process, Rules, Reference, Example Invocations, Governing Artifacts) MUST appear at fixed positions; non-canonical H2 sections MUST be appended verbatim in source order between Rules and Reference; no other position is allowed for them.
Scenario: a skill with non-canonical H2 sections renders them in source orderโ
- WHEN
skills/adr/SKILL.mdbody contains## Process,## MADR Template,## Architecture Diagram,## Graph Edge Frontmatter, and## Rulesin that source order - THEN the generated
adr.mdxMUST emit Title โ Subtitle โ Governing Artifacts โ Usage โ Required Tools โ Overview โ Process โ Rules โ MADR Template โ Architecture Diagram โ Graph Edge Frontmatter โ Reference โ Example Invocations - AND the non-canonical H2s MUST appear in their original source order (MADR Template before Architecture Diagram before Graph Edge Frontmatter)
Scenario: missing canonical Process or Rules sectionโ
- WHEN
skills/\{name\}/SKILL.mddoes not contain a## Processor## RulesH2 - THEN the generated page MUST omit the corresponding section
- AND the build MUST succeed
- AND non-canonical H2s and the Reference / Example Invocations sections MUST still render
Requirement: Hero-Tile Index Pageโ
transform-skills.js MUST also emit docs-generated/skills/index.mdx rendering a <SkillTile> per registered skill. Each tile MUST carry the skill's name, description (truncated to ~140 chars at a word boundary), and argument-hint, linking to /skills/\{name\}. Tiles MUST be grouped and ordered exclusively by skills/_index.json; per-skill frontmatter MUST NOT influence grouping or order.
Scenario: hero tiles render in manifest orderโ
- WHEN
skills/_index.jsondeclares a groupCreating Artifactswith skills[adr, spec]and a groupImplementationwith skills[work, review] - THEN
docs-generated/skills/index.mdxMUST render the Creating Artifacts group before the Implementation group - AND within each group the tiles MUST appear in the order listed in the manifest
Scenario: tile description truncationโ
- WHEN a skill's
descriptionexceeds 140 characters - THEN the tile MUST display the truncated description ending at a word boundary, suffixed with an ellipsis
- AND the per-skill page subtitle MUST display the full untruncated description
Requirement: Manifest Schema and Validationโ
skills/_index.json MUST conform to the JSON Schema at docs-site/scripts/schemas/skills-index.schema.json. The manifest MUST be an object whose keys are group display names (strings) and whose values are ordered arrays of skill name strings. transform-skills.js MUST validate the manifest with Ajv at the start of the build. Schema violations MUST fail the build with the specific Ajv error message. The schema MUST forbid duplicate skill names across or within groups.
Scenario: manifest fails Ajv validationโ
- WHEN
skills/_index.jsoncontains a group whose value is a string instead of an array - THEN the build MUST fail with the Ajv error identifying the offending key and the expected array type
Scenario: duplicate skill across groupsโ
- WHEN
skills/_index.jsonlistsadrin bothCreating ArtifactsandImplementation - THEN the build MUST fail with an error naming
adrand both group keys
Requirement: Bidirectional Manifest Consistencyโ
The set of skill names in skills/_index.json MUST equal the set of subdirectories of skills/ containing a SKILL.md. A skill present on disk but absent from the manifest MUST fail the build with an error naming the unregistered skill. A manifest entry referencing a non-existent skills/\{name\}/SKILL.md MUST fail the build with an error naming the stale entry.
Scenario: a skill directory exists but is not registeredโ
- WHEN
skills/foo/SKILL.mdexists butfoodoes not appear in any group ofskills/_index.json - THEN the build MUST fail with the error:
skills/foo: not registered in skills/_index.json โ add it to a group or remove the directory
Scenario: a manifest entry has no corresponding SKILL.mdโ
- WHEN
skills/_index.jsonlistsbarin a group butskills/bar/SKILL.mddoes not exist - THEN the build MUST fail with the error:
skills/_index.json references "bar" but skills/bar/SKILL.md does not exist
Requirement: Governing-Comment Aggregation and Cross-Linkingโ
transform-skills.js MUST scan the entire SKILL.md body (frontmatter excluded) for <!-- Governing: ... --> and <!-- Implements: ... --> comments, parse each into ADR-XXXX and SPEC-YYYY references, deduplicate by reference, and sort the deduped set with ADRs ascending followed by SPECs ascending. The result MUST render as a single "Governing Artifacts" pill list at the top of the page (above Overview, below Subtitle). ADR pills MUST link to /decisions/\{adr-slug\} via transformAdrReferences from transform-utils.js; SPEC pills MUST link to /specs/\{spec-slug\}/spec#spec-NNNN (the SPEC-ID anchor only) via transformSpecReferences from transform-utils.js. The REQ "..." clause that may follow a SPEC-ID inside a <!-- Governing: --> or <!-- Implements: --> comment MAY be retained as the pill's display text, but the URL fragment MUST remain the SPEC-ID anchor โ REQ-level anchoring is not currently produced by transformSpecReferences and is deferred (see Open Questions in design.md). <!-- Implements: ... --> comments MUST be folded into the same pill list as <!-- Governing: ... --> comments โ no separate section.
Scenario: multiple comments collapse to a single pill listโ
- WHEN
skills/work/SKILL.mdcontains five<!-- Governing: -->comments (file-scope and inline within## Process) referencing ๐ ADR-0017 twice,