Files
blueprints/.claude/skills/illustrate-blueprint/SKILL.md
Julien Calixte 5dff87a90d chore(skills): add illustrate-blueprint skill
Guided workflow for turning a blueprint-ontology blueprint into an
interactive illustration: read the source contract, author the typed data
module and bespoke specimen, register the pair, and verify the build.
2026-07-02 23:29:14 +02:00

187 lines
9.8 KiB
Markdown

---
name: illustrate-blueprint
description: >-
Illustrate a UI-pattern blueprint from the blueprint-ontology repo as an
interactive schematic in this app. Use when asked to illustrate, add, render,
or build the illustration for a blueprint — e.g. "illustrate the table
blueprint", "add the feed blueprint", "/illustrate-blueprint stack". Reads the
source contract from ../blueprint-ontology, authors the typed data module and a
bespoke specimen, registers the pair, and verifies the build.
---
# Illustrate a blueprint
Turn one blueprint from the `blueprint-ontology` repo into an interactive
illustration in this app (`blueprints`, deployed at https://blueprints.apoena.dev).
Read `CONTEXT.md` (ubiquitous language) and `DESIGN.md` (architecture) at the repo
root first if you haven't this session — they define the vocabulary (Illustration,
Viewer, Specimen, Readout, Companion, Function) used throughout.
## What an illustration is
The generic **Viewer** (`src/components/BlueprintViewer.vue`) renders every generic
part — title block, legend, function tabs, the full-view Readout, the composition
map, and the state machine — from a blueprint's **typed data**. You author two
things per blueprint and register them:
| File | What it is |
| --- | --- |
| `src/data/<camel>Blueprint.ts` | the `Blueprint` object — the contract as typed data (see the `Blueprint` type in `src/data/blueprint.ts`) |
| `src/specimens/<Pascal>Specimen.vue` | the **bespoke** static visual mock (left pane), with per-function highlighting |
| `src/data/blueprints.ts` | one line registering the `{ blueprint, specimen }` pair under its slug |
Registering in `blueprints.ts` is all that's needed — the Gallery (`/`) and the
route `/b/<slug>` both derive from the registry automatically. Do **not** hand-edit
the router or the Gallery.
> **This is interpretive authoring, not parsing.** `DESIGN.md` D1 deliberately
> rejects auto-generating from the ontology: the specimen must be hand-built, and
> the contract material (behaviors, invariants, failures, performance) has to be
> **redistributed per function**. Do the transcription by hand, faithfully.
## Reference examples
Study the closest existing pair before you start — copy its shape, don't reinvent:
| Kind | Data module | Specimen | Notes |
| --- | --- | --- | --- |
| **Feature** (set) | `src/data/listBlueprint.ts` | `src/specimens/ListSpecimen.vue` | reuses the shared `LOADSTATE_MACHINE` (`src/data/loadStateMachine.ts`) |
| **Feature** (set) | `src/data/gridBlueprint.ts` | `src/specimens/GridSpecimen.vue` | 2D tile layout |
| **Feature** (temporal) | `src/data/calendarBlueprint.ts` | `src/specimens/CalendarSpecimen.vue` | annotates one surface per function |
| **Capability** | `src/data/rememberMeBlueprint.ts` | `src/specimens/RememberMeSpecimen.vue` | defines a **bespoke** state machine + new `SmKind`s |
## Workflow
### 0 · Read the source blueprint
The source of truth is the sibling repo, default `../blueprint-ontology`
(confirm the path if it's not there). For a blueprint with slug `<slug>`, read all
three:
- `blueprints/<slug>/README.md` — the prose contract
- `blueprints/<slug>/<slug>.als` — the formal model (the real invariants/behaviors)
- `blueprints/<slug>/<slug>.test.als` — checks that pin the contract
The README anatomy is fixed: **Signature → UI snapshot → State machine → Behaviors
→ Invariants → Functional analysis → Critical performance → Failure modes → Formal
model**. Map it to the `Blueprint` type like this:
| README section | `Blueprint` field |
| --- | --- |
| `# Name<Item>` + intro one-liner | `name`, `tagline` |
| Signature / Standard composition | `sig` (`header`, `fields`, `types`), `role`, `extendsName`, `composesCount` |
| Relations | `related[]` (`{ name, relation }`) |
| UI snapshot (ASCII sketch) | informs the **Specimen** — not a data field |
| State machine | `stateMachine` — reuse `LOADSTATE_MACHINE` or author a bespoke one (see step 2) |
| Behaviors | distributed into `functions[].behaviors[]` (`{ sig, pre, eff }`) |
| Invariants → Core | top-level `coreInvariants[]` |
| Invariants → per-behavior | the owning `functions[].invariants[]` |
| Functional analysis (the table) | **`functions[]`** — one row = one `BlueprintFunction` |
| Critical performance | distributed into `functions[].perf[]` |
| Failure modes | distributed into `functions[].failures[]` |
| Formal model | rendered automatically from `slug` in the footer |
### 1 · Decide role and state-machine strategy
- **Role** — `"feature"` (a pattern the user interacts with: List, Grid, Calendar)
or `"capability"` (composes onto a host: Async, RememberMe, MFA). Set
`extendsName` (the base/host) and, for features that compose capabilities,
`composesCount`.
- **State machine** —
- Set features that compose Async + Paginated + Pullable (List, Grid, Feed,
Table…) **reuse** the shared machine: `import { LOADSTATE_MACHINE }` and set
`stateMachine: LOADSTATE_MACHINE`.
- A blueprint with its own lifecycle authors a **bespoke** `StateMachine`
(`nodes` with hand-placed `x/y/w/h`, `edges`, `initId`, `caption`, `field`).
Copy the layout approach from `rememberMeBlueprint.ts`.
- **If the bespoke machine has transition kinds not already in `SmKind`**, add
them to both `SmKind` **and** `SM_COLORS` in `src/data/blueprint.ts` (a color
per kind). Reuse an existing kind whenever the semantics match.
- A blueprint with no lifecycle simply omits `stateMachine`.
### 2 · Author `src/data/<camel>Blueprint.ts`
Export a `const <camel>: Blueprint`. Conform exactly to the `Blueprint` interface —
open `src/data/blueprint.ts` and follow it field by field. Start the file with a
header comment citing the ontology source (README + `.als`), like the existing
modules do.
Per function (one per Functional-analysis row):
- `id` — short, kebab/lowercase, **stable**; the specimen switches on it.
- `name` — the table's Function name.
- `fig``"01"`, `"02"`, … in table order (shown as `FIG.NN` in the specimen cap).
- `caps` — the blueprint(s)/capability that provide this function (drives the
composition-map links and the Readout's "source capability").
- `verb` — the table's Responsibility, tightened to one imperative sentence.
- `state``[name, type]` pairs for the state this function touches.
- `behaviors` / `invariants` / `failures` / `perf` / `notes` — pull the matching
lines from the README's Behaviors / Invariants / Failure modes / Critical
performance sections. Leave arrays empty (`[]`) when a function has none.
Fill `composition` when the pattern is a host-plus-capabilities or a capability
over a host: `caps[]` (nodes, mark the core one `core: true`), `funcs[]` (function
names in order), `links[]` (`[capId, functionName]` pairs). The composition map
only renders when `composition` is present.
### 3 · Author `src/specimens/<Pascal>Specimen.vue`
The Specimen is a **static** (never live) visual mock of the pattern's real UI,
bespoke to this blueprint. Contract:
- `<script setup lang="ts">`, props `defineProps<{ activeId: string }>()`.
- A `computed` `frame` with **one `case` per function `id`** plus a `default`
(the `"full"`/overview view). Function ids **must match** the data module — a
mismatch means selecting that tab highlights nothing.
- Each frame highlights the region that function owns using the shared callout
convention: the highlighted element gets the `hl` class and a `data-tag`
attribute (e.g. `◈ DISPLAY`), and the `.hl` / `.hl[data-tag]::after` CSS from an
existing specimen renders the dashed-amber outline + label. Copy that CSS block
verbatim.
- Use theme variables, not hard-coded colors: `var(--color-base-content)`,
`--color-accent` (amber highlight), `--color-secondary` (cyan ink),
`--color-error`, `--color-success`. Reuse the local aliases the existing
specimens define (`--ink`, `--amber`, `--cyan`, `--green`, `--red`, `--paper`,
`--line`, `--line-strong`).
- Pick the specimen shape that fits: features with a real screen mock its surface
and move the highlight per function (List/Grid/Calendar); a capability with
little UI of its own shows its few screens plus the machinery it owns
(RememberMe's device + secure-storage vault + state badges).
Keep it self-contained and scoped (`<style scoped>`); no new dependencies.
### 4 · Register the pair
In `src/data/blueprints.ts`: import the data export and the specimen component, then
add one entry to `REGISTRY` keyed by the **slug** (kebab-case, matching the ontology
folder and `blueprint.slug`). That's the only wiring required.
### 5 · Verify (all must pass)
```bash
pnpm build # vue-tsc type-check + vite build — catches Blueprint-type drift
pnpm lint # oxlint
pnpm fmt # oxfmt (pnpm fmt:check to verify only)
```
Then sanity-check it renders: `pnpm dev` and open `/b/<slug>` — every function tab
should highlight a region, the Readout should populate, and the composition map /
state machine (if present) should draw. The `/run` or `/verify` skills can drive
this if you want runtime confirmation.
## Conventions checklist
- Code style: **no semicolons, double quotes** (oxfmt enforces; see `.oxfmtrc.json`).
- Names: slug = kebab-case (`remember-me`); data export = camelCase (`rememberMe`);
data file = `<camel>Blueprint.ts`; specimen = `<Pascal>Specimen.vue`.
- Function `id`s are shared contract between the data module and the specimen —
keep them identical.
- Transcribe faithfully from the ontology; cite the source in the file header. The
ontology stays the source of truth — the data module is a transcription.
- Don't edit the router or Gallery; don't add "planned" cards (D3 — the Gallery
lists only illustrated blueprints).
- Scope discipline: add the new files + the registry line (+ `SmKind`/`SM_COLORS`
only if a new transition kind is genuinely needed). Don't refactor the Viewer or
existing blueprints as a side effect.