@radicool/throughline 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/adapters/codex/AGENTS.md +2 -1
- package/adapters/codex/prompts/component-builder.md +80 -2
- package/adapters/codex/prompts/design-system-audit.md +20 -0
- package/adapters/codex/prompts/document-component.md +26 -0
- package/adapters/codex/prompts/icon-system-builder.md +10 -2
- package/adapters/codex/prompts/repository-builder.md +14 -0
- package/adapters/codex/prompts/retrofit-planner.md +21 -1
- package/adapters/codex/prompts/storybook-chromatic-builder.md +52 -7
- package/adapters/codex/prompts/token-builder.md +16 -0
- package/adapters/codex/prompts/token-sheet-builder.md +15 -1
- package/adapters/codex/prompts/token-sync-layer.md +14 -7
- package/adapters/cursor/.cursor/commands/document-component.md +26 -0
- package/adapters/cursor/.cursor/rules/component-builder.mdc +80 -2
- package/adapters/cursor/.cursor/rules/design-system-audit.mdc +20 -0
- package/adapters/cursor/.cursor/rules/icon-system-builder.mdc +10 -2
- package/adapters/cursor/.cursor/rules/repository-builder.mdc +14 -0
- package/adapters/cursor/.cursor/rules/retrofit-planner.mdc +22 -2
- package/adapters/cursor/.cursor/rules/storybook-chromatic-builder.mdc +52 -7
- package/adapters/cursor/.cursor/rules/token-builder.mdc +16 -0
- package/adapters/cursor/.cursor/rules/token-sheet-builder.mdc +15 -1
- package/adapters/cursor/.cursor/rules/token-sync-layer.mdc +14 -7
- package/adapters/generic/AGENTS.md +2 -1
- package/adapters/generic/commands/document-component.md +26 -0
- package/adapters/generic/skills/component-builder/SKILL.md +80 -2
- package/adapters/generic/skills/design-system-audit/SKILL.md +20 -0
- package/adapters/generic/skills/icon-system-builder/SKILL.md +10 -2
- package/adapters/generic/skills/repository-builder/SKILL.md +14 -0
- package/adapters/generic/skills/retrofit-planner/SKILL.md +21 -1
- package/adapters/generic/skills/storybook-chromatic-builder/SKILL.md +52 -7
- package/adapters/generic/skills/token-builder/SKILL.md +16 -0
- package/adapters/generic/skills/token-sheet-builder/SKILL.md +15 -1
- package/adapters/generic/skills/token-sync-layer/SKILL.md +14 -7
- package/package.json +1 -1
- package/references/agent-routing.md +70 -0
- package/references/component-doc-archetypes.md +86 -0
- package/references/component-doc-schema.md +136 -0
- package/references/figma-component-standards.md +24 -0
- package/references/figma-scripting.md +62 -0
- package/references/manifest-schema.md +27 -5
- package/references/sync-adapters.md +5 -0
- package/scripts/README.md +11 -0
- package/scripts/build-docs-digest.mjs +74 -0
- package/scripts/docs-check.mjs +103 -0
- package/scripts/lib/doc-record.mjs +54 -0
|
@@ -51,6 +51,22 @@ and why large `WRAP` grids time out. For a simple verification read, prefer the
|
|
|
51
51
|
dedicated `figma_get_variables` tool (it handles dynamic-page correctly and
|
|
52
52
|
resolves aliases with `resolveAliases: true`) over a hand-written script.
|
|
53
53
|
|
|
54
|
+
**Execution model — sequential architect → figma-executor with model routing.**
|
|
55
|
+
If your host supports subagent dispatch, plan the token architecture once with
|
|
56
|
+
one **architect** dispatch (deep tier) — it reads existing Figma state and emits
|
|
57
|
+
a transcription-grade spec in stable identifiers (collection/variable *names*,
|
|
58
|
+
never nodeIds) — then build the variables with **figma-executor** dispatches
|
|
59
|
+
(balanced tier), strictly sequentially: preflight `figma_get_status` and never
|
|
60
|
+
run two Figma-touching subagents at once (the single bridge is concurrency-1).
|
|
61
|
+
Each executor finalizes by build-verify-then-replace with a programmatic
|
|
62
|
+
read-back (`figma_get_variables`, not a screenshot); gate the result with a
|
|
63
|
+
**reviewer** pass. **Keep the human checkpoint between tiers** (the PAUSE in
|
|
64
|
+
Steps 2–4) — subagents run continuously within a tier, but you pause for the
|
|
65
|
+
human between them. Route per
|
|
66
|
+
`.throughline/references/agent-routing.md`; never parallelize Figma
|
|
67
|
+
work. If your host has no subagent dispatch, build and verify each tier inline,
|
|
68
|
+
sequentially, as the steps below describe.
|
|
69
|
+
|
|
54
70
|
## Step 1 — Brainstorm the structure (before building anything)
|
|
55
71
|
|
|
56
72
|
Run the protocol in `.throughline/references/brainstorm-before-build.md`. **First establish
|
|
@@ -33,6 +33,20 @@ swatch/type grids — exactly the layouts that hit the two worst traps: the
|
|
|
33
33
|
single-call `layoutWrap = "WRAP"` timeout on big grids (build manual rows or split
|
|
34
34
|
across calls instead). Also run the single-bridge-instance preflight before writing.
|
|
35
35
|
|
|
36
|
+
**Execution model — sequential architect → figma-executor with model routing.**
|
|
37
|
+
If your host supports subagent dispatch, plan the sheet layout once with one
|
|
38
|
+
**architect** dispatch (deep tier) — a lighter plan than token-builder's, since
|
|
39
|
+
the tokens already exist; it emits a stable-identifier layout spec (which
|
|
40
|
+
collections/sections, how much per-token detail) — then build the page with
|
|
41
|
+
**figma-executor** dispatches (balanced tier), strictly sequentially: preflight
|
|
42
|
+
`figma_get_status`, concurrency-1, `WIP:` frame + build-verify-then-replace with
|
|
43
|
+
a programmatic read-back (spot-check that the swatches resolved to their bound
|
|
44
|
+
variables via `figma_get_variables`, not just a screenshot), gated by a
|
|
45
|
+
**reviewer** visual pass. Route per
|
|
46
|
+
`.throughline/references/agent-routing.md`; never parallelize Figma
|
|
47
|
+
work. If your host has no subagent dispatch, build and verify the page inline,
|
|
48
|
+
sequentially, as the steps below describe.
|
|
49
|
+
|
|
36
50
|
## Step 1 — Brainstorm the layout (lightly)
|
|
37
51
|
|
|
38
52
|
Run `.throughline/references/brainstorm-before-build.md`, but keep it light — this is a
|
|
@@ -141,7 +155,7 @@ refresh the "Last updated" date instead of creating a second Cover.)
|
|
|
141
155
|
## Step 3 — Checkpoint
|
|
142
156
|
|
|
143
157
|
Show the user the Foundations page and the Cover page. Sequential review (this is
|
|
144
|
-
a Figma-authoring skill
|
|
158
|
+
a Figma-authoring skill). Iterate on layout/styling if they want
|
|
145
159
|
changes. Then update the manifest: `sheets.built` = `true`, append
|
|
146
160
|
`token-sheet-builder` to `completedSkills`.
|
|
147
161
|
|
|
@@ -139,13 +139,18 @@ register the platform, transform group, format, and `outputReferences`
|
|
|
139
139
|
flattens). Web adapters emit `:root`/`.dark` (or `[data-theme]`); the shadcn
|
|
140
140
|
adapter also emits a Tailwind preset.
|
|
141
141
|
|
|
142
|
-
**Execution model — subagent
|
|
143
|
-
platform is
|
|
144
|
-
|
|
142
|
+
**Execution model — subagent dispatch with model routing.** Generating each
|
|
143
|
+
platform's output is independent and verifiable. If your host supports subagent
|
|
144
|
+
dispatch, dispatch **one `code-executor` per adapter** — each produces its
|
|
145
145
|
platform's files and verifies them (the config builds, the expected files
|
|
146
|
-
appear, references resolve
|
|
147
|
-
before combining.
|
|
148
|
-
|
|
146
|
+
appear, references resolve for web / flatten for native) — then a **`reviewer`**
|
|
147
|
+
to check each before combining. Choose each subagent's model from its role tier
|
|
148
|
+
per `.throughline/references/agent-routing.md` (`code-executor` → fast,
|
|
149
|
+
`reviewer` → balanced), and only dispatch once each adapter's spec is complete
|
|
150
|
+
enough to transcribe. If your host has no subagent dispatch, generate and verify
|
|
151
|
+
each adapter inline instead. For a single platform, run inline either way. This
|
|
152
|
+
is a code-gen stage, so these subagents may run in parallel — unlike Figma
|
|
153
|
+
authoring, which is always sequential.
|
|
149
154
|
|
|
150
155
|
## Step 4 — Build and place outputs
|
|
151
156
|
|
|
@@ -169,7 +174,9 @@ icons (`icons.built` true):
|
|
|
169
174
|
- **Custom icons** — these the repo owns, so generate them: export the custom
|
|
170
175
|
SVGs from Figma, optimize, and componentize via SVGR into `packages/ui` (or a
|
|
171
176
|
dedicated icons package). This is real code generation and rides the same
|
|
172
|
-
|
|
177
|
+
**`code-executor` (fast) + `reviewer` (balanced)** routing and PR-review as
|
|
178
|
+
token output (SVGR transforms are the textbook mechanical op the fast tier is
|
|
179
|
+
for) — see the Step 4 execution model.
|
|
173
180
|
|
|
174
181
|
## Step 5 — Full regeneration + rename detection (the safety net)
|
|
175
182
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radicool/throughline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Build a complete design system end to end — author in Figma, sync tokens to code, generate Storybook. Usable from Claude Code, Cursor, Codex, or any AGENTS.md agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Agent routing — choosing a model per subagent
|
|
2
|
+
|
|
3
|
+
**Claude-only.** This reference governs subagent dispatch, a Claude Code
|
|
4
|
+
capability. Hosts without subagent dispatch (Codex, generic AGENTS.md) run the
|
|
5
|
+
work inline on their single model and ignore this file.
|
|
6
|
+
|
|
7
|
+
## Why route
|
|
8
|
+
|
|
9
|
+
Do the *thinking* on the best model and the *doing* on a cheap one. Deciding a
|
|
10
|
+
component's variant matrix, token architecture, or adapter strategy is reasoning
|
|
11
|
+
— it earns the best model. Running the resulting spec — transcribing code,
|
|
12
|
+
placing Figma nodes, emitting adapter output — is mechanical, and a cheap model
|
|
13
|
+
does it well **once the plan is complete**.
|
|
14
|
+
|
|
15
|
+
## The tier ladder
|
|
16
|
+
|
|
17
|
+
Relative, never a hardcoded model name — installers have different plans, so we
|
|
18
|
+
name a *capability tier* and resolve it against the models actually available.
|
|
19
|
+
|
|
20
|
+
- `fast` — cheapest/fastest tier. Transcription-grade work from a complete spec.
|
|
21
|
+
- `balanced` — mid tier. Judgment, integration, review scaled to risk.
|
|
22
|
+
- `deep` — most capable tier available. Planning, architecture, hard reasoning.
|
|
23
|
+
|
|
24
|
+
### Recommended mapping (Anthropic)
|
|
25
|
+
|
|
26
|
+
| Tier | Recommended model |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `fast` | Haiku |
|
|
29
|
+
| `balanced` | Sonnet |
|
|
30
|
+
| `deep` | Opus (or the most capable model you have) |
|
|
31
|
+
|
|
32
|
+
**Override in one place:** edit this table for your plan. Everything downstream
|
|
33
|
+
reads tiers, not model names.
|
|
34
|
+
|
|
35
|
+
### Resolution + fallback
|
|
36
|
+
|
|
37
|
+
At dispatch, resolve the role's tier to a concrete model **from the models you
|
|
38
|
+
actually have**, then pass it explicitly (an omitted model inherits the session
|
|
39
|
+
model — often the most expensive — which defeats routing). If a tier's model is
|
|
40
|
+
unavailable, **collapse to the nearest lower tier you have**; `deep` always maps
|
|
41
|
+
to the most capable model available. An installer with only one model degrades
|
|
42
|
+
to that model everywhere — routing becomes a no-op, never a failure.
|
|
43
|
+
|
|
44
|
+
## Roles → tiers
|
|
45
|
+
|
|
46
|
+
| Agent | Tier | Concurrency | Role |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `code-executor` | `fast` | parallel-safe | Transcribe code/adapter output from a complete spec; verify its own build. |
|
|
49
|
+
| `reviewer` | `balanced` (scale to risk) | parallel-safe | Spec-compliance + quality gate; code-diff or Figma-visual mode. |
|
|
50
|
+
| `architect` | `deep` | 1 | Plan a stage; read Figma read-only; emit a transcription-grade spec in stable identifiers (names, never nodeIds). |
|
|
51
|
+
| `figma-executor` | `fast`→`balanced` (default **`balanced`** for a real component build; `fast` only for trivial mechanical ops) | **1 (bridge-locked)** | Resolve names→nodeIds at run time; build into a `WIP:` frame; verify via `COMPONENT_SET` read-back (not screenshot-only); finalize by build-verify-then-replace and reap `WIP:` debris. |
|
|
52
|
+
|
|
53
|
+
## The spec-completeness gate
|
|
54
|
+
|
|
55
|
+
Dispatch an executor on `fast` **only when the spec is transcription-grade** —
|
|
56
|
+
complete enough that execution is copying, not deciding. Turn count beats token
|
|
57
|
+
price: a cheap model on a vague task takes 2–3× the turns and costs more. If the
|
|
58
|
+
spec is incomplete, run the work inline on the stronger model instead.
|
|
59
|
+
|
|
60
|
+
## Escalation
|
|
61
|
+
|
|
62
|
+
A `BLOCKED` executor is re-dispatched **one tier up**, never the same model
|
|
63
|
+
unchanged. If still blocked at `deep`, escalate to the human.
|
|
64
|
+
|
|
65
|
+
## Concurrency
|
|
66
|
+
|
|
67
|
+
Code-gen roles are parallel-safe. Figma work is **not**: the figma-console
|
|
68
|
+
bridge is a single live connection with global selection/current-page state, so
|
|
69
|
+
the entire Figma surface is concurrency-1. Route Figma work through
|
|
70
|
+
sequential subagents — model routing yes, parallelism never.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Component documentation archetypes
|
|
2
|
+
|
|
3
|
+
The best-practice knowledge layer for the documentation generation pipeline
|
|
4
|
+
(`component-builder` Step: *Author the documentation record*). When authoring a
|
|
5
|
+
component's `.doc.json`, match the component to the nearest **archetype** below and
|
|
6
|
+
seed its `dos`, `donts`, `accessibility`, `whenToUse`, and `whenNotToUse` from that
|
|
7
|
+
entry, then specialize to the target framework and confirm with the user. Stamp
|
|
8
|
+
`provenance` as `best-practice` (or `w3c-apg` for the accessibility block) for
|
|
9
|
+
anything sourced here.
|
|
10
|
+
|
|
11
|
+
These are **seeds, not gospel** — the user's approval and the actual built
|
|
12
|
+
component override them. Sources: W3C ARIA Authoring Practices Guide (roles +
|
|
13
|
+
keyboard), Material 3, Shopify Polaris (content/usage), IBM Carbon (usage).
|
|
14
|
+
|
|
15
|
+
## Button
|
|
16
|
+
|
|
17
|
+
- **whenToUse:** trigger an action or event (submit, confirm, open a dialog).
|
|
18
|
+
- **whenNotToUse:** navigation between pages/URLs — use a Link.
|
|
19
|
+
- **dos:** lead the label with a verb; keep one primary (highest-emphasis) button
|
|
20
|
+
per view; keep labels short (≤ ~3 words).
|
|
21
|
+
- **donts:** don't use a button for navigation; don't stack multiple primary
|
|
22
|
+
buttons; don't disable without telling the user why.
|
|
23
|
+
- **accessibility (w3c-apg):** role `button`; Enter and Space activate; an
|
|
24
|
+
icon-only button needs an `aria-label`; disabled buttons are not focusable.
|
|
25
|
+
|
|
26
|
+
## Input / text field
|
|
27
|
+
|
|
28
|
+
- **whenToUse:** collect a single line of free-form text.
|
|
29
|
+
- **whenNotToUse:** choosing from a fixed set (use Select/Radio); long multi-line
|
|
30
|
+
text (use Textarea).
|
|
31
|
+
- **dos:** always pair with a visible label; show format hints as helper text;
|
|
32
|
+
reserve space for error text to avoid layout shift.
|
|
33
|
+
- **donts:** don't use placeholder text as the only label; don't validate on every
|
|
34
|
+
keystroke before first blur.
|
|
35
|
+
- **accessibility (w3c-apg):** every input has a programmatically associated
|
|
36
|
+
`<label>`; error state sets `aria-invalid` and links the message via
|
|
37
|
+
`aria-describedby`.
|
|
38
|
+
|
|
39
|
+
## Checkbox / radio / toggle
|
|
40
|
+
|
|
41
|
+
- **whenToUse:** checkbox/toggle for independent on/off; radio for one-of-many.
|
|
42
|
+
- **whenNotToUse:** a single either/or action that takes effect immediately with no
|
|
43
|
+
save (prefer a toggle) vs. a form choice (prefer radio/checkbox).
|
|
44
|
+
- **dos:** label the control, not just the group; make the label clickable.
|
|
45
|
+
- **donts:** don't use a radio group for multi-select; don't use a toggle for
|
|
46
|
+
choices that only apply after a separate Save.
|
|
47
|
+
- **accessibility (w3c-apg):** roles `checkbox` / `radio` / `switch`; Space
|
|
48
|
+
toggles; radio groups navigate with arrow keys; state exposed via
|
|
49
|
+
`aria-checked`.
|
|
50
|
+
|
|
51
|
+
## Card
|
|
52
|
+
|
|
53
|
+
- **whenToUse:** group related content and actions about a single subject.
|
|
54
|
+
- **whenNotToUse:** primary page layout scaffolding; a bare list of text.
|
|
55
|
+
- **dos:** make the primary action obvious; keep one main call-to-action per card.
|
|
56
|
+
- **donts:** don't nest cards more than one level; don't make the whole card AND an
|
|
57
|
+
inner button separately clickable in conflicting ways.
|
|
58
|
+
- **accessibility:** if the whole card is a link/button, it needs an accessible
|
|
59
|
+
name; don't bury interactive controls that keyboard users can't reach in order.
|
|
60
|
+
|
|
61
|
+
## Modal / dialog
|
|
62
|
+
|
|
63
|
+
- **whenToUse:** interrupt for a focused task or a decision that blocks the flow.
|
|
64
|
+
- **whenNotToUse:** non-critical messages (use an inline banner or toast).
|
|
65
|
+
- **dos:** trap focus while open; return focus to the trigger on close; provide an
|
|
66
|
+
explicit close affordance.
|
|
67
|
+
- **donts:** don't stack modals; don't put long scrolling forms in a small modal.
|
|
68
|
+
- **accessibility (w3c-apg):** role `dialog` with `aria-modal="true"`; labelled by
|
|
69
|
+
its title (`aria-labelledby`); Escape closes; focus is trapped within.
|
|
70
|
+
|
|
71
|
+
## Badge / chip / tag
|
|
72
|
+
|
|
73
|
+
- **whenToUse:** short status, count, or category label (badge); a removable/
|
|
74
|
+
selectable token (chip).
|
|
75
|
+
- **whenNotToUse:** interactive primary actions (use a Button).
|
|
76
|
+
- **dos:** keep text to a word or two; bind color to a semantic tone token.
|
|
77
|
+
- **donts:** don't rely on color alone to convey status — include text/icon.
|
|
78
|
+
- **accessibility:** a removable chip's remove control needs an accessible name
|
|
79
|
+
(e.g. "Remove <label>"); status conveyed with text, not color only (WCAG 1.4.1).
|
|
80
|
+
|
|
81
|
+
## Fallback (unlisted archetype)
|
|
82
|
+
|
|
83
|
+
For a component without an entry above: derive `dos`/`donts` from its role and
|
|
84
|
+
built structure, source the `accessibility` block from the matching W3C APG
|
|
85
|
+
pattern, and mark everything for user confirmation. Add a new archetype section
|
|
86
|
+
here once the component's guidance stabilizes.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Component documentation record
|
|
2
|
+
|
|
3
|
+
The canonical, folder-resident source of truth for a component's usage
|
|
4
|
+
documentation. One JSON file per component at
|
|
5
|
+
`design-system/docs/components/<ComponentName>.doc.json`. Every other
|
|
6
|
+
documentation surface (Figma component description, the doc card, Storybook
|
|
7
|
+
autodocs/MDX, the AI digest) is a **projection** rendered from this file — never
|
|
8
|
+
authored independently.
|
|
9
|
+
|
|
10
|
+
It lives in the working folder from the moment a component is built (exactly like
|
|
11
|
+
`design-system.json`), so it exists during the Figma-only *folder* stage, before
|
|
12
|
+
any repo. The path stays `design-system/docs/` across folder→repo; files never
|
|
13
|
+
move.
|
|
14
|
+
|
|
15
|
+
## Why JSON (not YAML)
|
|
16
|
+
|
|
17
|
+
The plugin's scripts are zero-dependency and there is no YAML parser available, so
|
|
18
|
+
the record is JSON to keep `docs:check` and `docs:digest` able to parse it
|
|
19
|
+
deterministically. JSON is equally machine-legible for AI consumers.
|
|
20
|
+
|
|
21
|
+
## Schema
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"name": "Button",
|
|
26
|
+
"summary": "Triggers an action or event.",
|
|
27
|
+
"description": "A clickable control that initiates an action…",
|
|
28
|
+
"whenToUse": ["Submitting a form", "Confirming a decision"],
|
|
29
|
+
"whenNotToUse": ["Navigating to a new page — use a Link"],
|
|
30
|
+
"variants": {
|
|
31
|
+
"type": { "primary": "Highest-emphasis action…", "secondary": "…", "ghost": "…" },
|
|
32
|
+
"size": { "sm": "…", "md": "…", "lg": "…" }
|
|
33
|
+
},
|
|
34
|
+
"states": { "hover": "…", "focus": "…", "disabled": "…", "loading": "…" },
|
|
35
|
+
"dos": ["Lead with a verb", "One primary button per view"],
|
|
36
|
+
"donts": ["Don't use for navigation", "Don't stack >2 primaries"],
|
|
37
|
+
"accessibility": {
|
|
38
|
+
"role": "button",
|
|
39
|
+
"keyboard": ["Enter / Space activates"],
|
|
40
|
+
"notes": ["Icon-only buttons need an aria-label"]
|
|
41
|
+
},
|
|
42
|
+
"tokensUsed": ["color.bg.primary", "spacing.sm", "radius.md"],
|
|
43
|
+
"status": "stable",
|
|
44
|
+
"updatedAt": "2026-07-14",
|
|
45
|
+
"provenance": {
|
|
46
|
+
"description": "ai-inferred",
|
|
47
|
+
"dos": "best-practice+user",
|
|
48
|
+
"accessibility": "w3c-apg"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Fields (v1 core)
|
|
54
|
+
|
|
55
|
+
- **Required:** `name`, `summary`, `description`.
|
|
56
|
+
- **Optional content:** `whenToUse`, `whenNotToUse`, `variants`, `states`, `dos`,
|
|
57
|
+
`donts`, `accessibility`, `tokensUsed`.
|
|
58
|
+
- **Lifecycle:** `status` (`draft`|`beta`|`stable`|`deprecated`), `updatedAt` (ISO date).
|
|
59
|
+
- **`provenance`** — per-block author source, one of `imported`, `ai-inferred`,
|
|
60
|
+
`best-practice`, `w3c-apg`, `framework`, `user`, or a `+`-joined combination
|
|
61
|
+
(e.g. `best-practice+user`). Regeneration re-infers `ai-inferred`/`framework`
|
|
62
|
+
blocks and **never overwrites** a block whose provenance includes `user` or
|
|
63
|
+
`imported`.
|
|
64
|
+
|
|
65
|
+
Deferred to a later version (do not emit in v1): `anatomy`, `content` (writing
|
|
66
|
+
guidelines), `examples`.
|
|
67
|
+
|
|
68
|
+
## Fingerprint algorithm
|
|
69
|
+
|
|
70
|
+
`fingerprint = sha256(stableStringify(record_without_provenance)).slice(0, 16)`
|
|
71
|
+
|
|
72
|
+
- `provenance` is **excluded** — it is authoring metadata, not projected content.
|
|
73
|
+
- `stableStringify` sorts object keys recursively so formatting/key-order never
|
|
74
|
+
affects the hash.
|
|
75
|
+
- The 16-hex-char result is the stamp recorded per surface and per canonical file.
|
|
76
|
+
|
|
77
|
+
Implemented once in `scripts/lib/doc-record.mjs` (`canonicalFingerprint`) and
|
|
78
|
+
reused by `docs:check`, `docs:digest`, and — for the Figma surfaces — by the
|
|
79
|
+
Figma-connected skill computing the identical hash over the description content.
|
|
80
|
+
|
|
81
|
+
## Projection mapping
|
|
82
|
+
|
|
83
|
+
| Block(s) | Figma component description | Doc card | Storybook autodocs (MDX) + JSDoc | AI digest |
|
|
84
|
+
|---|---|---|---|---|
|
|
85
|
+
| summary, description | ✔ | ✔ | ✔ | ✔ |
|
|
86
|
+
| whenToUse / whenNotToUse | ✔ | ✔ | ✔ | ✔ |
|
|
87
|
+
| variants, states (meanings) | ✔ compact | ✔ legend | ✔ argTypes | ✔ |
|
|
88
|
+
| dos / donts | ✔ | ✔ | ✔ | ✔ |
|
|
89
|
+
| accessibility | ✔ | ✔ | ✔ | ✔ |
|
|
90
|
+
| tokensUsed | — | — | ✔ | ✔ |
|
|
91
|
+
|
|
92
|
+
Each surface carries a fingerprint stamp of the record it was rendered from:
|
|
93
|
+
- **Figma component description** — a trailing marker line `<!-- tl:doc <fp> -->`.
|
|
94
|
+
- **Doc card** — a named metadata node `Doc Fingerprint` holding `<fp>`.
|
|
95
|
+
- **Storybook MDX** — a frontmatter field `docFingerprint: <fp>`.
|
|
96
|
+
|
|
97
|
+
## Manifest pointer (`components.meta[name].doc`)
|
|
98
|
+
|
|
99
|
+
The manifest stores pointers + per-surface fingerprints, never content:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"doc": {
|
|
104
|
+
"path": "design-system/docs/components/Button.doc.json",
|
|
105
|
+
"fingerprint": "<canonical fingerprint at last render>",
|
|
106
|
+
"surfaces": {
|
|
107
|
+
"figmaDescription": { "src": "<fp>", "render": "<hash of description text>" },
|
|
108
|
+
"docCard": { "src": "<fp>", "render": "<hash of card content>" },
|
|
109
|
+
"storybookMdx": { "src": "<fp>", "render": "<hash of mdx file>", "file": "packages/ui/src/Button/Button.mdx" }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- `src` — the canonical fingerprint the surface was rendered from (detects **stale**).
|
|
116
|
+
- `render` — a hash of the surface's rendered content at render time (detects
|
|
117
|
+
**edited**, for surfaces the tooling can re-read).
|
|
118
|
+
- `file` — repo-relative path for code surfaces so `docs:check` can re-read them.
|
|
119
|
+
|
|
120
|
+
## Drift + reconciliation contract
|
|
121
|
+
|
|
122
|
+
`docs:check` classifies each surface:
|
|
123
|
+
- **canonical-changed** — the `.doc.json` fingerprint ≠ `doc.fingerprint`.
|
|
124
|
+
- **stale** — `surface.src` ≠ current canonical fingerprint.
|
|
125
|
+
- **edited** — a re-readable surface's current content hash ≠ `surface.render`.
|
|
126
|
+
- **missing-surface** — a repo surface that declares a `file` which is now gone.
|
|
127
|
+
Failing, and distinct from `edit-unverified`: the surface *was* re-readable and
|
|
128
|
+
its rendered output has been deleted, not merely unreadable this run.
|
|
129
|
+
- **edit-unverified** — a surface the CLI can't read (Figma); checked live by the
|
|
130
|
+
Figma-connected skill instead.
|
|
131
|
+
|
|
132
|
+
Reconciliation is **per item, reviewable**: for each drift the user chooses
|
|
133
|
+
**re-render** (canonical wins) or **pull-back** (fold the surface edit into the
|
|
134
|
+
record), landed as a PR. **Brownfield first run is an adoption**, not a re-render:
|
|
135
|
+
existing surface content is claimed into the record as `provenance: imported` and
|
|
136
|
+
fingerprints are stamped, rather than treated as `edited` drift.
|
|
@@ -227,6 +227,30 @@ variant explosion), so **prefer them for composite components**.
|
|
|
227
227
|
plain instance-swap property instead — slots are for freeform/repeating areas.)
|
|
228
228
|
- **Single icon** (button leading icon) → instance-swap property, not a slot.
|
|
229
229
|
|
|
230
|
+
### Rule: icons match the component's text color
|
|
231
|
+
|
|
232
|
+
**A leading or trailing icon inside a text-bearing component ALWAYS takes the
|
|
233
|
+
same color token as that component's adjacent text/label — in every variant,
|
|
234
|
+
tone, and state.** Bind the icon to the *same* variable the label uses (e.g. a
|
|
235
|
+
badge icon and its label both bind the tone's `fg`; a primary button's icon and
|
|
236
|
+
label both bind `text/onEmphasis`). This is what prevents the mismatch failures:
|
|
237
|
+
a white-text/black-icon control, an icon that stays one fixed color while the
|
|
238
|
+
text changes per tone, or an icon that ignores the theme.
|
|
239
|
+
|
|
240
|
+
Apply the color on the channel the icon actually draws with, and only that
|
|
241
|
+
channel:
|
|
242
|
+
|
|
243
|
+
- **Line / outline icons** (Lucide, Material Symbols outlined — most sets here)
|
|
244
|
+
draw with a **stroke and no fill.** Bind the color on the vectors' **stroke**
|
|
245
|
+
and leave the **fill empty.** Never bind a line icon's fill — a filled
|
|
246
|
+
outline path renders as a solid blob.
|
|
247
|
+
- **Solid / filled glyphs** draw with a **fill.** Bind the color on the fill;
|
|
248
|
+
they have no meaningful stroke.
|
|
249
|
+
|
|
250
|
+
Never hardcode an icon color, and never leave a fixed dark (or light) icon color
|
|
251
|
+
across tones/states. Bind the override on the icon **instance's** vectors — never
|
|
252
|
+
edit the shared `icon/*` source component, which would recolor every other usage.
|
|
253
|
+
|
|
230
254
|
**Practical rules (from Figma's constraints):**
|
|
231
255
|
- **Auto layout must be clean first.** Slots depend on a correct auto layout
|
|
232
256
|
setup — a messy one makes everything shift. This is why auto-layout-on-
|
|
@@ -38,6 +38,37 @@ This is the bridge-side application of the read-discipline principle (B4) in
|
|
|
38
38
|
`${CLAUDE_PLUGIN_ROOT}/references/brownfield-retrofit.md`: don't assert "another
|
|
39
39
|
instance is active" without confirming it's actually live.
|
|
40
40
|
|
|
41
|
+
## Preflight: confirm the *active* file is your target (writes hit the active file)
|
|
42
|
+
|
|
43
|
+
A connected bridge is **not** the same as the *right* file. `figma_execute` (and
|
|
44
|
+
every write) targets whichever file is currently **active** in Figma Desktop — it
|
|
45
|
+
takes no `fileUrl`/`fileKey` argument. So a green `figma_get_status` only proves a
|
|
46
|
+
bridge exists; the active file can silently drift to a different open file between
|
|
47
|
+
your preflight and your write (a user tabbing to another file, a `figma_navigate`
|
|
48
|
+
from an earlier step, a plugin reload). Writing then lands in the wrong file —
|
|
49
|
+
best case it errors with "collection/node not found"; worse case it mutates the
|
|
50
|
+
wrong document.
|
|
51
|
+
|
|
52
|
+
**So the preflight must assert the active file's *identity*, not just connection:**
|
|
53
|
+
|
|
54
|
+
1. **Know your target `fileKey`** before writing (the one your architect read, or
|
|
55
|
+
the one the workflow is scoped to).
|
|
56
|
+
2. **Read the active file and compare.** `figma_get_status` reports
|
|
57
|
+
`currentFileKey` / `currentFileName`; `figma_list_open_files` shows every
|
|
58
|
+
connected file and which is active. Confirm the active `fileKey` equals your
|
|
59
|
+
target before the first write **and** re-confirm after any step that could have
|
|
60
|
+
switched it.
|
|
61
|
+
3. **If it drifted, navigate — don't just retry.** `figma_navigate` to the target
|
|
62
|
+
file URL, then re-read status to confirm the switch took, then write. A bare
|
|
63
|
+
re-run without navigating will hit the wrong file again.
|
|
64
|
+
|
|
65
|
+
This is the file-level analogue of the live-instance discipline above: the
|
|
66
|
+
instance preflight guards against *two* live bridges; this guards against the *one*
|
|
67
|
+
live bridge pointing at the *wrong file*. (Observed live: a status probe reported
|
|
68
|
+
the intended file connected, but by write time the active file had drifted to a
|
|
69
|
+
different open document, and the build failed with "collection not found" until a
|
|
70
|
+
`figma_navigate` corrected it.)
|
|
71
|
+
|
|
41
72
|
## Read discipline: never report "empty" without a verified read (B1/B2)
|
|
42
73
|
|
|
43
74
|
Before reporting that a file has no variables, no text styles, or no effect styles,
|
|
@@ -276,3 +307,34 @@ const totalBindings = Object.values(tally).reduce((a, b) => a + b, 0);
|
|
|
276
307
|
- **This is the number `design-system-audit` records** as
|
|
277
308
|
`audit.figmaInventory.bindings`, and the before/after gate the `token-builder`
|
|
278
309
|
brownfield branch runs around every rename.
|
|
310
|
+
|
|
311
|
+
## Subagent authoring: named working frame + finalize protocol
|
|
312
|
+
|
|
313
|
+
Figma writes are not git-committable, so a dead executor must never leave a
|
|
314
|
+
corrupted live component. Any subagent authoring a component (`figma-executor`)
|
|
315
|
+
uses **build-verify-then-replace**:
|
|
316
|
+
|
|
317
|
+
1. **Always build into a distinct working frame** named `WIP: <ComponentName>` —
|
|
318
|
+
never edit the live component in place.
|
|
319
|
+
2. **Verify the working frame green before touching anything real** — a
|
|
320
|
+
screenshot alone is **insufficient**: 10 tone-colored frames render
|
|
321
|
+
identically to 10 real variants, so a fast model can build plain frames and
|
|
322
|
+
self-report success. The gate is a **programmatic read-back** (`figma_execute`)
|
|
323
|
+
that asserts `node.type === 'COMPONENT_SET'` (never `'FRAME'`), the child count
|
|
324
|
+
matches the variant matrix with every child a `'COMPONENT'`,
|
|
325
|
+
`variantGroupProperties` names the expected axes, and a spot-check of ≥2
|
|
326
|
+
variants shows fills/strokes/radius bound to variables (not raw values) with
|
|
327
|
+
the expected `clipsContent`. A `figma_capture_screenshot` is a **secondary**
|
|
328
|
+
check (create → read-back → screenshot → iterate, max ~3), never the sole one.
|
|
329
|
+
3. **Only then finalize:** remove/replace any existing same-named component,
|
|
330
|
+
rename the working frame's component set to the real `<ComponentName>`, **and
|
|
331
|
+
reap leftover artifacts** — search for and remove any stray `WIP:` frames or
|
|
332
|
+
orphaned fragments (from this or a prior failed run) so the file is left with
|
|
333
|
+
exactly one finalized component and zero `WIP:` debris.
|
|
334
|
+
4. **On failure / `BLOCKED`:** leave the `WIP:` frame intact and named; the
|
|
335
|
+
existing real component is **never touched**. A resumed run finds the `WIP:`
|
|
336
|
+
frame by name and either continues or rebuilds it. A failure therefore leaves
|
|
337
|
+
an obvious, named, resumable artifact — not a half-built live component.
|
|
338
|
+
|
|
339
|
+
Concurrency-1 still applies: the whole Figma surface serializes through the one
|
|
340
|
+
bridge, so only one subagent runs this protocol at a time.
|
|
@@ -11,11 +11,11 @@ what changed. Gating decisions are made by reading this file: if a prerequisite
|
|
|
11
11
|
field is unset, the skill **offers** to run the prerequisite skill rather than
|
|
12
12
|
bailing or running silently.
|
|
13
13
|
|
|
14
|
-
## Schema (schemaVersion
|
|
14
|
+
## Schema (schemaVersion 5)
|
|
15
15
|
|
|
16
16
|
```json
|
|
17
17
|
{
|
|
18
|
-
"schemaVersion":
|
|
18
|
+
"schemaVersion": 5,
|
|
19
19
|
"user": {
|
|
20
20
|
"codingLevel": "new"
|
|
21
21
|
},
|
|
@@ -87,7 +87,8 @@ bailing or running silently.
|
|
|
87
87
|
"ranAt": null,
|
|
88
88
|
"codeSurface": null,
|
|
89
89
|
"figmaInventory": null,
|
|
90
|
-
"percentSemantic": null
|
|
90
|
+
"percentSemantic": null,
|
|
91
|
+
"docSurface": null
|
|
91
92
|
},
|
|
92
93
|
"tokenCrosswalk": {
|
|
93
94
|
"path": null,
|
|
@@ -249,6 +250,17 @@ bailing or running silently.
|
|
|
249
250
|
the manifest and the artboard never disagree. Re-running a component refreshes
|
|
250
251
|
its `updatedAt`. Keep `built` (names) as the source of truth for "exists";
|
|
251
252
|
`meta` is supplementary doc metadata.
|
|
253
|
+
- `meta[name].doc` — documentation pointer + per-surface fingerprints for the
|
|
254
|
+
component (v1: components only). **Pointers and hashes, never content** — the
|
|
255
|
+
content lives in `design-system/docs/components/<name>.doc.json`. Shape:
|
|
256
|
+
`{ path, fingerprint, surfaces: { <surfaceName>: { src, render, file? } } }`,
|
|
257
|
+
where `fingerprint` is the canonical fingerprint at last render, `src` is the
|
|
258
|
+
canonical fingerprint a surface was rendered from (detects stale), `render` is a
|
|
259
|
+
hash of the surface's rendered content (detects edits, for re-readable surfaces),
|
|
260
|
+
and `file` is the repo-relative path of a code surface. Written by
|
|
261
|
+
`component-builder` (Figma + card surfaces) and `storybook-chromatic-builder`
|
|
262
|
+
(code surfaces); read by the `docs:check` gate. See
|
|
263
|
+
`${CLAUDE_PLUGIN_ROOT}/references/component-doc-schema.md`.
|
|
252
264
|
- `instanceSwapUpgradePending` — array of component names whose icon/component
|
|
253
265
|
slots were built with the **toggle + manual-swap fallback** because the
|
|
254
266
|
library wasn't published yet, so the typed `INSTANCE_SWAP` dropdown is still
|
|
@@ -292,6 +304,11 @@ system so the retrofit can be right-sized.
|
|
|
292
304
|
semantic. The single number that decides rename+cleanup vs. rewrite — see
|
|
293
305
|
`${CLAUDE_PLUGIN_ROOT}/references/brownfield-retrofit.md` (safe sequence, `audit`
|
|
294
306
|
phase) for how it's used.
|
|
307
|
+
- `docSurface` — object sizing the documentation debt for a brownfield retrofit,
|
|
308
|
+
from verified per-component reads: e.g. `{ "documented": 12, "undocumented": 34,
|
|
309
|
+
"sources": { "codeJsdoc": 8, "mdx": 4, "figmaDescription": 6, "readme": 3 } }`.
|
|
310
|
+
`null` until the audit's documentation-sizing pass runs. Counts come from real
|
|
311
|
+
reads, never assumptions (same discipline as `codeSurface` / `figmaInventory`).
|
|
295
312
|
|
|
296
313
|
### `tokenCrosswalk`
|
|
297
314
|
Populated by the `token-crosswalk-builder` skill. Points at the backbone artifact
|
|
@@ -307,8 +324,9 @@ that maps new token ↔ old Figma token ↔ code identifier.
|
|
|
307
324
|
Populated by the `retrofit-planner` orchestrator. Tracks where a multi-phase
|
|
308
325
|
retrofit stands so a later session can resume.
|
|
309
326
|
- `phase` — one of `"audit"`, `"refine"`, `"rebind"`, `"sync"`, `"baseline"`,
|
|
310
|
-
`"code"`, `"cleanup"`, `"done"`, or `null` (no retrofit in progress).
|
|
311
|
-
in that order;
|
|
327
|
+
`"code"`, `"docs"`, `"cleanup"`, `"done"`, or `null` (no retrofit in progress).
|
|
328
|
+
Phases run in that order; the `docs` phase adopts existing documentation (see
|
|
329
|
+
`retrofit-planner`). See the safe sequence in
|
|
312
330
|
`${CLAUDE_PLUGIN_ROOT}/references/brownfield-retrofit.md`.
|
|
313
331
|
- `startedAt` / `completedAt` — ISO timestamps bounding the retrofit.
|
|
314
332
|
- `journalScaffolded` — whether the `docs/design-system/` decision journal has been
|
|
@@ -339,3 +357,7 @@ retrofit stands so a later session can resume.
|
|
|
339
357
|
6. **`workspace.origin` is immutable after intake.** Written once by
|
|
340
358
|
`figma-environment-setup` Step 0 and must not be overwritten by any downstream
|
|
341
359
|
skill. Skills read it to adapt behavior — they do not modify it.
|
|
360
|
+
|
|
361
|
+
**v4 → v5 migration:** add `audit.docSurface` (default `null`) and the `docs`
|
|
362
|
+
retrofit phase; `components.meta[name].doc` is added lazily per component as docs
|
|
363
|
+
are authored. Bump `schemaVersion` to `5`. No existing field changes.
|
|
@@ -139,3 +139,8 @@ each platform's output is independent and verifiable, which makes it a good fit
|
|
|
139
139
|
for **parallel subagent generation** — one subagent per adapter, each producing
|
|
140
140
|
and validating its platform's files, reviewed before the combined result is
|
|
141
141
|
landed in a PR. See the token-sync skill for the execution model.
|
|
142
|
+
|
|
143
|
+
Token-adapter generation parallelizes because each adapter writes independent
|
|
144
|
+
files. Figma authoring does **not**: the single figma-console bridge is
|
|
145
|
+
concurrency-1, so Figma work uses sequential subagents — model routing yes,
|
|
146
|
+
parallel never (see `${CLAUDE_PLUGIN_ROOT}/references/agent-routing.md`).
|
package/scripts/README.md
CHANGED
|
@@ -12,6 +12,8 @@ tested here; copied verbatim by `token-crosswalk-builder` into the user's
|
|
|
12
12
|
| `guard-token-removal.mjs` | Grep `.ts/.tsx` (minus generated + tests) for about-to-be-deleted symbols; blocks cleanup until zero references remain. | run during the cleanup phase |
|
|
13
13
|
| `lib/crosswalk.mjs` | Shared loader + structural validation for `crosswalk.json` (used by the validator and reverse-index). | copied alongside |
|
|
14
14
|
| `crosswalk.schema.json` | The finalized JSON Schema for `crosswalk.json` (contract + editor support). | copied beside `crosswalk.json` |
|
|
15
|
+
| `build-docs-digest.mjs` | Aggregate every `design-system/docs/components/*.doc.json` into `design-system/docs/index.json` + `llms.txt` for AI/human consumers. | `docs:digest` |
|
|
16
|
+
| `docs-check.mjs` | Drift gate — verifies each component's doc surfaces still match its canonical record (via `lib/doc-record.mjs` fingerprints). Exits 1 on drift. | `docs:check` |
|
|
15
17
|
|
|
16
18
|
The crosswalk contract is documented in
|
|
17
19
|
`${CLAUDE_PLUGIN_ROOT}/references/crosswalk-schema.md`.
|
|
@@ -76,3 +78,12 @@ rewriting `${CLAUDE_PLUGIN_ROOT}` → `.throughline`:
|
|
|
76
78
|
npx @radicool/throughline init --target=cursor|codex|generic
|
|
77
79
|
|
|
78
80
|
See `scripts/install.mjs` (pure core + CLI + `install.test.mjs`).
|
|
81
|
+
|
|
82
|
+
## Documentation scripts
|
|
83
|
+
|
|
84
|
+
`docs:digest` and `docs:check` operate on the folder-resident documentation store
|
|
85
|
+
at `design-system/docs/`. Both share `lib/doc-record.mjs` (record loading +
|
|
86
|
+
fingerprinting). `docs:check` re-reads repo surfaces (Storybook MDX); Figma
|
|
87
|
+
surfaces are marked `edit-unverified` and are checked live by the Figma-connected
|
|
88
|
+
skills. See `${CLAUDE_PLUGIN_ROOT}/references/component-doc-schema.md` for the
|
|
89
|
+
record schema and fingerprint contract.
|