diff --git a/CLAUDE.md b/CLAUDE.md index ca9bb11..742be0f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,11 +73,11 @@ order until it carries the consumer's voice. ### Surface counts (current) -- **27 commands** under `commands/` (plus 2 internal `_translation-pipeline.md` +- **30 commands** under `commands/` (plus 2 internal `_translation-pipeline.md` / `_translation-strategy.md` includes — underscore prefix marks them as helpers reused by `translate-content`). -- **19 agents** under `agents/`. -- **18 skills** under `skills//SKILL.md`. +- **20 agents** under `agents/`. +- **20 skills** under `skills//SKILL.md`. - **1 runtime** under `assets/runtime/overlay-protocol.md` (the executable spec of the Base + Overlay loop). - **1 CI workflow** under `.github/workflows/lint.yml` with 3 lint scripts diff --git a/agents/workbook-module-composer.md b/agents/workbook-module-composer.md new file mode 100644 index 0000000..30c370e --- /dev/null +++ b/agents/workbook-module-composer.md @@ -0,0 +1,125 @@ +--- +name: workbook-module-composer +description: > + Composes ONE module ("lesson") of an interactive workbook into a self-contained HTML + fragment + manifest, for the `workbook-generate` fan-out (DOJ-4835). Reads a module's + text classes, applies the content-shape -> component mapping, and returns a namespaced + `.wb-module` fragment — NOT a full document. The orchestrator assembles fragments into + the single standalone workbook. Voice-neutral; the shared spec is the consistency + contract. +model: sonnet +tools: + - Read + - Grep + - Glob +--- + +# Workbook Module Composer Agent + +Compose a single module of a course workbook into a **content fragment** the +`workbook-generate` orchestrator can concatenate into one self-contained HTML +file. You handle exactly one module so the whole-course generation can fan out +in parallel without exhausting any one context window. + +Read the design system first: +`${CLAUDE_PLUGIN_ROOT}/skills/workbook-generate/SKILL.md` — the component kit, +the content-shape -> component mapping, the accessibility contract, the +"invariant frame vs. creative payload" split, and "the kit is a floor, not a +ceiling". This agent **follows that skill**; it does not redefine it. + +## Inputs (passed by the orchestrator) + +- `module_index` (required): the module's 1-based position in the course (the + `N` used for the ID namespace and the `mod-{N}` id). +- `module_path` (required): absolute path to the module directory. +- `text_classes` (required): the ordered list of `text-*.md` paths for this + module (already ordered by the orchestrator). +- `module_title` (required): the display title for the module. +- `design` (required): the resolved palette / typography / voice / spacing from + the consumer spec — consume these **only** as the template's CSS custom + properties (e.g. `var(--wb-accent)`). Never hardcode hex values. +- `components` (required): the enabled component catalog (the spec's + `workbooks.components`, or IDT's full built-in catalog when absent). +- `sibling_artifacts` (optional): `quiz-*.md` to fold in as predict-and-reveal + checks; `video-*.md` / `slides-*.html` / `challenge-*.md` to reference (never + embed). + +## Process + +1. Read the module's `text-*.md` (frontmatter + H1->H2->H3 / lists / tables / + code / prose) and any `quiz-*.md`. +2. Apply the skill's content-shape -> component mapping **as heuristics, not a + lookup table**. Vary the representation to fit each concept; do not default + to the same component for every section. When a concept needs a + visualization the kit lacks, author it directly with inline SVG/CSS within + the design tokens + accessibility contract (the kit is a floor, not a + ceiling). Reserve "compose from the tested kit" for fragile stateful JS. +3. Build the module fragment per the contract below. Every step is one `H2` + (typically); each interactive block must earn its place (felt understanding + or loop-closing export) — no capture-to-nowhere widgets. + +## Output contract (return EXACTLY these two blocks) + +Return a `MANIFEST` then a `FRAGMENT`, nothing else. Your final message IS the +data the orchestrator parses — no preamble. + +**1. `MANIFEST` — a fenced `json` block:** + +```json +{ + "moduleId": "mod-{N}", + "title": "{module_title}", + "steps": [ + { "id": "m{N}-step-1", "title": "..." }, + { "id": "m{N}-step-2", "title": "..." } + ], + "components_used": ["callout", "flow-diagram", "annotated-code", "predict-reveal"] +} +``` + +**2. `FRAGMENT` — a fenced `html` block** containing a single module chunk: + +```html +
+
+
{module_title}
+

...

+ +
+ +
+``` + +## Fragment rules (non-negotiable — the assembler depends on them) + +- **ID & attribute namespacing:** every `id`, `aria-controls`, + `aria-labelledby`, label `for`, and form-control `name` (radio/checkbox + groups) you emit is prefixed `m{N}-` (e.g. `m3-acc-1-panel`, + `name="m3-quiz-1"`). This guarantees global uniqueness — and correct + label/control associations and radio-group isolation — when fragments are + concatenated. The module wrapper id is `mod-{N}`. +- **Fragment only — no global chrome.** Emit the single `.wb-module` div and its + `.wb-step` sections. Do **NOT** emit ``, ``, `