diff --git a/.claude/skills/illustrate-blueprint/SKILL.md b/.claude/skills/illustrate-blueprint/SKILL.md new file mode 100644 index 0000000..c0cb5a0 --- /dev/null +++ b/.claude/skills/illustrate-blueprint/SKILL.md @@ -0,0 +1,186 @@ +--- +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/Blueprint.ts` | the `Blueprint` object — the contract as typed data (see the `Blueprint` type in `src/data/blueprint.ts`) | +| `src/specimens/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/` 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 ``, read all +three: + +- `blueprints//README.md` — the prose contract +- `blueprints//.als` — the formal model (the real invariants/behaviors) +- `blueprints//.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` + 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/Blueprint.ts` + +Export a `const : 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/Specimen.vue` + +The Specimen is a **static** (never live) visual mock of the pattern's real UI, +bespoke to this blueprint. Contract: + +- `