@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.
Files changed (45) hide show
  1. package/README.md +2 -2
  2. package/adapters/codex/AGENTS.md +2 -1
  3. package/adapters/codex/prompts/component-builder.md +80 -2
  4. package/adapters/codex/prompts/design-system-audit.md +20 -0
  5. package/adapters/codex/prompts/document-component.md +26 -0
  6. package/adapters/codex/prompts/icon-system-builder.md +10 -2
  7. package/adapters/codex/prompts/repository-builder.md +14 -0
  8. package/adapters/codex/prompts/retrofit-planner.md +21 -1
  9. package/adapters/codex/prompts/storybook-chromatic-builder.md +52 -7
  10. package/adapters/codex/prompts/token-builder.md +16 -0
  11. package/adapters/codex/prompts/token-sheet-builder.md +15 -1
  12. package/adapters/codex/prompts/token-sync-layer.md +14 -7
  13. package/adapters/cursor/.cursor/commands/document-component.md +26 -0
  14. package/adapters/cursor/.cursor/rules/component-builder.mdc +80 -2
  15. package/adapters/cursor/.cursor/rules/design-system-audit.mdc +20 -0
  16. package/adapters/cursor/.cursor/rules/icon-system-builder.mdc +10 -2
  17. package/adapters/cursor/.cursor/rules/repository-builder.mdc +14 -0
  18. package/adapters/cursor/.cursor/rules/retrofit-planner.mdc +22 -2
  19. package/adapters/cursor/.cursor/rules/storybook-chromatic-builder.mdc +52 -7
  20. package/adapters/cursor/.cursor/rules/token-builder.mdc +16 -0
  21. package/adapters/cursor/.cursor/rules/token-sheet-builder.mdc +15 -1
  22. package/adapters/cursor/.cursor/rules/token-sync-layer.mdc +14 -7
  23. package/adapters/generic/AGENTS.md +2 -1
  24. package/adapters/generic/commands/document-component.md +26 -0
  25. package/adapters/generic/skills/component-builder/SKILL.md +80 -2
  26. package/adapters/generic/skills/design-system-audit/SKILL.md +20 -0
  27. package/adapters/generic/skills/icon-system-builder/SKILL.md +10 -2
  28. package/adapters/generic/skills/repository-builder/SKILL.md +14 -0
  29. package/adapters/generic/skills/retrofit-planner/SKILL.md +21 -1
  30. package/adapters/generic/skills/storybook-chromatic-builder/SKILL.md +52 -7
  31. package/adapters/generic/skills/token-builder/SKILL.md +16 -0
  32. package/adapters/generic/skills/token-sheet-builder/SKILL.md +15 -1
  33. package/adapters/generic/skills/token-sync-layer/SKILL.md +14 -7
  34. package/package.json +1 -1
  35. package/references/agent-routing.md +70 -0
  36. package/references/component-doc-archetypes.md +86 -0
  37. package/references/component-doc-schema.md +136 -0
  38. package/references/figma-component-standards.md +24 -0
  39. package/references/figma-scripting.md +62 -0
  40. package/references/manifest-schema.md +27 -5
  41. package/references/sync-adapters.md +5 -0
  42. package/scripts/README.md +11 -0
  43. package/scripts/build-docs-digest.mjs +74 -0
  44. package/scripts/docs-check.mjs +103 -0
  45. 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 — no subagents). Iterate on layout/styling if they want
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-driven for multiple platforms.** When more than one
143
- platform is targeted, generating each platform's output is independent and
144
- verifiable, so dispatch **one subagent per adapter**: each produces its
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 correctly for web / flatten for native). Review each
147
- before combining. For a single platform, run inline. (See the selective-
148
- subagent decision: code-gen skills parallelize; Figma-authoring skills don't.)
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
- PR-review and subagent model as token output.
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.12.1",
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 4)
14
+ ## Schema (schemaVersion 5)
15
15
 
16
16
  ```json
17
17
  {
18
- "schemaVersion": 4,
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). Phases run
311
- in that order; see the safe sequence in
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.