-
Notifications
You must be signed in to change notification settings - Fork 0
DOJ-4835: optional per-module fan-out orchestration for workbook-generate #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dbejarano820
merged 2 commits into
main
from
daniel/doj-4835-workbook-fanout-orchestration
Jun 2, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| <div class="wb-module" id="mod-{N}" role="group" aria-label="{module_title}"> | ||
| <section class="wb-step" id="m{N}-step-1" role="region" aria-label="..."> | ||
| <div class="wb-chapter"><div class="wb-eyebrow">{module_title}</div></div> | ||
| <h2>...</h2> | ||
| <!-- kit components / bespoke inline SVG, all theme-variable-driven --> | ||
| </section> | ||
| <!-- more steps... --> | ||
| </div> | ||
| ``` | ||
|
|
||
| ## 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 `<html>`, `<head>`, `<style>`, | ||
| `<script>`, the `.wb-progress` bar, the `.wb-nav` table of contents, or the | ||
| `.wb-module__nav` footer. Those are global and the orchestrator owns them | ||
| (the footer is added by the assembler so the "Module N of M" arc is correct). | ||
| - **No hardcoded brand.** Use the CSS custom properties (`var(--wb-accent)`, | ||
| `var(--wb-text)`, `var(--wb-font-body)`, etc.) — never literal hex or font | ||
| names. Brand lives in the assembled document's `:root`. | ||
| - **Accessibility:** each step is `role="region"` with an `aria-label`; | ||
| interactive controls are focusable; quiz/diff/comparison state is conveyed by | ||
| text + icon, not color alone; honour `prefers-reduced-motion` patterns from | ||
| the kit. | ||
| - **In-memory only / standalone:** no `localStorage`, no `postMessage`, no CDN | ||
| runtime deps. Any image is inlined or relative. | ||
| - **Voice-neutral:** no brand voice, no "Dojo"-isms, no momentum copy. Leave the | ||
| forward-hook teaser to the overlay (the assembler/overlay owns it). | ||
|
|
||
| ## Anti-patterns | ||
|
|
||
| - Emitting a full HTML document, a `<style>`/`<script>` block, the nav, the | ||
| progress bar, or the module footer — all assembler-owned. | ||
| - Un-namespaced ids (collide on concatenation). | ||
| - Hardcoded colors/fonts instead of the design tokens. | ||
| - Mapping every section to the same component (monotony) — vary to fit. | ||
| - Capture-to-nowhere widgets; freeform stateful JS. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -250,6 +250,48 @@ visualization is encouraged. The only thing to avoid is fragile, untested | |
| **stateful JS** widgets — those should be added to the kit deliberately (and | ||
| tested), not improvised per page. | ||
|
|
||
| ## Generation at scale: per-module fan-out (orchestration) | ||
|
|
||
| A per-course workbook can span many modules × text classes — more than one | ||
| context window composes well in a single pass. So generation is **size-gated**: | ||
|
|
||
| - **Small course** (≈ 2 modules or fewer) → compose **linearly** in one pass. | ||
| - **Larger course** → **map → reduce**: dispatch one `workbook-module-composer` | ||
| subagent **per module** (in parallel, via the standard Task/Agent tool — never | ||
| a session-specific orchestration tool; the command must stay portable), then | ||
| the orchestrator **assembles** the returned fragments into the single | ||
| self-contained workbook. | ||
|
|
||
| This is also a quiet win for variety: independent per-module composition resists | ||
| the "I already used a chart, reuse it" monotony a single pass can fall into. | ||
|
|
||
| ### The fragment contract (what binds it together) | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [NIT] ⚪ P4 (nit) — Escaped quotes ( |
||
| Each composer returns a **MANIFEST** (`{ moduleId, title, steps[], components_used }`) | ||
| plus a **FRAGMENT**: one `<div class="wb-module" id="mod-{N}">` of namespaced | ||
| `.wb-step`s and kit components — **not** a full document. Rules: | ||
|
|
||
| - **ID & attribute namespacing:** every `id` / `aria-controls` / | ||
| `aria-labelledby` / label `for` / form-control `name` is prefixed `m{N}-` | ||
| (module index) so fragments concatenate without id collisions, broken label | ||
| associations, or cross-module radio-group bleed. | ||
| - **Fragment only:** no `<html>/<head>/<style>/<script>`, no `.wb-progress`, no | ||
| `.wb-nav`, **no `.wb-module__nav` footer**. Those are global and the | ||
| orchestrator owns them — the footer is added at assembly so the "Module N of M" | ||
| arc is correct. | ||
| - **No hardcoded brand:** consume design tokens (`var(--wb-accent)`, …), never | ||
| literal hex/fonts. Voice-neutral; the forward-hook teaser stays an overlay | ||
| slot. | ||
|
|
||
| ### Why it stays consistent (no stitched-together feel) | ||
|
|
||
| The shared `instruction-bundle-spec.yaml` (brand / a11y / kit) is the binding | ||
| contract handed to every composer, so fragments are visually uniform by | ||
| construction. The **assembler** then runs a normalization pass — verify each | ||
| fragment uses only allowed components, IDs are namespaced + globally unique, | ||
| a11y roles are present — and runs the variety/fit self-check on the assembled | ||
| whole. Drift is bounded by the frame, not left to chance. | ||
|
|
||
| ## Consuming the consumer spec (`instruction-bundle-spec.yaml`) | ||
|
|
||
| The workbook's brand + structural vocabulary comes from the consumer's | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[NIT] ⚪ P4 (nit) — Escaped quotes (
\"Module N of M\") are used within the parentheses, which will display the backslashes literally in markdown editors. Standard quotes can be used directly here.