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.
This commit is contained in:
186
.claude/skills/illustrate-blueprint/SKILL.md
Normal file
186
.claude/skills/illustrate-blueprint/SKILL.md
Normal file
@@ -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/<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.
|
||||||
Reference in New Issue
Block a user