Imported from oliyh/alluminate (
skills/alluminate/SKILL.md). Install upstream withnpx skills add oliyh/alluminate --skill alluminate. Copyright stays with the author.
Alluminate
This skill illuminates an Allium spec: it turns a .allium file into a static HTML document a non-spec-reader can pick up cold. The document reads like a product owner walking a newcomer through the system — a short orientation, then a sequence of chronological stories about how each feature actually gets used — with a full reference appendix underneath for lookup. Alluminate is a single-shot generator, not an iterative editing loop: run it, get a document, done. It does not modify the spec.
This skill is independent of the allium Claude Code plugin (it doesn't require that plugin's skills to be installed), but it is functionally pointless without an Allium spec to point it at, and much richer with the allium CLI available — see Prerequisites. Diagrams are authored directly as Mermaid rather than depending on an external diagram tool, so they're never something worth losing sleep over.
Voice
This is the part of the skill most responsible for whether the output is worth reading, and the part most likely to silently regress back toward a reference manual — because a reference manual is what falls out naturally if you just render JSON into tables. It isn't. Every run, hold the document to this bar:
You're new to this project. You've sat down with the product owner, who knows what the whole thing is for, how it works end to end, and how it fits into the wider ecosystem. They're walking you through the spec's features, one at a time, telling you what happens and why — not reading you a data dictionary.
Concretely, that means:
- Lead with what happens, not with what exists. A person, a device, or a clock does something; then a chain of consequences follows. Name the trigger before you name the rule.
- Second-hand narration, not clause syntax. Never show
when/requires/ensuresas Allium syntax in the Orientation/Cast/Journeys sections — say what it means. The raw clauses belong only in the reference appendix, where a reader has explicitly gone looking for the exact shape. - Explain consequences, not just state. "Once the last segment of the last block finishes, the session becomes
completeand stays there — there's no way back into an in-progress session once you've finished it" beats "status: complete (terminal)". - Findings are part of the story, not a report bolted onto it. A caveat about a state with no way out belongs in the sentence describing that state.
- Once Cast names an actor, that's its name for the rest of the document — no synonym drift. If Cast introduces the reader-facing role name "Participant" for the spec's
Accountentity, every later mention — Orientation, every Journey, every diagram lifeline/node label, every mockup caption — says "Participant," never a different word for the same actor introduced nowhere else (a past run drifted into calling the same actor "Runner" partway through, a name Cast never established). This most often creeps in as a side effect of routing around the Mermaid alias-collision gotcha (naming a sequence-diagram actorParticipantbreaks the parser — see mermaid-gotchas.md): the fix for that is a safe internal diagram id with the real name kept as the display label (actor P as Participant), never a substitute word adopted for the diagram and then left to leak into the surrounding prose. If a domain synonym genuinely reads better than the spec's own entity name, that's Cast's reader-facing-role-name decision to make once, explicitly (see html-structure.md) — not something that appears only in some sections. Before shipping, skim the assembled document for any actor-referring word that isn't one of Cast's declared names and fix every instance, the same whole-set pass Cast's own lead-with-function rule already requires (see html-structure.md) — not just the one a reviewer happened to point at.
Before (the regression this rewrite is fixing — do not produce this):
Session
Fields:
device: Device,programme: Programme,status: waiting | active | complete,current_block_index: Integer(whenstatus ∈ {waiting, active}),current_segment_index: Integer(whenstatus = active)... Transitions:waiting → active,active → waiting,active → complete(terminal)
After (what this skill now produces):
Once a participant picks today's programme, the app creates a Session to track their progress through it. It starts out
waiting— queued up, nothing running yet — untilSessionInitialisedfires and it flips toactive, where it stays for the rest of the run: current block, current segment, and pace guidance all live on the Session while it's in this state. When the last segment of the last block finishes, it becomescompleteand stays there — there's no declared way back into an in-progress session once it's done. ⚠ One gap worth knowing about: the spec never says what status a Session starts in when it's first created (see.created()at line 621) —waitingis the only state with an inbound edge, so that's almost certainly the intent, but it isn't written down.
Same underlying facts, told as what happens rather than what's declared. This is the standard for Orientation, Cast, and every Journey — the reference appendix is allowed to be a data dictionary, because that's explicitly what it's for (see html-structure.md's section order).
Prerequisites
- An Allium spec — the
.alliumfile to document (search the project to find it if not specified). - The
alliumCLI, forcheck/model/plan/analyse— optional. Install viabrew install alliumorcargo install allium-cli(see juxt.github.io/allium/installation). If unavailable, fall back to reading the spec directly for entity/rule/config/surface structure (see the language reference); note the fallback once in Orientation. Note also that neithermodelnorplancarries rule declaration text (when/requires/ensures) or surfaceprovidestext even when the CLI is available — Journeys and the rules/surfaces reference sections always come from reading the spec directly (see Phase 3). - The Wireframer skill (
github.com/agilek/wireframer-skill) — optional, for surface mockups. If not installed, skip mockups and note how to install it.
Missing either degrades the document, it never blocks generation. Only a missing or unreadable spec file is a hard stop.
Diagrams don't have a prerequisite of their own: they're authored directly as Mermaid from the model/spec data alluminate already has, not delegated to an external tool — see Phase 1, step 5. allium-diagram (a companion tool bundled with Allium's non-marketplace VS Code extension, not the core CLI) is essentially never installed in a Claude Code environment, and that's fine — it's not worth checking for, mentioning to the user, or noting its absence in the document. If it does happen to be on PATH, feel free to use it as a cross-check, but hand-authored Mermaid is the normal path, not a degraded fallback.
Process
Phase 0 — Resolve inputs and output location
- Locate the spec file.
- Find the repo root (
git rev-parse --show-toplevelfrom the spec's directory; if that fails, walk upward forpackage.json/Cargo.toml/pyproject.toml/go.mod/.git; if none, use the spec's parent directory). - Output path:
<repo-root>/docs/alluminate/<spec-basename>/index.html— see Output conventions below. - If
<repo-root>/docs/already exists and looks like an unrelated docs site (has its ownindex.html,_config.yml,mkdocs.yml,docusaurus.config.*outsidedocs/alluminate/), stop and confirm the output location with the user rather than nesting into someone else's site. Skip this check on regeneration oncedocs/alluminate/<spec-basename>/already exists from a prior run.
Phase 1 — Validate and gather structured data
Steps 1–4 are independent of each other (none consumes another's output) — issue them as concurrent Bash calls in a single message rather than one after another; on a spec of any real size this is the cheapest wall-clock win in the whole pipeline. Read the results back in this order once they're all in:
allium check <spec>— capture diagnostics. A failing check does not block generation; failing diagnostics become badges attached at the point they concern (see diagnostic attribution), and Orientation states plainly that the spec does not currently validate.allium model <spec>— JSON: entity shapes, constraints, state machines.allium plan <spec>— JSON: test obligations.allium analyse <spec>— structural/process findings (deadlock,invariant_risk,missing_producer,unreachable_trigger,dead_transition,conflict). Only run this once the spec has reached a level of maturity where analysis is meaningful — a spec with only entities and transition graphs but no witnessing rules or surfaces is too coarse foranalysefindings to be worth surfacing yet. Its exact output shape isn't guaranteed; the attribution mechanism in diagnostic-attribution.md is designed to work whether it returns structured JSON or plain diagnostic text.- Author diagrams directly as Mermaid, by hand, from the
modelJSON (and, wheremodelisn't available, the direct spec read from Phase 3): a whole-spec relationship diagram (graph/flowchart, entities as nodes, references and rule-mediated interactions as edges), plus one state diagram (stateDiagram-v2) per entity that declares atransitions { }block. Pick whichever Mermaid diagram type actually fits what's being shown rather than defaulting to one shape everywhere — a sequence diagram for a multi-actor journey handoff, an ER-flavoured graph for a dense relationship web, a state diagram only where there's a real transition graph. Ifallium-diagramhappens to be onPATH, it's fine to use as a cross-check, but it's not expected to be present and its absence is never worth mentioning to the user or noting in the document. Before embedding any diagram, check it against mermaid-gotchas.md — a<pre class="mermaid">block passing through the browser's HTML parser first, and a note/participant syntax that's stricter than it looks (including an actor/participant alias that collides with Mermaid's own reserved keywords, e.g. naming oneParticipant), are both reliable sources of a rendered "Syntax error in text" that a plain read of the diagram won't catch.scripts/validate-mermaid.mjschecks against a realmermaid.parse()when network access is available.
If a data source from steps 1–4 is missing or a command errors, skip it, note its absence once in Orientation — first time only, don't repeat the caveat per-source in later sections — and continue.
As soon as model (or, absent the CLI, a direct read of each surface declaration) is in hand, every surface's mockup prompt can be assembled — that's all Phase 5 needs, and none of it depends on Phase 2's tables, Phase 3's journeys, or Phase 4's diagnostics. Dispatch Phase 5's fan-out now, in the background, and let it render while Phases 2–4 proceed on the main thread — see Phase 5 below for the mechanism. This is the second-biggest wall-clock win after step 1's parallel CLI calls: Wireframer invocations are each a full generation pass, and running N of them one after another for N surfaces is pure serial waste when nothing forces that order.
Phase 2 — Render the reference-appendix fragments
Run node scripts/render-tables.mjs --model <model.json> --plan <plan.json> --spec <spec-file> --out <scratch-dir>/fragments/. This produces config.html, entities.html, variants.html, and rules.html — deterministic HTML tables built from the confirmed-JSON model/plan output, destined for the reference appendix (never for Orientation, Cast, or Journeys — those are narrated by hand, see Phase 3). It also builds and writes a construct span index ({name, kind, startLine, endLine} per entity/rule/surface/variant/config/invariant block, derived from the raw spec text), reused in Phase 4.
allium plan carries test obligations, not rule declarations — there is no when/requires/ensures text in either model or plan output. rules.html's fragment is therefore always a placeholder; the reference appendix's actual Rules section, like Journeys, is authored by reading the spec directly (Phase 3 covers this reading once, for both). If allium model/plan weren't available in Phase 1, the same applies to config.html/entities.html/variants.html too — build the equivalent tables by hand, using the language reference to identify constructs.
Phase 3 — Map entrypoints and journeys
This is the phase that produces the document's main content, and the one most in need of care — see journeys.md for the full method:
- Read every
rule'swhen/requires/ensuresand everysurface'sprovides, directly from the spec text. - Classify each distinct trigger as actor-initiated, external-system-initiated, scheduled, internal, or orphaned. The first three (plus orphaned ones) are entrypoints.
- Walk each entrypoint forward through the rules it fires, the branches its
requiresclauses create, and the further triggers itsensuresproduces, until the chain terminates — this is one journey. - Order journeys by dependency, not drama: origin journeys that create something from nothing first, journeys that consume what those create next, journeys depending on both after that, the payoff journeys last, edge/admin always last — never "exciting happy path first, setup explained afterward." Get this wrong and later journeys casually reference entities the reader hasn't been shown how to get yet.
- Read the assembled sequence back — journeys.md Step 5 — and fix any cross-journey link that turns out to point forward at something not yet introduced without reading as a deliberate teaser.
The same close reading also produces the reference appendix's Rules section (Phase 2's rules.html fragment is a placeholder precisely because this reading has to happen anyway) and the Cast section's entity introductions — do this once and reuse it, rather than re-reading the spec separately for each section.
Phase 4 — Attach diagnostics inline
Walk the check and analyse findings from Phase 1 and attach each to the specific journey step, cast card, and/or reference-appendix construct it concerns, using the two-tier mechanism in diagnostic-attribution.md. A finding almost always has a journey that passes through the construct it names — attach it there, in the narration, as well as in the reference appendix; only findings with no attributable construct at all go in the Validation appendix. An unreachable_trigger/missing_producer finding on an entrypoint is what makes a journey orphaned (see journeys.md Step 1) — render that as the journey's own gap statement, not a separate badge appended after the fact. Never show the reader a raw finding type or JSON blob — phrase every caveat as a plain-language statement about the behaviour, following the translation strategies in diagnostic-attribution.md (e.g. a deadlock finding becomes "This state has no declared way out — if a Candidacy reaches screening and the background check never resolves, it can never leave," not type: deadlock).
Phase 5 — Generate mockups
Dispatch this phase immediately after Phase 1, not after Phase 4 — nothing here depends on the reference tables, journeys, or diagnostics, only on the surface declarations Phase 1 already has. For each surface, generate a wireframe via the Wireframer skill as an independent background subagent, all launched together, so N surfaces cost roughly one Wireframer invocation's wall-clock time instead of N. Full fan-out mechanism, prompt template, and output-relocation contract in wireframer-integration.md. Primary placement is inline in whichever journey reaches that surface, with a secondary lookup copy in the reference appendix's Surfaces section. Skip gracefully with a note if Wireframer isn't installed — do not attempt to auto-install it, and don't bother dispatching anything for this phase at all in that case.
Before Phase 6, confirm every dispatched subagent has actually completed (check outstanding background tasks if a completion notification hasn't arrived for one yet) — assembly needs to know, per surface, whether it has a real mockup or a skip. Phases 2–4 are close-reading work with no hard ceiling on how long they take, so in practice the mockups are almost always done first; only wait on this specifically as a last step, don't poll for it throughout.
Phase 6 — Assemble
Copy references/document-template.html and fill in every <!-- FILL: ... --> and <!-- REPEAT: ... --> block:
- Orientation (domain only — the whole-spec diagram, if captured; never mention the document itself or what Phase 1 couldn't get, that's Phase 6 item 6)
- Cast
- Journeys (Phase 3's ordered output, opening with a journey table of contents, carrying Phase 4's inline caveats — margin sidenote for ordinary findings, full-width gap-callout only for genuinely story-breaking ones — and Phase 5's mockup exhibits, always rendered open)
- Reference appendix — collapsed
<details>: Config, Entities and variants, Rules, Surfaces, Diagrams - Validation appendix (only findings Phase 4 couldn't attribute to any construct)
- About this document — always present: what generated it, which
alliumcommands ran and at what version, the diagnostic/finding totals, whether rule text came from the CLI or a direct spec read, and whether mockups were generated (with a pointer to add them if not) — diagrams are always hand-authored Mermaid, so there's nothing to disclose about their source
Skip a section only where its own comment in the template says it's conditional. Escape any free text pulled from spec comments or prose before interpolating it into HTML — spec authors write comments assuming a reader, not an HTML sink. See html-structure.md for the rationale behind this structure and the CSS token set, and the Voice section above before writing a word of Orientation, Cast, or Journeys prose.
Before writing each cast card, journey, diagram, or mockup exhibit, check it against its contract in component-schemas.md — what fields that component type needs to carry (an actor accent, a hierarchy depth, a dependency tier) to render consistently with every other instance of the same component in the same document. scripts/validate-components.mjs can check a staged JSON draft against these contracts without any external dependency, if drafting one is useful before committing to the HTML.
Phase 7 — Report
Print the resolved output path and one summary line: N journeys (N entrypoints, N orphaned), N entities, N rules, N surfaces, N mockups generated (M skipped), N check diagnostics, N analyse findings — all attributed. Do not git add or commit the generated files — tell the user they're new/untracked and ready for review.
Output conventions
<repo-root>/docs/alluminate/<spec-basename>/index.html
<repo-root>/docs/alluminate/<spec-basename>/mockups/<surface-name>/index.html (one directory per surface)
Example: spec/leadout.allium → docs/alluminate/leadout/index.html, with e.g. docs/alluminate/leadout/mockups/InstructorChannelPage/index.html.
Why this layout:
- Everything generated for one spec lives under one directory — trivial to regenerate cleanly (
rm -rf docs/alluminate/<spec-basename>/and re-run). docs/is the conventional location for publishable static content (GitHub Pages serves/docsby default) — matches "static file in repo, possibly published."- Namespacing under
docs/alluminate/avoids colliding with a repo's existing unrelateddocs/(see Phase 0, step 4). index.htmlas the entry filename makes each spec's directory directly browsable and linkable.
Regeneration overwrites the whole docs/alluminate/<spec-basename>/ directory. This is generated documentation, not a hand-edited file — the first time you generate a document for a given spec, tell the user this explicitly so manual edits to a previous run don't get lost silently.
Document structure and styling
See html-structure.md for the section order, content rules per section, and CSS token set (light/dark via prefers-color-scheme — there is no artifact theme-toggle for a static file, so data-theme stamping does not apply here); the literal markup to fill in is document-template.html. Follow the same design posture as the bundled artifact-design skill even though this isn't a Claude Artifact: ground the palette in the domain, avoid a generic AI look, orient before narrating, narrate before tabulating, overflow-x: auto on wide tables/diagrams rather than page-level horizontal scroll, encode status with icon + label, never color alone.
Diagrams embed the Mermaid source authored in Phase 1, rendered client-side via Mermaid.js from a CDN — this, along with Wireframer's wired-elements/Google Fonts CDN dependency, means the document needs internet access to render fully. The template detects an actual Mermaid load failure (onerror on the CDN <script> plus a typeof mermaid check) and only then reveals a warning banner — never state unconditionally that the document "requires an internet connection," since most of the time it just rendered and that sentence would be false the moment the reader reads it. Vendoring these assets for offline use is out of scope.
Entrypoints and journeys
See journeys.md for how entrypoints are identified from the spec's triggers and surfaces, how each is walked into a chronological journey, and how journeys are ordered and written. This is the core of what makes the document narrative rather than a reference manual — read it before Phase 3 of a fresh run.
Diagnostic attribution
See diagnostic-attribution.md for the severity taxonomy (reusing the dataviz skill's fixed status palette — do not invent a new one), the two-tier name-based / line-based attribution mechanism, and how a finding attaches to both a journey step and its reference-appendix construct.
Wireframe mockups
See wireframer-integration.md for the per-surface prompt template, output relocation, primary placement inline in Journeys with a secondary reference-appendix copy, always rendered open with equal billing alongside diagrams, and how mockups are surfaced without visually clashing with alluminate's own theme.
Component schemas
See component-schemas.md for the JSON Schema contract each recurring component (cast card, journey, journey step, diagram, mockup exhibit) is expected to satisfy, and scripts/validate-components.mjs for a dependency-free validator against those contracts. Not wired into the pipeline automatically — a checklist and an available spot-check, not an enforced gate, until Journeys authoring becomes JSON-first the way Phase 2's render-tables.mjs already is for config/entities/variants.
Interaction with the Allium plugin
Alluminate is a separate, standalone skill/plugin, but it's designed to sit alongside the allium Claude Code plugin and its skills (elicit, distill, tend, weed, propagate) if installed:
- A spec worth alluminating is usually one that has moved past the earliest coarse stage (entities and transition graphs, but no rules or surfaces yet) —
elicit/distillgrow specs to that point, and a spec at that stage has no entrypoints to find yet either. weedchecks spec-code alignment; alluminate does not — it documents the spec as written, not whether the implementation matches it.tendevolves specs. Re-run alluminate after spec changes to refresh the document; there's no auto-sync.propagategenerates tests from the sameallium model/allium planJSON alluminate reads. The two are independent outputs of the same source data.
None of the above are required for alluminate to run — only the allium CLI itself (for check/model/plan/analyse) and, optionally, the Wireframer skill; diagrams are self-sufficient, hand-authored Mermaid.
Boundaries
- Alluminate does not modify the
.alliumfile. Read-only with respect to the spec. - Alluminate does not verify spec-code alignment.
- Modular specs (
use "..." as x): alluminate documents only the given file's own declarations. Qualified references (oauth/Session) render as plain text, not expanded from the imported spec. - Alluminate is standalone and user-invoked. It does not auto-trigger on spec edits.
Limitations
allium check/allium analyseoutput format is not guaranteed by this skill's design — the attribution mechanism is built to degrade to line-based matching if structured fields aren't present.- Wireframer's output shape (single file vs. several linked screens) can vary by prompt; always normalise into the per-surface mockup directory regardless of what it produces.
- Generated mockups and diagrams require internet access to render (CDN-hosted Mermaid.js,
wired-elements, Google Fonts). - Journey construction (Phase 3) is a close-reading task, not a mechanical JSON transform — a spec with tangled or heavily cross-referenced trigger chains may need real judgement calls on where one journey ends and another begins; when a call is close, say so briefly rather than presenting an arbitrary split as obviously correct.