Files
Reactive-Resume/docs/superpowers/plans/2026-07-29-semantic-css-reference-and-docs-generation.md
2026-07-30 12:39:15 +02:00

1389 lines
48 KiB
Markdown

# 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<typeof z.toJSONSchema>`.
- 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<object>`.
- 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<Record<string, readonly string[]>>` 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<Record<string, readonly string[]>>;
};
```
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<Record<string, DiagnosticReference>>;
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<Record<Template, TemplateSemanticManifest>>
```
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<DocumentationPaths>): Promise<void>`.
- 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:START --><!-- SEMANTIC-CSS-ELEMENTS:END --><!-- SEMANTIC-CSS-ELEMENTS:START --><!-- SEMANTIC-CSS-ELEMENTS:END -->",
"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 = `<!-- ${name}:START -->`;
const end = `<!-- ${name}: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 `<template>:<part>`. Alias parts display “canonical node” instead of pretending that a template-part wrapper exists. Include owner placement, section types, columns, and last-item conditions when present.
Label registry values “Known keywords” and add this sentence below the table:
> Known keywords are completion hints, not a complete value grammar; use the property examples and value rules above for accepted numbers, colors, functions, and shorthands.
- [ ] **Step 5: Render both schema outputs from one schema object**
Serialize the full guide block as:
```ts
const fullSchemaBlock = [
"```json /schema.json lines expandable",
JSON.stringify(schema, null, "\t"),
"```",
].join("\n");
```
Replace `RESUME-JSON-SCHEMA` markers in `docs/guides/json-resume-schema.mdx`.
For the skill reference, walk object properties and array item schemas depth-first. Emit:
```markdown
# Reactive Resume Schema Reference
Generated by `pnpm docs:gen` from `resumeDataSchema`. Do not edit this file directly.
Canonical schema: https://rxresu.me/schema.json
## Required top-level fields
`picture`, `basics`, `summary`, `sections`, `customSections`, and `metadata`
## Field catalog
| Path | Type | Required | Constraints and default | Description |
| --- | --- | --- | --- | --- |
```
Rules for the walker:
- Object properties append `.name`.
- Array items append `[]`.
- `anyOf`/`oneOf` types are joined with ` or ` and each branch is traversed.
- Required comes from the containing object's `required` array.
- Constraints include `enum`, `minimum`, `maximum`, `minLength`, `maxLength`, and JSON-stringified `default`.
- Escape Markdown pipes and newlines in descriptions.
- Prevent duplicate rows with a `Set` keyed by path plus rendered type.
- Sort object property names in their schema-defined order and union branches in emitted order.
- [ ] **Step 6: Add package dependencies and the root command**
In `tooling/package.json`, add:
```json
"docs:gen": "tsx semantic-css/generate-reference.ts"
```
and workspace dependencies for `@reactive-resume/pdf` and `@reactive-resume/schema`.
In the root `package.json`, replace `docs:semantic-css` with:
```json
"docs:gen": "pnpm --filter @reactive-resume/tooling docs:gen && pnpm --filter server docs:gen"
```
- [ ] **Step 7: Run focused generator tests and typechecks**
Run:
```bash
pnpm install --lockfile-only
pnpm --filter @reactive-resume/tooling test -- semantic-css/generate-reference.test.ts
pnpm --filter @reactive-resume/tooling typecheck
pnpm --filter server test -- src/openapi/generator.test.ts
pnpm --filter server typecheck
pnpm exec turbo boundaries
```
Expected: PASS against temporary marker fixtures.
- [ ] **Step 8: Commit the generator implementation**
Stage only this task's changes:
```bash
git add package.json tooling/package.json tooling/semantic-css/generate-reference.ts tooling/semantic-css/generate-reference.test.ts docs/guides/json-resume-schema.mdx
git add -p pnpm-lock.yaml
git diff --cached --check
git diff --cached
git commit -m "feat: generate documentation artifacts"
```
---
### Task 5: Write and Generate the Author-Facing References
**Files:**
- Rewrite: `docs/guides/semantic-css-reference.mdx`
- Modify: `docs/guides/json-resume-schema.mdx`
- Regenerate: `skills/resume-builder/references/schema.md`
- Regenerate: `docs/spec.json`
- Modify: `docs/docs.json`
- Modify: `tooling/semantic-css/generate-reference.test.ts`
**Interfaces:**
- Consumes: the marker contracts and `pnpm docs:gen` from Task 4.
- Produces: the canonical public Semantic CSS reference route and all synchronized generated artifacts.
- [ ] **Step 1: Write the Semantic CSS page around the generated markers**
Use the approved ten-section structure:
1. Semantic CSS in one minute
2. Selector grammar
3. Semantic element catalog
4. Cascade and values
5. Property reference
6. PDF behavior
7. Template-specific selectors
8. Diagnostics and limits
9. Copy-paste recipes
10. Unsupported capabilities and portability checklist
Include this complete portable example and mark it for compiler verification:
````mdx
<!-- SEMANTIC-CSS-EXAMPLE:valid -->
```css
@version 1;
:root {
--accent: #2563eb;
}
section-heading {
color: var(--accent);
border-bottom: 1pt solid var(--accent);
padding-bottom: 3pt;
}
section[type="experience"] > section-items > item + item {
margin-top: 8pt;
}
region[placement="sidebar"] section-heading {
font-size: 10pt;
text-transform: uppercase;
}
```
````
Include the complete selector forms:
```css
* { opacity: 1; }
section { margin-bottom: 8pt; }
#experience { break-before: page; }
[role~="primary-text"] { font-weight: 700; }
section[type="experience"] > section-items > item:first-child { margin-top: 0; }
field[name="company"], field[name="position"] { color: #111827; }
section:is([type="experience"], [type="education"]) { break-inside: avoid; }
item:not(:last-child) { margin-bottom: 6pt; }
```
Document all six supported attribute matchers: `=`, `~=`, `|=`, `^=`, `$=`, and `*=`. Attribute selector flags, custom classes, and pseudo-elements are rejected. Document `:root`, `:is()`, `:where()`, `:not()`, `:first-child`, `:last-child`, `:only-child`, `:nth-child()`, and `:nth-of-type()` with these exact restrictions:
- `:root`, `:first-child`, `:last-child`, and `:only-child` take no arguments.
- `:is()`, `:where()`, and `:not()` require a selector list.
- `:nth-child()` and `:nth-of-type()` accept integer `An+B`, `odd`, and `even`.
- `:nth-child()` may use an `of <selector-list>` clause.
- `:nth-of-type()` does not accept an `of` clause.
- A compound selector may contain only one semantic type selector.
- Selector functions, selector lists, selector length, and combinators are bounded by the generated limits.
Document media queries as comma-separated OR branches and `and`-combined features. Supported features are `width`, `min-width`, `max-width`, `height`, `min-height`, `max-height`, and `orientation: portrait|landscape`. Dimension values accept finite numbers with `pt`, `px`, `in`, `mm`, `cm`, `vw`, `vh`, `em`, or `rem`; omitted units resolve as points. Media rules evaluate against authored PDF dimensions, may nest up to the generated limit, and cannot contain `size`.
State the cascade and value behavior precisely:
- `!important` wins first, then specificity, then later source order.
- For a selector list, the highest-specificity branch that actually matches the node is used.
- `:where()` contributes zero specificity; `:is()` and `:not()` use their most specific nested selector.
- `inherit` reads the semantic parent's resolved value.
- `initial` removes the authored value.
- `unset` inherits inherited properties and removes non-inherited properties.
- `revert` restores the template/builder base value.
- Author custom properties inherit through the semantic tree; `var()` supports nested fallbacks.
- Missing variables without fallbacks and variable cycles are errors.
- `--resume-*` variables are read-only runtime values; author variables must use another prefix.
- Units resolve against PDF points, authored page dimensions, the root font size, or the current/parent font size as appropriate.
State PDF and lifecycle behavior precisely:
- `display: none` hides an existing semantic node; Semantic CSS never deletes source data.
- `order` changes stable sibling render order, with original semantic order as the tie-breaker.
- `break-before`, `break-inside`, `-resume-min-presence-ahead`, `orphans`, `widows`, and `-resume-fixed` map to bounded React PDF behavior.
- `size` applies only to `page` outside `@media`; one or two lengths create a custom page size.
- Semantic CSS cannot create, re-parent, or duplicate arbitrary nodes; fixed content repeats only an existing semantic node.
- Invalid editable source remains saved, while preview and export keep the last source that compiled and passed PDF preflight.
- Template-part selectors are optional and must be guarded with `resume[template="..."]` when portability matters.
End with an explicit unsupported checklist: class selectors, pseudo-elements, CSS Grid, `@import`, `@font-face`, `@supports`, arbitrary at-rules, `url()`, external/local assets, font loading, scripts, interaction states, animations, transitions, filters, gradients, general `box-shadow`/`text-shadow`, browser layout APIs, and node creation are unsupported. Note that picture shadows are available only through `-resume-shadow-color` and `-resume-shadow-width`.
Mark one invalid complete example:
````mdx
<!-- SEMANTIC-CSS-EXAMPLE:invalid INVALID_SELECTOR -->
```css
@version 1;
section::before {
content: "Unsupported";
}
```
````
Explain that pseudo-elements cannot create PDF nodes.
- [ ] **Step 2: Add generated sections at their lookup locations**
Place each exact marker pair once:
```md
<!-- SEMANTIC-CSS-SEMANTIC-ELEMENTS:START -->
<!-- SEMANTIC-CSS-SEMANTIC-ELEMENTS:END -->
```
Repeat for properties, system variables, template parts, diagnostics, and limits. Keep manual explanations immediately before or after their table, not inside generated blocks.
- [ ] **Step 3: Add exact author recipes**
Each recipe starts with `@version 1;` and is independently copyable:
```css
/* One section type */
@version 1;
section[type="experience"] section-heading {
color: #0f766e;
}
```
```css
/* One resume-specific node */
@version 1;
#experience-item-uuid {
break-inside: avoid;
}
```
```css
/* Sidebar content */
@version 1;
region[placement="sidebar"] {
background-color: #f8fafc;
padding: 18pt;
}
```
```css
/* Rich-text list row versus content */
@version 1;
list-item {
gap: 4pt;
}
list-marker {
color: var(--resume-primary-color);
}
list-item-content {
line-height: 1.35;
}
```
```css
/* Authored page dimensions */
@version 1;
page {
size: 210mm 297mm;
}
```
```css
/* Pagination */
@version 1;
section {
-resume-min-presence-ahead: 72pt;
}
item {
break-inside: avoid;
}
```
```css
/* Template decoration */
@version 1;
resume[template="azurill"] template-part[name="timeline-line"] {
background-color: #94a3b8;
}
```
```css
/* PDF dimension query */
@version 1;
@media (max-width: 600pt) {
region[placement="sidebar"] {
padding: 12pt;
}
}
```
Prefix each complete recipe fence with `<!-- SEMANTIC-CSS-EXAMPLE:valid -->`; leave only deliberately incomplete syntax fragments unmarked.
- [ ] **Step 4: Test examples directly from the page**
First add the committed-output synchronization test:
```ts
it("keeps every committed generated document synchronized", async () => {
const before = await readTargets(defaultDocumentationPaths);
await updateGeneratedDocumentation(defaultDocumentationPaths);
expect(await readTargets(defaultDocumentationPaths)).toEqual(before);
});
```
Then extract fenced CSS immediately following:
```text
<!-- SEMANTIC-CSS-EXAMPLE:valid -->
<!-- SEMANTIC-CSS-EXAMPLE:invalid CODE -->
```
For valid examples:
```ts
const result = compileStylesheet({ languageVersion: 1, text: example.source });
expect(result.program, example.label).not.toBeNull();
expect(result.diagnostics.filter(({ severity }) => severity === "error"), example.label).toEqual([]);
```
For invalid examples:
```ts
expect(result.diagnostics.map(({ code }) => code)).toContain(example.expectedCode);
```
- [ ] **Step 5: Add the schema guide marker and docs navigation**
Wrap only the full JSON code fence in:
```md
<!-- RESUME-JSON-SCHEMA:START -->
<!-- RESUME-JSON-SCHEMA:END -->
```
In `docs/docs.json`, insert:
```json
"guides/using-custom-styles",
"guides/semantic-css-reference",
"guides/using-ai-in-the-builder"
```
- [ ] **Step 6: Regenerate every artifact**
Run:
```bash
pnpm docs:gen
pnpm docs:gen
git diff --check -- docs/guides/semantic-css-reference.mdx docs/guides/json-resume-schema.mdx skills/resume-builder/references/schema.md docs/spec.json
```
Expected: the first run updates all four artifact groups; the second run is byte-for-byte idempotent as proved by the determinism and synchronization tests. `docs/spec.json` contains version `5.2.4` and server URL `https://rxresu.me/api/openapi`.
- [ ] **Step 7: Run focused documentation verification**
Run:
```bash
pnpm --filter @reactive-resume/tooling test -- semantic-css/generate-reference.test.ts
pnpm --filter @reactive-resume/tooling typecheck
pnpm exec markdownlint-cli2 docs/guides/semantic-css-reference.mdx docs/guides/json-resume-schema.mdx skills/resume-builder/references/schema.md
```
Expected: PASS.
- [ ] **Step 8: Commit the author references and generated artifacts**
```bash
git add docs/guides/semantic-css-reference.mdx docs/guides/json-resume-schema.mdx skills/resume-builder/references/schema.md docs/spec.json docs/docs.json tooling/semantic-css/generate-reference.test.ts
git diff --cached --check
git diff --cached
git commit -m "docs: publish the Semantic CSS author reference"
```
---
### Task 6: Link the Reference from Custom Styles
**Files:**
- Modify: `apps/web/src/features/resume/stylesheet/editor.tsx`
- Modify: `apps/web/src/features/resume/stylesheet/editor.test.tsx`
**Interfaces:**
- Consumes: the fixed public URL `https://docs.rxresu.me/guides/semantic-css-reference`.
- Produces: one translated, accessible link in shared editor chrome on desktop and mobile.
- [ ] **Step 1: Write the failing desktop and mobile link assertions**
Add a helper:
```ts
const referenceName = /browse the semantic css language reference.*opens in new tab/i;
const expectReferenceLink = (root: HTMLElement) => {
const link = within(root).getByRole("link", { name: referenceName });
expect(link).toHaveAttribute("href", "https://docs.rxresu.me/guides/semantic-css-reference");
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute("rel", "noopener noreferrer");
};
```
In a desktop test, render `StylesheetEditorShell` and call `expectReferenceLink(container)`.
In the existing mobile-sheet test, after opening the sheet:
```ts
expectReferenceLink(sheet);
```
- [ ] **Step 2: Run the focused editor test and verify failure**
Run:
```bash
pnpm --filter web test -- src/features/resume/stylesheet/editor.test.tsx
```
Expected: FAIL because the reference link is absent.
- [ ] **Step 3: Add the local help hint**
Import `BookOpenIcon` from the existing icon package and render this immediately after `StylesheetToolbar` and before the editor:
```tsx
<p className="flex items-center gap-1.5 text-muted-foreground text-xs">
<BookOpenIcon aria-hidden="true" className="shrink-0" />
<span>
<Trans>Not sure what to write?</Trans>{" "}
<a
className="text-primary underline underline-offset-4"
href="https://docs.rxresu.me/guides/semantic-css-reference"
target="_blank"
rel="noopener noreferrer"
>
<Trans>Browse the Semantic CSS language reference.</Trans>
<span className="sr-only">
{" "}
(<Trans>opens in new tab</Trans>)
</span>
</a>
</span>
</p>
```
Do not add props, a URL constant, or a shared component. The existing `editorChrome` reuse makes the hint available in both desktop and mobile focus mode.
- [ ] **Step 4: Run focused web tests and typecheck**
Run:
```bash
pnpm --filter web test -- src/features/resume/stylesheet/editor.test.tsx
pnpm --filter web typecheck
```
Expected: PASS.
- [ ] **Step 5: Commit only the help-hint hunks**
Both files already have worktree changes, so use interactive staging and inspect the cached patch:
```bash
git add -p apps/web/src/features/resume/stylesheet/editor.tsx apps/web/src/features/resume/stylesheet/editor.test.tsx
git diff --cached --check
git diff --cached
git commit -m "feat: link Custom Styles to Semantic CSS reference"
```
---
### Task 7: Final Non-Chrome Verification
**Files:**
- Verify only; fix failures in the owning task's files.
**Interfaces:**
- Consumes: all previous tasks.
- Produces: one clean, repeatable documentation generation and focused repository gates.
- [ ] **Step 1: Verify generation is deterministic and committed**
```bash
pnpm docs:gen
git diff --exit-code -- docs/guides/semantic-css-reference.mdx docs/guides/json-resume-schema.mdx skills/resume-builder/references/schema.md docs/spec.json
```
Expected: PASS with no generated diff.
- [ ] **Step 2: Run focused tests**
```bash
pnpm --filter @reactive-resume/schema test -- src/resume/json-schema.test.ts
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 @reactive-resume/tooling test -- semantic-css/generate-reference.test.ts
pnpm --filter server test -- src/openapi/generator.test.ts src/http/app.test.ts src/services/stylesheet-preflight.test.ts
pnpm --filter web test -- src/features/resume/stylesheet/editor.test.tsx
```
Expected: PASS.
- [ ] **Step 3: Run typechecks and package boundaries**
```bash
pnpm --filter @reactive-resume/schema typecheck
pnpm --filter @reactive-resume/resume typecheck
pnpm --filter @reactive-resume/pdf typecheck
pnpm --filter @reactive-resume/tooling typecheck
pnpm --filter server typecheck
pnpm --filter web typecheck
pnpm exec turbo boundaries
```
Expected: PASS.
- [ ] **Step 4: Run non-mutating formatting and documentation checks**
```bash
pnpm exec biome check packages/schema/src/resume/json-schema.ts packages/schema/src/resume/json-schema.test.ts apps/server/src/static/schema.ts apps/server/src/openapi apps/server/src/services/stylesheet-preflight.ts packages/resume/src/stylesheet packages/pdf/src/semantic packages/pdf/src/server.tsx tooling/semantic-css apps/web/src/features/resume/stylesheet/editor.tsx apps/web/src/features/resume/stylesheet/editor.test.tsx
pnpm exec markdownlint-cli2 docs/guides/semantic-css-reference.mdx docs/guides/json-resume-schema.mdx skills/resume-builder/references/schema.md docs/superpowers/plans/2026-07-29-semantic-css-reference-and-docs-generation.md
git diff --check
```
Expected: PASS. Do not run `pnpm check`, because it mutates the entire worktree.
- [ ] **Step 5: Inspect scope without using Chrome**
```bash
git status --short
git diff --stat
git log --oneline -8
```
Confirm:
- No Chrome or Playwright browser session was used.
- Unrelated pre-existing changes remain present and were not reverted.
- Generated artifacts are synchronized.
- The public link, nav entry, production OpenAPI URL, and root version are exact.