# Semantic CSS Author Reference and Unified Documentation Generation Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Publish a canonical author-facing Semantic CSS language reference, link it from Custom Styles, and make `pnpm docs:gen` regenerate every Semantic CSS, Resume JSON Schema, and OpenAPI documentation artifact. **Architecture:** Keep runtime registries and PDF template manifests authoritative, adding only the missing structured metadata needed for documentation. A tooling-owned generator updates Semantic CSS and Resume JSON Schema Markdown from those sources, while a server-owned generator reuses the runtime OpenAPI builder for `docs/spec.json`; the root command runs both. Generated Markdown is marker-delimited, deterministic, and tested against the committed artifacts. **Tech Stack:** TypeScript, pnpm workspaces, Zod 4 JSON Schema conversion, oRPC OpenAPI, Vitest, React 19, Lingui, Mintlify MDX. ## Global Constraints - The public reference route is exactly `https://docs.rxresu.me/guides/semantic-css-reference`. - The audience is resume authors, not compiler contributors. - The root command is exactly `pnpm docs:gen`; remove the obsolete `docs:semantic-css` script. - `pnpm docs:gen` regenerates `docs/guides/semantic-css-reference.mdx`, `skills/resume-builder/references/schema.md`, `docs/guides/json-resume-schema.mdx`, and `docs/spec.json`. - The checked-in OpenAPI document uses `https://rxresu.me/api/openapi` and the root package version. - Runtime `/schema.json`, the skill schema reference, and the JSON schema guide use one canonical input-side JSON Schema created from `resumeDataSchema`. - Complete Semantic CSS examples marked as valid must compile; selected invalid examples must emit their documented code. - Preserve distinct `list-item` row, `list-item-content`, and `list-marker` author semantics. - Do not add a shared docs-link component, URL registry, documentation playground, or contributor/compiler architecture page. - Do not add a general file-transaction framework; compute each generator's complete output set before its first write. - Preserve all pre-existing worktree changes. When a target file is already modified, stage only this plan's hunks and inspect `git diff --cached` before committing. - Verification is CLI-based. Do not use Chrome. ## File Map ### Canonical schema and API artifacts - Create `packages/schema/src/resume/json-schema.ts`: the only Zod-to-JSON-Schema conversion policy for ResumeData. - Create `packages/schema/src/resume/json-schema.test.ts`: proves transforms are handled and the emitted schema describes input payloads. - Modify `apps/server/src/static/schema.ts`: return the canonical schema helper result. - Create `apps/server/src/openapi/generator.ts`: own the shared OpenAPI router and `generateOpenApiSpec`. - Create `apps/server/src/openapi/generator.test.ts`: verify caller-provided URL/version and stable API metadata. - Create `apps/server/src/openapi/generate-spec.ts`: write the production documentation spec. - Modify `apps/server/src/openapi/handler.ts`: delegate spec generation while retaining request handling. - Modify `apps/server/package.json`: expose the server-side docs generation command. ### Semantic CSS reference metadata - Modify `packages/resume/src/stylesheet/diagnostics.ts`: add the typed compiler diagnostic catalog. - Modify `packages/resume/src/stylesheet/values.ts`: constrain the internal diagnostic helper to catalog codes. - Modify `packages/resume/src/stylesheet/registry/semantic.ts`: expose known attribute values, add `contact-list` as a valid template-part parent, and register the two Chikorita contact-row child contracts. - Modify `packages/resume/src/stylesheet/registry/index.ts`: export the new public metadata. - Modify `packages/resume/src/stylesheet/index.ts`: export compiler diagnostics and limits for generation. - Modify `packages/resume/src/stylesheet/registry/semantic.test.ts`: verify the new parent/value metadata. - Create `packages/pdf/src/semantic/preflight-reference.ts`: pure failure metadata and author-facing preflight limits. - Modify `packages/pdf/src/semantic/preflight-core.tsx`: consume the catalog-backed failure-code type. - Modify `packages/pdf/src/semantic/template-manifest.ts`: expose the frozen manifest record through the existing getter. - Modify `packages/pdf/src/semantic/template-manifest.test.ts`: enforce primitive-part child coverage. - Modify `packages/pdf/src/server.tsx`: retain the existing preflight type exports. - Modify `packages/pdf/package.json`: add an explicit `./semantic-manifest` export for the tooling package. - Modify `apps/server/src/services/stylesheet-preflight.ts`: consume and re-export the shared preflight limits. ### Documentation generation and content - Modify `tooling/package.json`: add schema/PDF workspace dependencies and a package-local `docs:gen` script. - Modify `tooling/semantic-css/generate-reference.ts`: render all Semantic CSS and Resume JSON Schema outputs. - Modify `tooling/semantic-css/generate-reference.test.ts`: verify markers, determinism, manifest coverage, examples, and committed output synchronization. - Modify `package.json`: replace `docs:semantic-css` with the root `docs:gen` orchestration. - Rewrite `docs/guides/semantic-css-reference.mdx`: the author-facing Semantic CSS reference with generated sections and recipes. - Modify `docs/guides/json-resume-schema.mdx`: replace the hand-copied schema with one generated block. - Regenerate `skills/resume-builder/references/schema.md`: compact field-path reference for AI authors. - Regenerate `docs/spec.json`: current version and production server URL. - Modify `docs/docs.json`: add the Semantic CSS reference after Using Custom Styles. ### Custom Styles hint - Modify `apps/web/src/features/resume/stylesheet/editor.tsx`: render the translated, accessible docs hint in shared editor chrome. - Modify `apps/web/src/features/resume/stylesheet/editor.test.tsx`: verify the exact link contract on desktop and in the mobile sheet. --- ### Task 1: Canonical Resume JSON Schema **Files:** - Create: `packages/schema/src/resume/json-schema.ts` - Create: `packages/schema/src/resume/json-schema.test.ts` - Modify: `apps/server/src/static/schema.ts` **Interfaces:** - Consumes: `resumeDataSchema` from `@reactive-resume/schema/resume/data`. - Produces: `createResumeDataJsonSchema(): ReturnType`. - Produces: an input-side draft 2020-12 schema that tolerates Zod transforms with `unrepresentable: "any"`. - [ ] **Step 1: Write the failing canonical-schema test** ```ts import { describe, expect, it } from "vitest"; import { createResumeDataJsonSchema } from "./json-schema"; describe("createResumeDataJsonSchema", () => { it("describes accepted ResumeData input even when the Zod schema contains transforms", () => { const schema = createResumeDataJsonSchema(); expect(schema).toMatchObject({ $schema: "https://json-schema.org/draft/2020-12/schema", type: "object", required: ["picture", "basics", "summary", "sections", "customSections", "metadata"], properties: { picture: { type: "object" }, basics: { type: "object" }, sections: { type: "object" }, metadata: { type: "object" }, }, }); }); }); ``` - [ ] **Step 2: Run the schema test and verify the missing module failure** Run: ```bash pnpm --filter @reactive-resume/schema test -- src/resume/json-schema.test.ts ``` Expected: FAIL because `./json-schema` does not exist. - [ ] **Step 3: Add the canonical conversion helper** ```ts import z from "zod"; import { resumeDataSchema } from "./data"; export function createResumeDataJsonSchema() { return z.toJSONSchema(resumeDataSchema, { io: "input", unrepresentable: "any", }); } ``` This policy is deliberate: documentation describes accepted resume payloads, and transform output cannot be represented exactly in JSON Schema. - [ ] **Step 4: Route `/schema.json` through the helper** Replace the direct Zod conversion in `apps/server/src/static/schema.ts` with: ```ts import { createResumeDataJsonSchema } from "@reactive-resume/schema/resume/json-schema"; export function handleSchemaJson() { return Response.json(createResumeDataJsonSchema(), { status: 200, headers: { "Content-Type": "application/schema+json; charset=utf-8", "Cache-Control": "public, max-age=86400, immutable", "Surrogate-Control": "max-age=86400", "X-Content-Type-Options": "nosniff", "X-Robots-Tag": "index, follow", ETag: appVersion, Vary: "Accept", }, }); } ``` - [ ] **Step 5: Run focused tests and typechecks** Run: ```bash pnpm --filter @reactive-resume/schema test -- src/resume/json-schema.test.ts pnpm --filter @reactive-resume/schema typecheck pnpm --filter server typecheck ``` Expected: PASS. - [ ] **Step 6: Commit only the canonical-schema changes** ```bash git add packages/schema/src/resume/json-schema.ts packages/schema/src/resume/json-schema.test.ts apps/server/src/static/schema.ts git diff --cached --check git commit -m "refactor: centralize resume JSON schema generation" ``` --- ### Task 2: Shared Runtime and Documentation OpenAPI Generator **Files:** - Create: `apps/server/src/openapi/generator.ts` - Create: `apps/server/src/openapi/generator.test.ts` - Create: `apps/server/src/openapi/generate-spec.ts` - Modify: `apps/server/src/openapi/handler.ts` - Modify: `apps/server/package.json` **Interfaces:** - Consumes: the current oRPC router, `downloadResumePdfProcedure`, and explicit `{ appUrl, version }`. - Produces: `generateOpenApiSpec(options: { appUrl: string; version: string }): Promise`. - Produces: server script `pnpm --filter server docs:gen`. - [ ] **Step 1: Write the failing OpenAPI generator test** ```ts import { describe, expect, it } from "vitest"; import { generateOpenApiSpec } from "./generator"; describe("generateOpenApiSpec", () => { it("uses caller-provided application URL and version", async () => { const spec = await generateOpenApiSpec({ appUrl: "https://rxresu.me", version: "9.8.7", }); expect(spec.info).toMatchObject({ title: "Reactive Resume", version: "9.8.7", }); expect(spec.servers).toEqual([{ url: "https://rxresu.me/api/openapi" }]); expect(spec.externalDocs).toEqual({ url: "https://docs.rxresu.me", description: "Reactive Resume Documentation", }); }); }); ``` - [ ] **Step 2: Run the test and verify the missing module failure** Run: ```bash pnpm --filter server test -- src/openapi/generator.test.ts ``` Expected: FAIL because `./generator` does not exist. - [ ] **Step 3: Extract the router and OpenAPI builder** Move `openAPIRouter`, `openAPIGenerator`, and the current metadata object from `handler.ts` into `generator.ts`. Keep the existing download-PDF override and internal-route filter. Export: ```ts export const openAPIRouter = { ...router, resume: { ...router.resume, downloadPdf: downloadResumePdfProcedure, }, }; export async function generateOpenApiSpec({ appUrl, version }: { appUrl: string; version: string }) { return openAPIGenerator.generate(openAPIRouter, { info: { title: "Reactive Resume", version, description: "Reactive Resume API", license: { name: "MIT", url: "https://github.com/amruthpillai/reactive-resume/blob/main/LICENSE" }, contact: { name: "Amruth Pillai", email: "hello@amruthpillai.com", url: "https://amruthpillai.com" }, }, servers: [{ url: `${appUrl}/api/openapi` }], externalDocs: { url: "https://docs.rxresu.me", description: "Reactive Resume Documentation" }, commonSchemas: { ResumeData: { schema: resumeDataSchema }, }, components: { securitySchemes: { apiKey: { type: "apiKey", name: "x-api-key", in: "header", description: "The API key to authenticate requests.", }, }, }, security: [{ apiKey: [] }], filter: ({ contract }) => !contract["~orpc"].route.tags?.includes("Internal"), }); } ``` - [ ] **Step 4: Make the runtime handler delegate** Keep `OpenAPIHandler`, plugins, interceptors, locale, and response-header behavior in `handler.ts`. Replace only its spec-building branch: ```ts if (request.method === "GET" && (request.url.endsWith("/spec.json") || request.url.endsWith("/spec"))) { return Response.json(await generateOpenApiSpec({ appUrl: env.APP_URL, version: appVersion })); } ``` - [ ] **Step 5: Add the deterministic documentation writer** Create `generate-spec.ts` with a default target of `docs/spec.json`, read the root `package.json` version, and serialize with tabs plus one trailing newline: ```ts import { readFile, writeFile } from "node:fs/promises"; import { fileURLToPath } from "node:url"; import { generateOpenApiSpec } from "./generator"; export async function generateOpenApiDocumentation( target = fileURLToPath(new URL("../../../../docs/spec.json", import.meta.url)), ) { const packageJson = JSON.parse( await readFile(new URL("../../../../package.json", import.meta.url), "utf8"), ) as { version: string }; const spec = await generateOpenApiSpec({ appUrl: "https://rxresu.me", version: packageJson.version }); await writeFile(target, `${JSON.stringify(spec, null, "\t")}\n`); } if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { await generateOpenApiDocumentation(process.argv[2]); } ``` Add to `apps/server/package.json`: ```json "docs:gen": "tsx src/openapi/generate-spec.ts" ``` - [ ] **Step 6: Run focused OpenAPI tests and typecheck** Run: ```bash pnpm --filter server test -- src/openapi/generator.test.ts src/http/app.test.ts pnpm --filter server typecheck ``` Expected: PASS, and existing request routing remains unchanged. - [ ] **Step 7: Commit the shared OpenAPI builder** ```bash git add apps/server/src/openapi/generator.ts apps/server/src/openapi/generator.test.ts apps/server/src/openapi/generate-spec.ts apps/server/src/openapi/handler.ts apps/server/package.json git diff --cached --check git commit -m "refactor: share OpenAPI documentation generator" ``` --- ### Task 3: Structured Semantic CSS Reference Metadata **Files:** - Modify: `packages/resume/src/stylesheet/diagnostics.ts` - Modify: `packages/resume/src/stylesheet/values.ts` - Modify: `packages/resume/src/stylesheet/registry/semantic.ts` - Modify: `packages/resume/src/stylesheet/registry/index.ts` - Modify: `packages/resume/src/stylesheet/index.ts` - Modify: `packages/resume/src/stylesheet/registry/semantic.test.ts` - Create: `packages/pdf/src/semantic/preflight-reference.ts` - Modify: `packages/pdf/src/semantic/preflight-core.tsx` - Modify: `packages/pdf/src/semantic/template-manifest.test.ts` - Modify: `packages/pdf/src/server.tsx` - Modify: `packages/pdf/package.json` - Modify: `apps/server/src/services/stylesheet-preflight.ts` **Interfaces:** - Produces: `SEMANTIC_CSS_DIAGNOSTIC_CATALOG_V1`, keyed by every compiler diagnostic code. - Produces: `attributeValues?: Readonly>` on semantic definitions. - Produces: `PDF_PREFLIGHT_DIAGNOSTIC_CATALOG`, keyed by every `PdfPreflightFailureCode`, without importing the PDF renderer. - Produces: `STYLESHEET_PREFLIGHT_LIMITS`, shared by docs and the server runner. - Produces: `@reactive-resume/pdf/semantic-manifest` as a tooling-safe explicit export. - [ ] **Step 1: Write failing registry and manifest consistency assertions** Add to `semantic.test.ts`: ```ts expect(SEMANTIC_REGISTRY_V1.resume.attributeValues?.template).toEqual(templateSchema.options); expect(SEMANTIC_REGISTRY_V1.section.attributeValues).toMatchObject({ type: sectionTypeSchema.options, placement: ["main", "sidebar"], origin: ["main", "sidebar"], }); expect(SEMANTIC_REGISTRY_V1["template-part"].parents).toContain("contact-list"); expect(TEMPLATE_PART_CHILD_KINDS_V1["contact-row-primary"]).toEqual(["contact-item"]); expect(TEMPLATE_PART_CHILD_KINDS_V1["contact-row-secondary"]).toEqual(["contact-item"]); ``` Add to `template-manifest.test.ts`: ```ts for (const manifest of Object.values(getTemplateSemanticRegistryFingerprintInput())) { for (const part of manifest.parts) { if (part.binding.type === "alias") continue; expect(TEMPLATE_PART_CHILD_KINDS_V1, `${manifest.template}:${part.name}`).toHaveProperty(part.name); } } ``` - [ ] **Step 2: Run the focused tests and verify the missing metadata failures** Run: ```bash pnpm --filter @reactive-resume/resume test -- src/stylesheet/registry/semantic.test.ts pnpm --filter @reactive-resume/pdf test -- src/semantic/template-manifest.test.ts ``` Expected: FAIL for missing attribute domains, `contact-list` parent support, and Chikorita contact-row child contracts. - [ ] **Step 3: Add semantic attribute domains and parent/child consistency** Extend `SemanticNodeDefinition`: ```ts export type SemanticNodeDefinition = { parents: readonly SemanticNodeKind[]; attributes: readonly string[]; roles: readonly string[]; attributeValues?: Readonly>; }; ``` Import `sectionTypeSchema` and `templateSchema`, then add finite domains: ```ts resume: { parents: [], attributes: ["template"], roles: [], attributeValues: { template: templateSchema.options }, }, region: { parents: ["page"], attributes: ["placement", "region", "part"], roles: [], attributeValues: { placement: ["main", "sidebar"], region: ["header", "main", "sidebar", "featured"], }, }, section: { parents: ["region"], attributes: ["type", "placement", "origin", "part"], roles: ["featured-summary"], attributeValues: { type: sectionTypeSchema.options, placement: ["main", "sidebar"], origin: ["main", "sidebar"], }, }, "rich-heading": { parents: ["rich-text"], attributes: ["level"], roles: [], attributeValues: { level: ["1", "2", "3", "4", "5", "6"] }, }, "list-item-content": { parents: ["list-item"], attributes: ["direction"], roles: [], attributeValues: { direction: ["ltr", "rtl"] }, }, ``` Add `"contact-list"` to the `template-part` parent list and: ```ts "contact-row-primary": ["contact-item"], "contact-row-secondary": ["contact-item"], ``` Do not invent domains for resume-specific IDs, names, page numbers, part names, or roles. - [ ] **Step 4: Add exhaustive compiler diagnostic metadata** Define: ```ts type DiagnosticReference = { severity: SemanticCssDiagnostic["severity"]; meaning: string; action: string; }; export const SEMANTIC_CSS_DIAGNOSTIC_CATALOG_V1 = { MISSING_VERSION_DIRECTIVE: { severity: "warning", meaning: "The stylesheet omitted @version.", action: "Add @version 1; as the first statement.", }, DUPLICATE_VERSION_DIRECTIVE: { severity: "error", meaning: "More than one @version directive was found.", action: "Keep exactly one version directive.", }, INVALID_VERSION: { severity: "error", meaning: "The version directive is not one positive integer without a block.", action: "Use @version 1;.", }, VERSION_MISMATCH: { severity: "error", meaning: "The directive and stored language version disagree.", action: "Set both to version 1.", }, UNSUPPORTED_VERSION: { severity: "error", meaning: "The requested Semantic CSS version is not implemented.", action: "Use @version 1;.", }, CSS_PARSE_ERROR: { severity: "error", meaning: "The stylesheet is not valid parseable CSS syntax.", action: "Fix the syntax at the reported source range.", }, CSS_RAW_SYNTAX: { severity: "error", meaning: "The parser encountered unsupported raw CSS syntax.", action: "Rewrite the declaration or selector using documented Semantic CSS syntax.", }, FORBIDDEN_AT_RULE: { severity: "error", meaning: "The at-rule can load resources or execute unsupported CSS behavior.", action: "Remove the at-rule.", }, UNSUPPORTED_AT_RULE: { severity: "error", meaning: "The at-rule is not part of Semantic CSS version 1.", action: "Use only @version and documented @media queries.", }, INVALID_MEDIA_QUERY: { severity: "error", meaning: "The PDF dimension query is malformed or unsupported.", action: "Use width, min-width, max-width, height, min-height, or max-height with a Semantic CSS length.", }, MEDIA_PAGE_SIZE: { severity: "error", meaning: "A media rule attempts to change the page size it is evaluated against.", action: "Move the page size declaration outside @media.", }, INVALID_SELECTOR: { severity: "error", meaning: "The selector uses unsupported syntax or exceeds selector limits.", action: "Rewrite it using documented Semantic CSS selectors and combinators.", }, UNSUPPORTED_PROPERTY: { severity: "error", meaning: "The property is not in the Semantic CSS property registry.", action: "Choose a property from the property reference.", }, SYSTEM_VARIABLE_READONLY: { severity: "error", meaning: "An author attempted to assign a reserved --resume-* variable.", action: "Read the system variable or rename the author variable.", }, FORBIDDEN_CSS_VALUE: { severity: "error", meaning: "The value attempts to use an external resource or forbidden CSS capability.", action: "Use a PDF-safe local value.", }, INVALID_VALUE: { severity: "error", meaning: "The value does not match the supported grammar for the property.", action: "Use the documented property value form.", }, VARIABLE_CYCLE: { severity: "error", meaning: "Custom properties form a var() reference cycle.", action: "Break the cycle or provide a non-cyclic fallback.", }, UNRESOLVED_VARIABLE: { severity: "error", meaning: "A var() reference has neither a value nor a usable fallback.", action: "Define the variable or add a fallback.", }, EXTREME_VALUE: { severity: "warning", meaning: "A value is valid but likely to produce unusable output.", action: "Reduce the value unless the effect is intentional.", }, SELECTOR_NO_MATCH: { severity: "warning", meaning: "The selector matches no node in the current resume and template.", action: "Check the ID, attribute value, placement, or template guard.", }, PROPERTY_NOT_APPLICABLE: { severity: "warning", meaning: "The property cannot affect any matched semantic node kind.", action: "Target a node listed in the property's Applies to column.", }, RESOURCE_LIMIT: { severity: "error", meaning: "Compilation, matching, values, variables, or semantic nodes exceeded a bounded Semantic CSS limit.", action: "Reduce stylesheet or resume complexity.", }, } as const satisfies Readonly>; export type SemanticCssCompilerDiagnosticCode = keyof typeof SEMANTIC_CSS_DIAGNOSTIC_CATALOG_V1; ``` Change `createDiagnostic` and the private `values.ts` helper to accept `SemanticCssCompilerDiagnosticCode`. Export the catalog through `@reactive-resume/resume/stylesheet`. - [ ] **Step 5: Add exhaustive PDF preflight metadata and shared limits** Create `preflight-reference.ts` so documentation generation does not load React PDF merely to read constants: ```ts export const PDF_PREFLIGHT_DIAGNOSTIC_CATALOG = { STYLESHEET_PREFLIGHT_INVALID: { meaning: "The stylesheet has compiler or semantic errors.", action: "Fix the accompanying Semantic CSS diagnostics.", }, STYLESHEET_PREFLIGHT_PAGE_SIZE_LIMIT: { meaning: "An authored page exceeds the PDF dimension or area budget.", action: "Use a smaller page size.", }, STYLESHEET_PREFLIGHT_BYTE_LIMIT: { meaning: "The rendered PDF exceeds the byte budget.", action: "Reduce pages, images, or styled content.", }, STYLESHEET_PREFLIGHT_PAGE_LIMIT: { meaning: "The rendered PDF exceeds the page-count budget.", action: "Reduce content or pagination.", }, STYLESHEET_PREFLIGHT_TIMEOUT: { meaning: "PDF preflight exceeded its deadline.", action: "Reduce stylesheet or document complexity and retry.", }, STYLESHEET_PREFLIGHT_MEMORY_LIMIT: { meaning: "PDF preflight exceeded its memory budget.", action: "Reduce document, image, or layout complexity.", }, STYLESHEET_PREFLIGHT_RENDER_FAILED: { meaning: "The PDF renderer could not render the candidate stylesheet.", action: "Simplify the candidate and inspect accompanying diagnostics.", }, STYLESHEET_PREFLIGHT_PARSE_FAILED: { meaning: "The rendered PDF could not be inspected.", action: "Retry after simplifying the candidate.", }, STYLESHEET_PREFLIGHT_WORKER_FAILED: { meaning: "The isolated PDF preflight worker failed or its queue was full.", action: "Retry; simplify the candidate if the failure repeats.", }, } as const; export type PdfPreflightFailureCode = keyof typeof PDF_PREFLIGHT_DIAGNOSTIC_CATALOG; export const STYLESHEET_PREFLIGHT_LIMITS = Object.freeze({ timeoutMs: 5_000, maxPages: 20, maxBytes: 10_000_000, maxPageWidthPt: 2_000, maxPageHeightPt: 20_000, maxPageAreaPt2: 20_000_000, maxOldGenerationMb: 256, maxConcurrentWorkers: 1, maxQueuedRequests: 32, }); ``` Import `PdfPreflightFailureCode` into `preflight-core.tsx` and re-export the type there so current browser/server entrypoints retain their interface. Import and re-export `STYLESHEET_PREFLIGHT_LIMITS` from `apps/server/src/services/stylesheet-preflight.ts` so current callers and tests also retain their interface. Add an explicit `@reactive-resume/pdf/preflight-reference` package export for the tooling generator. - [ ] **Step 6: Expose actual template manifests to tooling** Add the package export: ```json "./semantic-manifest": "./src/semantic/template-manifest.ts" ``` The tooling generator will call the existing: ```ts getTemplateSemanticRegistryFingerprintInput(): Readonly> ``` Do not create a duplicate reference-specific manifest. - [ ] **Step 7: Run focused metadata tests and boundaries** Run: ```bash pnpm --filter @reactive-resume/resume test -- src/stylesheet/registry/semantic.test.ts src/stylesheet/values.test.ts pnpm --filter @reactive-resume/pdf test -- src/semantic/template-manifest.test.ts src/semantic/preflight-core.test.tsx pnpm --filter server test -- src/services/stylesheet-preflight.test.ts pnpm --filter @reactive-resume/resume typecheck pnpm --filter @reactive-resume/pdf typecheck pnpm --filter server typecheck pnpm exec turbo boundaries ``` Expected: PASS. - [ ] **Step 8: Commit only the structured reference metadata** Stage only the task's hunks, especially in already-modified PDF files: ```bash git add -p packages/resume/src/stylesheet packages/pdf/src apps/server/src/services/stylesheet-preflight.ts packages/pdf/package.json git diff --cached --check git diff --cached git commit -m "feat: expose Semantic CSS reference metadata" ``` --- ### Task 4: Unified Semantic CSS and Resume Schema Markdown Generator **Files:** - Modify: `tooling/package.json` - Modify: `tooling/semantic-css/generate-reference.ts` - Modify: `tooling/semantic-css/generate-reference.test.ts` - Modify: `package.json` - Modify: `docs/guides/json-resume-schema.mdx` **Interfaces:** - Consumes: canonical Resume JSON Schema, Semantic CSS registries/catalogs/limits, PDF preflight catalogs/limits, and actual template manifests. - Produces: `updateGeneratedDocumentation(paths?: Partial): Promise`. - Produces: root `pnpm docs:gen`. - [ ] **Step 1: Replace the synchronization test with failing multi-output tests** Define targets: ```ts export type DocumentationPaths = { semanticCssReference: string; jsonSchemaGuide: string; skillSchemaReference: string; }; ``` Tests must cover: ```ts it("rejects missing and duplicate generated markers", () => { expect(() => replaceGeneratedBlock("plain text", "SEMANTIC-CSS-ELEMENTS", "body", "reference.mdx")).toThrow( /Missing generated markers/, ); expect(() => replaceGeneratedBlock( "", "SEMANTIC-CSS-ELEMENTS", "body", "reference.mdx", ), ).toThrow(/Duplicate generated markers/); }); it("builds identical output twice", async () => { expect(await buildGeneratedDocumentation(paths)).toEqual(await buildGeneratedDocumentation(paths)); }); it("documents every manifest part with its real selector form", () => { const table = renderTemplateParts(getTemplateSemanticRegistryFingerprintInput()); for (const [template, manifest] of Object.entries(getTemplateSemanticRegistryFingerprintInput())) { for (const part of manifest.parts) { expect(table).toContain(`| \`${template}\``); expect(table).toContain(`\`${part.name}\``); } } }); ``` - [ ] **Step 2: Run the tooling test and verify it fails** Run: ```bash pnpm --filter @reactive-resume/tooling test -- semantic-css/generate-reference.test.ts ``` Expected: FAIL because the multi-output functions and markers do not exist. - [ ] **Step 3: Add the minimum marker and output pipeline** Implement: ```ts export function replaceGeneratedBlock(source: string, name: string, body: string, path: string): string { const start = ``; const end = ``; const startMatches = source.split(start).length - 1; const endMatches = source.split(end).length - 1; if (startMatches === 0 || endMatches === 0) throw new Error(`Missing generated markers ${name} in ${path}.`); if (startMatches !== 1 || endMatches !== 1) throw new Error(`Duplicate generated markers ${name} in ${path}.`); const startIndex = source.indexOf(start); const endIndex = source.indexOf(end); if (endIndex < startIndex) throw new Error(`Generated markers ${name} are out of order in ${path}.`); return `${source.slice(0, startIndex)}${start}\n${body}\n${end}${source.slice(endIndex + end.length)}`; } ``` `buildGeneratedDocumentation` reads every source, creates the canonical schema once, applies all blocks in memory, and returns the three complete output strings. `updateGeneratedDocumentation` calls it once and only then writes all three files with `Promise.all`. The determinism test uses temporary source files containing one of every required marker; the committed-output synchronization assertion is added after the real reference page is written in Task 5. - [ ] **Step 4: Render the Semantic CSS factual blocks** Use stable sort order and these marker names: - `SEMANTIC-CSS-SEMANTIC-ELEMENTS` - `SEMANTIC-CSS-PROPERTIES` - `SEMANTIC-CSS-SYSTEM-VARIABLES` - `SEMANTIC-CSS-TEMPLATE-PARTS` - `SEMANTIC-CSS-DIAGNOSTICS` - `SEMANTIC-CSS-LIMITS` Required table columns: ```text Semantic elements: Element | Parents | Attributes | Known values | Roles Properties: Property | Category | Applies to | Inherits | Units | Known keywords System variables: Variable | Runtime value Template parts: Template | Name | Selector | Owner/condition | Allowed children Diagnostics: Code | Severity | Meaning | What to do Limits: Stage | Limit | Value ``` For template parts: ```ts const selector = part.binding.type === "primitive" ? `template-part[name="${part.name}"]` : `${part.binding.canonicalKind}[role~="${part.binding.token}"]`; ``` Primitive parts read allowed children from `TEMPLATE_PART_CHILD_KINDS_V1`; missing coverage throws with `