@radicool/throughline 0.14.0 → 0.16.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 (49) hide show
  1. package/README.md +2 -1
  2. package/adapters/codex/AGENTS.md +10 -10
  3. package/adapters/codex/prompts/component-builder.md +59 -15
  4. package/adapters/codex/prompts/document-component.md +42 -10
  5. package/adapters/codex/prompts/storybook-chromatic-builder.md +52 -9
  6. package/adapters/codex/prompts/token-crosswalk-builder.md +2 -0
  7. package/adapters/codex/prompts/token-sync-layer.md +64 -4
  8. package/adapters/cursor/.cursor/commands/document-component.md +42 -10
  9. package/adapters/cursor/.cursor/rules/component-builder.mdc +60 -16
  10. package/adapters/cursor/.cursor/rules/component-pipeline.mdc +1 -1
  11. package/adapters/cursor/.cursor/rules/design-system-audit.mdc +1 -1
  12. package/adapters/cursor/.cursor/rules/figma-environment-setup.mdc +1 -1
  13. package/adapters/cursor/.cursor/rules/icon-system-builder.mdc +1 -1
  14. package/adapters/cursor/.cursor/rules/repository-builder.mdc +1 -1
  15. package/adapters/cursor/.cursor/rules/retrofit-planner.mdc +1 -1
  16. package/adapters/cursor/.cursor/rules/storybook-chromatic-builder.mdc +53 -10
  17. package/adapters/cursor/.cursor/rules/token-builder.mdc +1 -1
  18. package/adapters/cursor/.cursor/rules/token-crosswalk-builder.mdc +3 -1
  19. package/adapters/cursor/.cursor/rules/token-sheet-builder.mdc +1 -1
  20. package/adapters/cursor/.cursor/rules/token-sync-layer.mdc +65 -5
  21. package/adapters/generic/AGENTS.md +10 -10
  22. package/adapters/generic/commands/document-component.md +42 -10
  23. package/adapters/generic/skills/component-builder/SKILL.md +59 -15
  24. package/adapters/generic/skills/storybook-chromatic-builder/SKILL.md +52 -9
  25. package/adapters/generic/skills/token-crosswalk-builder/SKILL.md +2 -0
  26. package/adapters/generic/skills/token-sync-layer/SKILL.md +64 -4
  27. package/package.json +1 -1
  28. package/references/component-doc-archetypes.md +15 -11
  29. package/references/component-doc-schema.md +23 -5
  30. package/references/doc-card-builder.md +565 -0
  31. package/references/doc-writing-standard.md +144 -0
  32. package/references/figma-component-standards.md +63 -16
  33. package/references/guide-voice.md +96 -0
  34. package/references/manifest-schema.md +24 -6
  35. package/references/native-adapter-config.md +930 -0
  36. package/references/sync-adapters.md +94 -12
  37. package/scripts/README.md +37 -3
  38. package/scripts/build-doc-card-builder.mjs +143 -0
  39. package/scripts/build-native-adapter-config.mjs +280 -0
  40. package/scripts/docs-check.mjs +18 -4
  41. package/scripts/docs-lint.mjs +163 -0
  42. package/scripts/install.mjs +14 -1
  43. package/scripts/lib/doc-card-plan.mjs +101 -0
  44. package/scripts/lib/doc-card-render.figma.js +371 -0
  45. package/scripts/lib/dtcg.mjs +87 -0
  46. package/scripts/lib/native-literal.mjs +205 -0
  47. package/scripts/lib/sd-native.mjs +770 -0
  48. package/scripts/validate-crosswalk.mjs +3 -29
  49. package/scripts/validate-token-output.mjs +338 -0
@@ -44,8 +44,8 @@ Ask which platform(s) the user is building for. Read
44
44
  - **Curated (Tier 1):** `shadcn`, `tailwind`, `mui`, `vanilla-css`, `ios-swift`.
45
45
  Vetted presets — high confidence.
46
46
  - **Generated (Tier 2):** any other framework (Ant Design, Chakra, HeroUI,
47
- Android/Kotlin, Flutter, etc.). The skill generates an adapter and verifies it
48
- against a real component before trusting it.
47
+ Android/Kotlin, Flutter, etc.). The skill generates an adapter and
48
+ verifies it against a real component before trusting it.
49
49
 
50
50
  **Always tell the user which tier they're on.** If they name a curated one, say
51
51
  it'll be solid. If they name anything else, be honest: "That's not one I have a
@@ -139,11 +139,53 @@ 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
+ **Native targets import the shipped configuration; they do not transcribe it.**
143
+ Copy `.throughline/scripts/lib/sd-native.mjs` into
144
+ `packages/tokens/scripts/lib/` (see Step 4) and call it. The stock `ios-swift`
145
+ and `compose` transform groups emit every `px`-authored dimension at ×16 its
146
+ value — valid, compiling, silently wrong — and mishandle `color-mix()` and
147
+ dual-node DTCG the same way. Never build a native platform from a stock
148
+ `transformGroup`.
149
+
150
+ ```js
151
+ import StyleDictionary from 'style-dictionary';
152
+ import { registerNativeTransforms, nativePlatform, nativeSources }
153
+ from './scripts/lib/sd-native.mjs';
154
+
155
+ registerNativeTransforms(StyleDictionary);
156
+
157
+ for (const mode of MODES) { // e.g. ['light', 'dark']
158
+ const sd = new StyleDictionary({
159
+ source: nativeSources(sourcesFor(mode)), // guards against a mode collapse
160
+ preprocessors: ['dtcg/resolve-dual-node'],
161
+ platforms: {
162
+ ios: nativePlatform({ platform: 'ios-swift', buildPath: `ios/${mode}/` }),
163
+ // android also requires packageName:
164
+ // android: nativePlatform({ platform: 'android-kotlin',
165
+ // buildPath: `android/${mode}/`, packageName: 'com.example.tokens' }),
166
+ },
167
+ });
168
+ await sd.buildAllPlatforms();
169
+ }
170
+ ```
171
+
172
+ **One build per mode combination, and never a glob.** Style Dictionary dedupes
173
+ by dot-path, so a single build over the whole token directory collapses light
174
+ and dark into whichever file sorted last, silently dropping a mode. Passing each
175
+ mode's sources through `nativeSources` turns that into a thrown error naming the
176
+ colliding paths. The `dtcg/resolve-dual-node` preprocessor throws too, on its
177
+ own collision: a dual node's hoisted child renamed to a camel-joined name that
178
+ an existing sibling or an earlier hoist in the same pass already has. Then run
179
+ `tokens:validate-output` against each generated file with that same source
180
+ list. See `.throughline/references/native-adapter-config.md`.
181
+
142
182
  **Execution model — subagent dispatch with model routing.** Generating each
143
183
  platform's output is independent and verifiable. If your host supports subagent
144
184
  dispatch, dispatch **one `code-executor` per adapter** — each produces its
145
- platform's files and verifies them (the config builds, the expected files
146
- appear, references resolve for web / flatten for native) then a **`reviewer`**
185
+ platform's files and verifies them (for web: the config builds, the expected
186
+ files appear, references resolve; for native: `tokens:validate-output` passes
187
+ "the config builds" is not verification, it is the condition under which all
188
+ four known native failure modes ship silently) — then a **`reviewer`**
147
189
  to check each before combining. Choose each subagent's model from its role tier
148
190
  per `.throughline/references/agent-routing.md` (`code-executor` → fast,
149
191
  `reviewer` → balanced), and only dispatch once each adapter's spec is complete
@@ -159,6 +201,24 @@ as **build artifacts** — regenerated every sync, never hand-edited. Wire
159
201
  `packages/tokens/package.json` to export them so the UI package, Storybook, and
160
202
  any future app consume them.
161
203
 
204
+ **Install the native token toolkit — all four files, as a set.** Copy
205
+ `.throughline/scripts/validate-token-output.mjs` into
206
+ `packages/tokens/scripts/`, and
207
+ `.throughline/scripts/lib/dtcg.mjs`,
208
+ `.throughline/scripts/lib/native-literal.mjs`, and
209
+ `.throughline/scripts/lib/sd-native.mjs` into
210
+ `packages/tokens/scripts/lib/`. `sd-native.mjs` and the validator both import
211
+ `dtcg.mjs` and `native-literal.mjs`, so copying any of them without the others
212
+ breaks at import. Then register the gate so it stays live on every future sync:
213
+
214
+ ```json
215
+ "tokens:validate-output": "node scripts/validate-token-output.mjs --min-match 1"
216
+ ```
217
+
218
+ Invoke it once per native output file, passing the same `--source` list that
219
+ file's build used. `--min-match 1` is what makes it a gate: the flag defaults to
220
+ `0.5`, so without it a 60% match rate exits `0`.
221
+
162
222
  ## Step 4.5 — Icon code sync (install check + custom SVGR)
163
223
 
164
224
  Icons reach code differently from tokens, so handle them here if the system has
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radicool/throughline",
3
- "version": "0.14.0",
3
+ "version": "0.16.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": {
@@ -12,14 +12,16 @@ These are **seeds, not gospel** — the user's approval and the actual built
12
12
  component override them. Sources: W3C ARIA Authoring Practices Guide (roles +
13
13
  keyboard), Material 3, Shopify Polaris (content/usage), IBM Carbon (usage).
14
14
 
15
+ Seeds are clause-style shorthand; the authoring pipeline expands them into full sentences per references/doc-writing-standard.md before they enter a record.
16
+
15
17
  ## Button
16
18
 
17
19
  - **whenToUse:** trigger an action or event (submit, confirm, open a dialog).
18
- - **whenNotToUse:** navigation between pages/URLs use a Link.
20
+ - **whenNotToUse:** navigation between pages/URLs (use a Link).
19
21
  - **dos:** lead the label with a verb; keep one primary (highest-emphasis) button
20
22
  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
+ - **donts:** don't use a button for navigation (use a Link); don't stack multiple
24
+ primary buttons (keep one per view); don't disable without telling the user why.
23
25
  - **accessibility (w3c-apg):** role `button`; Enter and Space activate; an
24
26
  icon-only button needs an `aria-label`; disabled buttons are not focusable.
25
27
 
@@ -30,8 +32,8 @@ keyboard), Material 3, Shopify Polaris (content/usage), IBM Carbon (usage).
30
32
  text (use Textarea).
31
33
  - **dos:** always pair with a visible label; show format hints as helper text;
32
34
  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
+ - **donts:** don't use placeholder text as the only label (pair with a visible
36
+ label); don't validate on every keystroke before first blur.
35
37
  - **accessibility (w3c-apg):** every input has a programmatically associated
36
38
  `<label>`; error state sets `aria-invalid` and links the message via
37
39
  `aria-describedby`.
@@ -42,8 +44,8 @@ keyboard), Material 3, Shopify Polaris (content/usage), IBM Carbon (usage).
42
44
  - **whenNotToUse:** a single either/or action that takes effect immediately with no
43
45
  save (prefer a toggle) vs. a form choice (prefer radio/checkbox).
44
46
  - **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
+ - **donts:** don't use a radio group for multi-select (use checkboxes); don't use
48
+ a toggle for choices needing a separate Save (use radio/checkbox).
47
49
  - **accessibility (w3c-apg):** roles `checkbox` / `radio` / `switch`; Space
48
50
  toggles; radio groups navigate with arrow keys; state exposed via
49
51
  `aria-checked`.
@@ -51,7 +53,8 @@ keyboard), Material 3, Shopify Polaris (content/usage), IBM Carbon (usage).
51
53
  ## Card
52
54
 
53
55
  - **whenToUse:** group related content and actions about a single subject.
54
- - **whenNotToUse:** primary page layout scaffolding; a bare list of text.
56
+ - **whenNotToUse:** primary page layout scaffolding (use a layout/grid component); a
57
+ bare list of text (use a List).
55
58
  - **dos:** make the primary action obvious; keep one main call-to-action per card.
56
59
  - **donts:** don't nest cards more than one level; don't make the whole card AND an
57
60
  inner button separately clickable in conflicting ways.
@@ -71,10 +74,11 @@ keyboard), Material 3, Shopify Polaris (content/usage), IBM Carbon (usage).
71
74
  ## Badge / chip / tag
72
75
 
73
76
  - **whenToUse:** short status, count, or category label (badge); a removable/
74
- selectable token (chip).
77
+ selectable item (chip).
75
78
  - **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.
79
+ - **dos:** keep text to a word or two; match badge color to its semantic meaning
80
+ (success, warning, error).
81
+ - **donts:** don't rely on color alone to convey status (include text or an icon).
78
82
  - **accessibility:** a removable chip's remove control needs an accessible name
79
83
  (e.g. "Remove <label>"); status conveyed with text, not color only (WCAG 1.4.1).
80
84
 
@@ -58,9 +58,17 @@ deterministically. JSON is equally machine-legible for AI consumers.
58
58
  - **Lifecycle:** `status` (`draft`|`beta`|`stable`|`deprecated`), `updatedAt` (ISO date).
59
59
  - **`provenance`** — per-block author source, one of `imported`, `ai-inferred`,
60
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`.
61
+ (e.g. `best-practice+user`). Regeneration **re-infers** a block whose
62
+ provenance includes `ai-inferred`, `framework`, `best-practice`, or `w3c-apg`,
63
+ and **never overwrites** one whose provenance includes `user` or `imported`.
64
+ Every value is assigned to exactly one of those two tiers: generated content is
65
+ re-inferred, human input (`user`) and pre-existing external content
66
+ (`imported`) are protected. Protection takes precedence: a combination that
67
+ contains both — `best-practice+user`, say — is protected. A block is
68
+ re-inferred only when it carries no `user` or `imported` marker at all. A
69
+ protected block may still be rewritten when the user approves the rewrite at
70
+ the record-approval gate; the result is stamped `imported+user`, which is
71
+ protected from then on and never re-proposed.
64
72
 
65
73
  Deferred to a later version (do not emit in v1): `anatomy`, `content` (writing
66
74
  guidelines), `examples`.
@@ -84,7 +92,7 @@ Figma-connected skill computing the identical hash over the description content.
84
92
  |---|---|---|---|---|
85
93
  | summary, description | ✔ | ✔ | ✔ | ✔ |
86
94
  | whenToUse / whenNotToUse | ✔ | ✔ | ✔ | ✔ |
87
- | variants, states (meanings) | compact | ✔ legend | ✔ argTypes | ✔ |
95
+ | variants, states (meanings) | | ✔ legend | ✔ argTypes | ✔ |
88
96
  | dos / donts | ✔ | ✔ | ✔ | ✔ |
89
97
  | accessibility | ✔ | ✔ | ✔ | ✔ |
90
98
  | tokensUsed | — | — | ✔ | ✔ |
@@ -105,7 +113,7 @@ The manifest stores pointers + per-surface fingerprints, never content:
105
113
  "fingerprint": "<canonical fingerprint at last render>",
106
114
  "surfaces": {
107
115
  "figmaDescription": { "src": "<fp>", "render": "<hash of description text>" },
108
- "docCard": { "src": "<fp>", "render": "<hash of card content>" },
116
+ "docCard": { "src": "<fp>", "render": "<hash of card content>", "renderer": "4" },
109
117
  "storybookMdx": { "src": "<fp>", "render": "<hash of mdx file>", "file": "packages/ui/src/Button/Button.mdx" }
110
118
  }
111
119
  }
@@ -116,6 +124,11 @@ The manifest stores pointers + per-surface fingerprints, never content:
116
124
  - `render` — a hash of the surface's rendered content at render time (detects
117
125
  **edited**, for surfaces the tooling can re-read).
118
126
  - `file` — repo-relative path for code surfaces so `docs:check` can re-read them.
127
+ - `renderer` — (docCard only) the layout version of the builder that last
128
+ rendered the card: `DOC_CARD_RENDERER_VERSION` in `scripts/lib/doc-card-plan.mjs`,
129
+ currently `"4"`. Additive and optional — absence means the card predates the
130
+ versioned builder. Stamped from the builder's returned summary, never by
131
+ re-reading the card.
119
132
 
120
133
  ## Drift + reconciliation contract
121
134
 
@@ -123,6 +136,11 @@ The manifest stores pointers + per-surface fingerprints, never content:
123
136
  - **canonical-changed** — the `.doc.json` fingerprint ≠ `doc.fingerprint`.
124
137
  - **stale** — `surface.src` ≠ current canonical fingerprint.
125
138
  - **edited** — a re-readable surface's current content hash ≠ `surface.render`.
139
+ - **layout-upgrade-available** — informational, never failing, docCard only:
140
+ `surfaces.docCard.renderer` is missing or lower than the current
141
+ `DOC_CARD_RENDERER_VERSION`. The card's content is not in drift — its layout
142
+ predates the current builder. Re-render on next touch (no unprompted Figma
143
+ writes; untouched brownfield cards must not generate a standing warning wall).
126
144
  - **missing-surface** — a repo surface that declares a `file` which is now gone.
127
145
  Failing, and distinct from `edit-unverified`: the surface *was* re-readable and
128
146
  its rendered output has been deleted, not merely unreadable this run.