Plan HTML Review App Design

Defines a manual workflow that converts canonical markdown specs into clean HTML review pages via an agentic LLM CLI, served by a dedicated Astro app at apps/plans. The generated HTML is a fast-comprehension layer over the source markdown — not a second source of truth.

View source markdown ↗ generated by claude-sonnet-4-6 · diagrams mermaid

A manual TypeScript script orchestrates a pluggable agentic CLI (claude, codex, or gemini) to convert markdown specs into light-mode HTML review pages, stored in an Astro content collection and deployed to Cloudflare Pages. The HTML is a fast-comprehension layer over the source markdown — not a second source of truth.

  • StatusApproved design
  • Date2026-05-25
  • Source of truthdocs/superpowers/specs
  • Target appfrontend/astro/apps/plans
  • Deploy targetCloudflare Pages · super-app-plans
  • Related ADRsADR-0026, ADR-0027

Key Decisions

Generation strategy

LLM-driven via agentic CLI; agent writes JSON + HTML files directly to a temp dir.

Matches how agentic CLIs are designed. Post-hoc validation against Zod is clearer than parsing structured stdout. Delegating visual restructuring to the LLM allows rich, context-aware output that no deterministic transformer could produce. See ADR-0026.

Rejected: stdout JSON parsing (fragile), markdown-to-HTML deterministic transform (loses the enrichment benefit).

Output shape

Metadata JSON + sibling HTML body fragment. Astro shell wraps the fragment with chrome.

Keeps git diffs readable. Lets reviewers open the raw HTML for spot-checking. The layout (hero, TOC, footer) is shared across all specs via a single Astro layout component.

Rejected: single JSON with embedded HTML string (unreadable diffs), MDX (too much tooling for generated content).

Component vocabulary

Closed catalog (COMPONENT_CATALOG.md). body_html may only use declared classes. Undeclared patterns → suggestion entry in JSON, not a lint pass.

Prevents per-spec bespoke CSS from accumulating. Suggestion loop ensures catalog evolves based on real content needs rather than speculation. See ADR-0027.

Rejected: open CSS (drifts uncontrollably), AI-generated inline styles (unscoped, breaks global layout).

Re-run policy

SHA-256 hash skip by default. --force regenerates unconditionally. Atomic writes via temp dir + rename().

Avoids burning tokens on unchanged specs. Atomic writes ensure a failed validation never leaves broken files in the content collection.

Rejected: timestamp-based skip (unreliable), always-regenerate (wasteful).

Generation Pipeline

Generation pipeline — from source markdown to deployed page.
Generation pipeline — from source markdown to deployed page.

Non-Goals

  • Do not move canonical spec content out of docs/superpowers/specs.
  • Do not auto-convert specs during astro dev / astro build.
  • Do not use MDX as the generated review format.
  • Do not build a CMS or editing interface.
  • Do not require AI-generated images for every spec.
  • Do not produce HTML deterministically — enrichment is delegated to the LLM (ADR-0026).

Astro App Shape

Content storage
Astro Content Collection at src/content/specs/. Each spec → <slug>.json + <slug>.html. Schema shared with generator via Zod.
Archive page (/)
Specs newest-first, status badges, tag filters, client-side MiniSearch search bar.
Detail page (/specs/<slug>)
Astro layout provides hero, sticky TOC (extracted from <h2>/<h3>), footer. Body is <Fragment set:html={bodyHtml} />.
Search engine
MiniSearch over public/search-index.json. Field-weighted: title ×5, key_decisions ×3, tags ×3, summary ×2, search_text ×1.
Index rebuild triggers
End of every generate run + astro:build:done integration hook (same buildSearchIndex() function).
CSS
Independent of apps/platform. Own design tokens, Tailwind v4. Light-mode only in v1. No dark mode.
Deployment
Cloudflare Pages · super-app-plans. Static build, no SSR adapter, no auth. noindex + /robots.txt Disallow.

Script & CLI Flags

Path: frontend/astro/apps/plans/scripts/generate-plan-html.ts (TypeScript via tsx)

FlagDefaultEffect
--with=<cli>claudePluggable CLI: claude | codex | gemini. Env fallback: PLAN_HTML_CLI.
--model=<id>claude-sonnet-4-6Claude model ID. Only applies to --with=claude (direct Anthropic API).
--allIterate all docs/superpowers/specs/*.md.
--forcefalseRegenerate even if source hash matches.
--diagrams=<mode>mermaidmermaid (client-side) | image (requires image-capable CLI).
--with-imagesfalseAllow illustrative image generation (requires image-capable CLI).
--variant=<name>Save as <slug>.v.<name>.json/html. Canonical slot unchanged. Use to compare CLI outputs.

Variant System

Variants let you keep multiple CLI outputs for the same spec side-by-side without collision.

Canonical slot
<slug>.json + <slug>.html — drives the archive, search index, and detail page hero.
Variant slot
<slug>.v.<name>.json/html — shown in collapsible <details> sections below the canonical on the detail page.
Archive / search
Canonical only. Variants are excluded from the index.
Usage
pnpm generate <path> --with=codex --variant=codex to generate a codex variant alongside the claude canonical.

Output Schema

One Zod schema (src/lib/spec-schema.ts) is shared by the generator script, the Astro content collection, and the prompt's auto-rendered TypeScript type.

FieldWho sets itNotes
titleLLMSpec H1 or inferred
statusLLMdraft | approved | superseded | implemented
dateLLMYYYY-MM-DD from spec or filename prefix
summaryLLM1–3 sentences, plain text. Archive card + meta description.
tagsLLM1–8 lowercase kebab-case tags
key_decisionsLLM1–8 sentence-length locked decisions
search_textLLM≥50 chars plain text, prioritises headings/decisions/definitions
suggested_componentsLLMPatterns the catalog doesn't cover. Default [].
related_adrs / new_adrsLLMADR numbers referenced or introduced by the spec
slugScriptSource filename without .md
source_pathScriptRepo-relative path to markdown
source_hashScriptSHA-256 of source markdown bytes (used for skip logic)
generated_atScriptISO timestamp of this run
generated_byScriptModel ID (e.g. claude-sonnet-4-6) or CLI name
diagram_formatScriptmermaid | image — recorded for re-run policy

Content Preservation Rule

  • Long prose may be compressed when meaning is preserved.
  • Detailed information may move lower or into <details> blocks, but must not disappear.
  • Every generated page links back to the source markdown via a main-branch GitHub URL.
  • The source markdown remains the place to audit exact wording.

Diagrams & Images

TypeDefaultOpt-in flagRequiresStorage
Diagrams — Mermaid✓ (default)Any CLIInline <pre class="mermaid">, rendered client-side
Diagrams — image--diagrams=imageImage-capable CLI (codex)public/spec-images/<slug>/diagram-<n>.png
Illustrative images--with-imagesImage-capable CLI (codex)public/spec-images/<slug>/illustration-<n>.png

Component Catalog (v1 — 20+ classes)

Defined in apps/plans/COMPONENT_CATALOG.md. Enforced by a post-generation lint step in the script. The suggestion loop grows the catalog based on real content needs.

Layout & Structure

  • spec-section — top-level TOC-anchored section
  • spec-lede — executive summary paragraph
  • spec-meta-grid — facet tiles near top of spec

Decisions & Callouts

  • spec-callout — with modifiers: --decision, --rationale, --risk, --warning, --open-question, --constraint
  • spec-decision-card — structured decision with chosen + reason + alternatives

Comparison & Data

  • spec-comparison-grid — side-by-side option comparison
  • spec-table — tabular data with header emphasis
  • spec-kv — key-value definition list
  • spec-rule-list — heuristic/metric rows with accent stripes
  • spec-category-grid — descriptive card grid for roles/modules

Flow & Timeline

  • spec-flow — linear process steps, grouped in one container
  • spec-timeline — phased rollout over time
  • spec-diagram — figure wrapper for Mermaid or image diagrams
  • spec-mockup — UI mockup escape hatch (custom CSS allowed inside)

Prompt Template

Path
frontend/astro/apps/plans/prompts/generate-plan.md
Format
Plain markdown with {{VAR}} substitution. One template for all CLIs.
Key variables
SPEC_PATH, OUTPUT_JSON_PATH, OUTPUT_HTML_PATH, SOURCE_MARKDOWN, COMPONENT_CATALOG, JSON_SCHEMA, DIAGRAM_MODE, IMAGES_ENABLED
JSON_SCHEMA
Auto-rendered from Zod via renderAgentSchemaAsTypeScript() — schema and prompt stay in sync automatically.
Evolution
Material changes are commits with rationale. After any change, regenerate ≥1 representative spec with --force and PR-review the diff before claiming it works.

Repo-local Skill

Path
.agents/skills/plan-html/SKILL.md, registered in .claude/skills.yaml
Trigger
"generate plan HTML", "render this spec", "regenerate spec pages", "publish plan"
Behavior
Prompt-then-run: ask before invoking the script. Default command: pnpm --filter @astro/plans generate <spec-path>. Use --all for multiple specs. Use --all --force after prompt/catalog/CSS changes.
Post-run
Surface suggested_components to the human. Spot-check the generated page at desktop + mobile widths.

Testing & Verification

  • Script run against the full docs/superpowers/specs corpus — Zod validation passes for every <slug>.json.
  • Generated HTML inspected for: title, summary, source link, navigation, all important sections (per content preservation rule).
  • Search index contains every canonical spec; client search finds at least one result per spec via title and via a key decision.
  • pnpm --filter @astro/plans build succeeds; astro:build:done hook regenerates search-index.json.
  • Browser review at desktop and mobile widths.
  • --with=codex --diagrams=image produces a diagram image with non-empty alt text and an <img> reference in the HTML.
  • Variant system: --variant=codex produces a .v.codex.json/html pair; detail page shows it in a collapsible section below canonical.

Locked Implementation Decisions

#DecisionResolutionADR
1Generation strategyLLM-driven via agentic CLI; agent writes files directlyADR-0026
2Output shapeMetadata JSON + sibling HTML fragment; Astro shell wraps itADR-0026
3CLI selectionPluggable via --with= flag, default claude
4Storage locationAstro Content Collection at src/content/specs/
5Re-run policySHA-256 hash skip + --force + atomic writes
6Schema authoritySingle Zod schema shared by script + collection + prompt
7Search engineMiniSearch over custom search-index.json
8DeploymentCloudflare Pages, static, no auth, noindex
9Source linkMain-branch GitHub URL, no commit pinning
10Component vocabularyClosed catalog + suggestion loop; manual adoptionADR-0027
11Diagrams defaultMermaid; image opt-in requires codex; alt text required
12Prompt templateSingle markdown file, {{VAR}} substitution, all CLIs
13Schema in promptAuto-rendered as TypeScript type from Zod
14Search index rebuildScript end-of-run + astro:build:done safety net
15Skill default behaviorPrompt-then-run before invoking script