@radicool/throughline 0.19.0 → 0.20.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/prompts/component-builder.md +24 -1
- package/adapters/codex/prompts/document-component.md +10 -1
- package/adapters/codex/prompts/icon-system-builder.md +4 -0
- package/adapters/codex/prompts/storybook-chromatic-builder.md +114 -7
- package/adapters/codex/prompts/token-builder.md +110 -3
- package/adapters/codex/prompts/token-crosswalk-builder.md +2 -0
- package/adapters/codex/prompts/token-sheet-builder.md +4 -0
- package/adapters/codex/prompts/token-sync-layer.md +140 -20
- package/adapters/cursor/.cursor/commands/document-component.md +10 -1
- package/adapters/cursor/.cursor/rules/component-builder.mdc +24 -1
- package/adapters/cursor/.cursor/rules/icon-system-builder.mdc +4 -0
- package/adapters/cursor/.cursor/rules/storybook-chromatic-builder.mdc +114 -7
- package/adapters/cursor/.cursor/rules/token-builder.mdc +110 -3
- package/adapters/cursor/.cursor/rules/token-crosswalk-builder.mdc +2 -0
- package/adapters/cursor/.cursor/rules/token-sheet-builder.mdc +4 -0
- package/adapters/cursor/.cursor/rules/token-sync-layer.mdc +140 -20
- package/adapters/generic/commands/document-component.md +10 -1
- package/adapters/generic/skills/component-builder/SKILL.md +24 -1
- package/adapters/generic/skills/icon-system-builder/SKILL.md +4 -0
- package/adapters/generic/skills/storybook-chromatic-builder/SKILL.md +114 -7
- package/adapters/generic/skills/token-builder/SKILL.md +110 -3
- package/adapters/generic/skills/token-crosswalk-builder/SKILL.md +2 -0
- package/adapters/generic/skills/token-sheet-builder/SKILL.md +4 -0
- package/adapters/generic/skills/token-sync-layer/SKILL.md +140 -20
- package/package.json +1 -1
- package/references/component-doc-schema.md +11 -1
- package/references/figma-component-standards.md +14 -2
- package/references/figma-scripting.md +12 -2
- package/references/manifest-schema.md +42 -2
- package/references/proof-bundle.md +305 -0
- package/references/sync-adapters.md +38 -53
- package/scripts/README.md +140 -6
- package/scripts/build-doc-card-builder.mjs +1 -1
- package/scripts/grep-color-usage.mjs +10 -30
- package/scripts/guard-token-removal.mjs +10 -26
- package/scripts/lib/component-states.mjs +56 -0
- package/scripts/lib/contrast.mjs +117 -0
- package/scripts/lib/dtcg.mjs +3 -2
- package/scripts/lib/proof.mjs +186 -0
- package/scripts/lib/source-scan.mjs +45 -0
- package/scripts/validate-adherence.mjs +895 -0
- package/scripts/validate-token-output.mjs +468 -63
- package/scripts/verify-check.mjs +710 -0
|
@@ -27,6 +27,9 @@ Ask which component to document (e.g. "Button"), then:
|
|
|
27
27
|
verify its returned summary and stamp `surfaces.docCard.{src,render,renderer}`
|
|
28
28
|
from it), and (if the repo/code side exists) render MDX/JSDoc and run
|
|
29
29
|
`docs:digest` per the `storybook-chromatic-builder` render step.
|
|
30
|
+
Before the Figma writes, run the active-file preflight in
|
|
31
|
+
`.throughline/references/figma-scripting.md` — this command writes
|
|
32
|
+
inline, so no executor checks the file for you.
|
|
30
33
|
3. **Reconcile drift.** Before trusting `docs:check`, confirm the repo's copy of
|
|
31
34
|
the doc scripts is current: compare `DOC_CARD_RENDERER_VERSION` in the repo's
|
|
32
35
|
`scripts/lib/doc-card-plan.mjs` against the same constant in
|
|
@@ -36,7 +39,13 @@ Ask which component to document (e.g. "Button"), then:
|
|
|
36
39
|
doc scripts from the plugin copy. Refresh the whole set and re-check the npm
|
|
37
40
|
registrations, both per **Documentation scripts — install as a set** in
|
|
38
41
|
`.throughline/scripts/README.md` — a refreshed file whose script was
|
|
39
|
-
never registered is the same failure in a new place.
|
|
42
|
+
never registered is the same failure in a new place. Two of the registrations,
|
|
43
|
+
`adherence:check` and `verify:check`, carry placeholder paths and a
|
|
44
|
+
`--package` specifier: substitute the repo's real ones per the paragraphs under
|
|
45
|
+
that table, run each once, and use the documented `--skip` for a rule the
|
|
46
|
+
system doesn't fit, rather than registering a placeholder that fails every
|
|
47
|
+
run. A repo refreshed from before 0.20.0 gets both for the first time, so say
|
|
48
|
+
so. Run `docs:check` (with the
|
|
40
49
|
refreshed scripts, if any). For each drifted surface, offer a per-item
|
|
41
50
|
choice — **re-render** (canonical wins) or **pull-back**
|
|
42
51
|
(fold the surface edit into the record) — and land the result as a reviewable
|
|
@@ -109,6 +109,10 @@ component once its slice of the spec is complete enough to transcribe. **Keep th
|
|
|
109
109
|
human checkpoint between components** — subagents run continuously within a
|
|
110
110
|
component, but you pause for the human between them. If your host has no subagent
|
|
111
111
|
dispatch, build and verify each component inline, sequentially, as before.
|
|
112
|
+
Inline, run the active-file preflight in
|
|
113
|
+
`.throughline/references/figma-scripting.md` before the first write and
|
|
114
|
+
again before replacing anything — the executor's file check does not come with
|
|
115
|
+
you.
|
|
112
116
|
|
|
113
117
|
## Step 3 — Build each component, bound to tokens/styles
|
|
114
118
|
|
|
@@ -256,7 +260,10 @@ reference register, not this skill's guide voice.
|
|
|
256
260
|
2. **Enrich from the archetype knowledge base.** Match the component to the nearest
|
|
257
261
|
archetype in `.throughline/references/component-doc-archetypes.md` and
|
|
258
262
|
seed `dos`, `donts`, `accessibility`, `whenToUse`, `whenNotToUse`. Provenance
|
|
259
|
-
`best-practice` (or `w3c-apg` for the accessibility block).
|
|
263
|
+
`best-practice` (or `w3c-apg` for the accessibility block). Record the matched
|
|
264
|
+
archetype's id in the record's `archetype` field — one of `button`, `input`,
|
|
265
|
+
`choice`, `card`, `modal`, `badge`, `other` — so `verify:check` can read it
|
|
266
|
+
later rather than re-guessing from the name.
|
|
260
267
|
3. **Specialize to `project.uiFramework`.** Align variant-meaning wording and the
|
|
261
268
|
accessibility idiom to the target framework (the same field you read for variant
|
|
262
269
|
vocabulary). Provenance `framework`.
|
|
@@ -351,6 +358,22 @@ any component built with the toggle + manual-swap fallback is listed in
|
|
|
351
358
|
`components.instanceSwapUpgradePending`. Append `component-builder` to
|
|
352
359
|
`completedSkills`.
|
|
353
360
|
|
|
361
|
+
**Record the stage entry, one call per component that actually finished:**
|
|
362
|
+
`node .throughline/scripts/verify-check.mjs --record --stage
|
|
363
|
+
component-builder --subject <Name> --entry <tmp>.json`. Build `<tmp>.json` with
|
|
364
|
+
`changed` (what was built), the attested checks `structural-read-back`,
|
|
365
|
+
`state-baseline` and `figma-name` from what the `figma-executor` returned,
|
|
366
|
+
`review` when a reviewer ran, `screenshot` when one was captured, and
|
|
367
|
+
`advancedBecause`. The entry shape is
|
|
368
|
+
`.throughline/references/proof-bundle.md` — don't restate it here. Write
|
|
369
|
+
an entry only for a component that actually finished; a `BLOCKED` component gets
|
|
370
|
+
none. Do this after adding the component to `components.built` above — by the
|
|
371
|
+
time this loop runs, the whole run's components are already there, which is what
|
|
372
|
+
lets this stage's first `--record` capture the full adoption batch; each record
|
|
373
|
+
then removes its own subject from the captured `exempt` list, so running the
|
|
374
|
+
loop over every finished component (not a one-shot write) is what un-grandfathers
|
|
375
|
+
the rest of the batch.
|
|
376
|
+
|
|
354
377
|
**Upgrade pass:** if `components.instanceSwapUpgradePending` is non-empty, re-resolve
|
|
355
378
|
publish state (detect-or-ask per `.throughline/references/figma-publishing.md`);
|
|
356
379
|
only when it is **confirmed published** (`figma.libraryPublished` true via a verified
|
|
@@ -184,6 +184,10 @@ needed here. Route per `.throughline/references/agent-routing.md`. If
|
|
|
184
184
|
your host has no subagent dispatch, script the SVG-to-component pass inline,
|
|
185
185
|
sequentially, with the user in the loop. Either way, follow
|
|
186
186
|
`.throughline/references/figma-scripting.md`.
|
|
187
|
+
Inline, run the active-file preflight in
|
|
188
|
+
`.throughline/references/figma-scripting.md` before the first write and
|
|
189
|
+
again before replacing anything — the executor's file check does not come with
|
|
190
|
+
you.
|
|
187
191
|
|
|
188
192
|
## Step 3 — Normalize: page, naming, sizing, variants
|
|
189
193
|
|
|
@@ -31,9 +31,18 @@ system). Wire it to consume `packages/tokens` output so stories render with the
|
|
|
31
31
|
real design tokens (import the generated CSS/theme). Checkpoint: confirm
|
|
32
32
|
Storybook runs and shows the token-themed canvas.
|
|
33
33
|
|
|
34
|
-
Install the documentation scripts alongside the token scripts: copy the
|
|
35
|
-
files and register the
|
|
36
|
-
install as a set** in `.throughline/scripts/README.md`.
|
|
34
|
+
Install the documentation scripts alongside the token scripts: copy the twelve
|
|
35
|
+
files and register the five npm scripts listed under **Documentation scripts —
|
|
36
|
+
install as a set** in `.throughline/scripts/README.md`. `adherence:check`
|
|
37
|
+
needs the repo's own values substituted in — the UI package's specifier for
|
|
38
|
+
`--package`, and paths for `--root`/`--system` that match this repo's layout;
|
|
39
|
+
registering it with the table's placeholders leaves a script that cannot run.
|
|
40
|
+
`verify:check` needs the same treatment for its `--root` and `--tokens`, and
|
|
41
|
+
registering it without `--tokens` is worse than leaving a placeholder: the script
|
|
42
|
+
runs, and skips both `orphan-token` and `color-contrast` on every run. Pass **one
|
|
43
|
+
`--tokens` flag per mode file** — `color-contrast` checks each file as its own
|
|
44
|
+
mode, so a multi-mode system registered with a single `--tokens` silently checks
|
|
45
|
+
only one of them. That table is
|
|
37
46
|
the single source of truth for what a consuming repo gets; do not restate the
|
|
38
47
|
list here.
|
|
39
48
|
|
|
@@ -248,13 +257,83 @@ step and/or a Turbo task). It compares every surface against its record and exit
|
|
|
248
257
|
non-zero on drift; Figma surfaces report `edit-unverified` (checked live in a Figma
|
|
249
258
|
session). Run `docs:check` once here and confirm it passes before handing off.
|
|
250
259
|
|
|
260
|
+
Ensure `verify:check` is wired alongside it, with the repo's real `--tokens`
|
|
261
|
+
paths — not the registered placeholder, and one flag per mode file — so
|
|
262
|
+
`orphan-token` and `color-contrast` actually run instead of reporting as skipped.
|
|
263
|
+
All four rules (`orphan-token`, `state-incomplete`, `name-drift`,
|
|
264
|
+
`color-contrast`) go live now; if the orphan rule misfires on this repo,
|
|
265
|
+
`--skip orphan-token` is the documented answer, and `--skip color-contrast` is
|
|
266
|
+
the same answer on the same terms for a system whose colour roles are named
|
|
267
|
+
differently. Both print on the report's
|
|
268
|
+
`skipped:` line. Dropping `--tokens` is not an equivalent fix — that switches the
|
|
269
|
+
rules off silently instead of visibly. **Run `verify:check` once here, before
|
|
270
|
+
Step 6 promotes anything to `stable`, and confirm it passes before handing
|
|
271
|
+
off — running it here rather than after Step 6 is deliberate, not an
|
|
272
|
+
oversight**: this run's components are still `draft`, so
|
|
273
|
+
`storybook-chromatic-builder` owes them no entry yet and the gate passes; Step 6
|
|
274
|
+
is what promotes them to `stable`, and Step 7 is what records their entries.
|
|
275
|
+
Moving this call after Step 6, or moving Step 7's recording ahead of it, would
|
|
276
|
+
leave the components `stable`, owed an entry, and without one.
|
|
277
|
+
|
|
278
|
+
**A contrast failure the user already accepted.** `token-builder` stops on a pair
|
|
279
|
+
below 4.5:1, and a user may explicitly accept it anyway. That acceptance lives in
|
|
280
|
+
`design-system/proof/token-builder.json` under the repo root, on the `system`
|
|
281
|
+
entry's `contrast-baseline` check, as `accepted: [{ fg, bg, mode }]`. Read that
|
|
282
|
+
file — it may not exist, in which case nothing is accepted and any failure stops
|
|
283
|
+
this step as usual. Then compare **this run's live failures** against it: take
|
|
284
|
+
the `[color-contrast]` lines off the report you just ran, and match each one's
|
|
285
|
+
`fg` and `bg` — the canonical `CONTRAST_PAIRS` paths, identical on both sides and
|
|
286
|
+
needing no translation — against each `accepted` entry's `fg`/`bg`, on the same
|
|
287
|
+
normalized fold (lowercase, alphanumerics only). Then:
|
|
288
|
+
|
|
289
|
+
- **every live failure is an accepted role pair** → register the `verify:check`
|
|
290
|
+
script in `package.json` with `--skip color-contrast`, and say in one line which
|
|
291
|
+
pair was accepted, that contrast is therefore **not checked in CI at all** while
|
|
292
|
+
that flag is there, and that deleting the flag is what re-enables it;
|
|
293
|
+
- **no live failures** → register the script **without** the flag. The pair was
|
|
294
|
+
fixed, or never applied to this repo, and switching the rule off would hide a
|
|
295
|
+
future one;
|
|
296
|
+
- **any live failure whose roles are not in `accepted`** → stop, exactly as this
|
|
297
|
+
step already does.
|
|
298
|
+
|
|
299
|
+
**The live run is the condition, not the presence of the field.** `accepted`
|
|
300
|
+
persists until `token-builder` is re-run, which nothing asks for — so keying off
|
|
301
|
+
the field alone would switch CI's contrast rule off for a pair that now passes,
|
|
302
|
+
while telling the user the flag is what re-enables it.
|
|
303
|
+
|
|
304
|
+
**Matching on the roles, and ignoring the mode, is deliberate here.** `--skip` is
|
|
305
|
+
rule-wide: there is no way to skip contrast in Dark and keep it in Light, so a
|
|
306
|
+
per-mode test would decide nothing a role test does not. This skill also *cannot*
|
|
307
|
+
translate a mode name — the recorded `mode` is the Figma name, and this skill
|
|
308
|
+
neither read the Figma modes nor wrote the DTCG files. Only `token-sync-layer`,
|
|
309
|
+
which did both, matches on the mode. The cost, stated rather than hidden:
|
|
310
|
+
whenever an accepted pair's roles also fail in a mode the user did not accept,
|
|
311
|
+
now or later, the roles still match and CI stays skipped. The sync is the
|
|
312
|
+
backstop — it subtracts per mode, so it stops on that unaccepted mode, and every
|
|
313
|
+
token change goes through it.
|
|
314
|
+
|
|
315
|
+
**That backstop only holds on a tree the sync finished on.** A sync that stopped
|
|
316
|
+
leaves both mode files written and the failure standing, and this skill running
|
|
317
|
+
over that tree would see two failures, match both on roles, and register the flag
|
|
318
|
+
over one nobody accepted. Do not wire the script on a tree whose last sync
|
|
319
|
+
stopped: the pipeline stopped there too, and the fix belongs in Figma before the
|
|
320
|
+
storying stage runs at all.
|
|
321
|
+
|
|
322
|
+
This skill is where the registered script is created, so it is the only place
|
|
323
|
+
that can apply this consequence — `token-builder` runs at folder stage, before
|
|
324
|
+
any repo, and cannot edit a `package.json` that does not exist.
|
|
325
|
+
|
|
251
326
|
## Step 6 — Finalize component status (Figma write-back)
|
|
252
327
|
|
|
253
328
|
A component built and storied here is now **done** — but its Figma doc card was
|
|
254
329
|
stamped `draft` by component-builder and won't change on its own. Once the code
|
|
255
330
|
component renders and its stories build (and the user has approved the result),
|
|
256
331
|
**promote each finalized component to `stable`** so the design system tells the
|
|
257
|
-
truth in both places.
|
|
332
|
+
truth in both places. **This promotion is what makes a component owe a
|
|
333
|
+
`storybook-chromatic-builder` proof entry** — the stage's lifecycle gate reads
|
|
334
|
+
`components.meta[name].status`, and a component still at `draft` owes it
|
|
335
|
+
nothing. Step 7's recording only follows this step for that reason: reorder them
|
|
336
|
+
and a component would be `stable`, owed an entry, and recorded before it exists.
|
|
258
337
|
|
|
259
338
|
**Confirm the write-back once, up front.** Before touching Figma, state the batched
|
|
260
339
|
change in one line and get a yes — *"I'll update the N doc cards in Figma: flip the
|
|
@@ -267,15 +346,25 @@ For every component you finalized in this run, follow the **"Promoting a
|
|
|
267
346
|
component's status (write-back on finalize)"** routine in
|
|
268
347
|
`.throughline/references/figma-component-standards.md` (which also covers
|
|
269
348
|
the `figma_execute` scripting gotchas — `getNodeByIdAsync` and an explicit
|
|
270
|
-
`timeout` for the multi-card write)
|
|
349
|
+
`timeout` for the multi-card write). This write runs inline, so run the
|
|
350
|
+
active-file preflight in `.throughline/references/figma-scripting.md`
|
|
351
|
+
before it:
|
|
271
352
|
|
|
272
353
|
- Set `components.meta[name].status` = `"stable"` and refresh
|
|
273
|
-
`components.meta[name].updatedAt` to today
|
|
354
|
+
`components.meta[name].updatedAt` to today — for every component this pass
|
|
355
|
+
promotes, including ones this run didn't build. **No verification gate reads
|
|
356
|
+
`updatedAt`.** A promotion run moves the timestamp of components other stages
|
|
357
|
+
own, so `proof-missing` scopes itself with the stage's captured `exempt` list
|
|
358
|
+
instead of a date comparison.
|
|
274
359
|
- When promoting status (e.g. draft → stable), also set `status` + `updatedAt` in
|
|
275
360
|
the component's `.doc.json`, recompute its fingerprint, re-run `docs:digest`, and
|
|
276
361
|
re-render the affected surfaces so `docs:check` stays green.
|
|
277
362
|
- If Figma is connected (per `figma.mechanism`), open the component's doc card and
|
|
278
|
-
|
|
363
|
+
locate its status chip under either header shape, as the routine describes: a
|
|
364
|
+
`Status` chip with a `Status Label` text node on to-spec cards, a `Status Pill`
|
|
365
|
+
with one text node on legacy cards. If neither is there, don't guess — tell the
|
|
366
|
+
user that card still shows the old status. Otherwise set the label text to
|
|
367
|
+
`stable`, re-bind the chip fill to
|
|
279
368
|
the **success** semantic color variable (mode-aware, not a hardcoded hex), then
|
|
280
369
|
re-run the canonical doc-card builder against the same card to refresh the
|
|
281
370
|
header date from the `record.updatedAt` already set above (it locates the date
|
|
@@ -296,6 +385,24 @@ Set `storybook.initialized` = `true`, `storybook.chromatic` accordingly,
|
|
|
296
385
|
Step 6.) Note the ongoing loop: new components flow through the
|
|
297
386
|
component-pipeline orchestrator; token changes flow through `/sync-figma-tokens`.
|
|
298
387
|
|
|
388
|
+
**Record the stage entry, one call per component finalized this run:**
|
|
389
|
+
`node .throughline/scripts/verify-check.mjs --record --stage
|
|
390
|
+
storybook-chromatic-builder --subject <Name> --entry <tmp>.json`. Build
|
|
391
|
+
`<tmp>.json` with the attested checks `build` (the story build outcome) and
|
|
392
|
+
`review`, `advancedBecause`, and **the two component-scoped derived results,
|
|
393
|
+
`state-incomplete` and `name-drift`, read off the `verify:check` report Step
|
|
394
|
+
5.5 already ran** — recorded with `method: "derived"`. This is one of two stages
|
|
395
|
+
that record derived results, and it qualifies for the same reason the other does:
|
|
396
|
+
it has already run the gate before recording, so it holds the result rather than
|
|
397
|
+
asserting it (`.throughline/references/proof-bundle.md` names both); the
|
|
398
|
+
entry shape is
|
|
399
|
+
`.throughline/references/proof-bundle.md` — don't restate it here. As
|
|
400
|
+
in `component-builder`, this run's components are already in
|
|
401
|
+
`components.built` by the time this loop runs, so this stage's first `--record`
|
|
402
|
+
captures the whole batch into `exempt`; looping over every finalized component
|
|
403
|
+
here, rather than writing once, is what removes each one from that list instead
|
|
404
|
+
of leaving the rest of the batch grandfathered.
|
|
405
|
+
|
|
299
406
|
## Brownfield: baseline before retrofit + the verification triad
|
|
300
407
|
|
|
301
408
|
On a **retrofit** (`tokens.intakeMode: "retrofit"`), the order of operations and the
|
|
@@ -70,6 +70,10 @@ human between them. Route per
|
|
|
70
70
|
`.throughline/references/agent-routing.md`; never parallelize Figma
|
|
71
71
|
work. If your host has no subagent dispatch, build and verify each tier inline,
|
|
72
72
|
sequentially, as the steps below describe.
|
|
73
|
+
Inline, run the active-file preflight in
|
|
74
|
+
`.throughline/references/figma-scripting.md` before the first write and
|
|
75
|
+
again before replacing anything — the executor's file check does not come with
|
|
76
|
+
you.
|
|
73
77
|
|
|
74
78
|
## Step 1 — Brainstorm the structure (before building anything)
|
|
75
79
|
|
|
@@ -305,9 +309,84 @@ and later Device (`Spacing`/`Typography`). That's the cost of independent axes.
|
|
|
305
309
|
Verify the aliases resolve — use `figma_get_variables` (filtered to the new
|
|
306
310
|
semantic collection, `resolveAliases: true`) to confirm semantic tokens point at
|
|
307
311
|
primitives, not literals; if you read via a `figma_execute` script instead, use
|
|
308
|
-
the async APIs (see the dynamic-page note in Prerequisites).
|
|
309
|
-
|
|
310
|
-
|
|
312
|
+
the async APIs (see the dynamic-page note in Prerequisites).
|
|
313
|
+
|
|
314
|
+
**Then assert contrast, before the checkpoint.** Using that same
|
|
315
|
+
`figma_get_variables` read (filtered to `Color/Semantic`, `resolveAliases: true`),
|
|
316
|
+
convert Figma's 0–1 RGBA channels to hex and **run the shipped module rather than
|
|
317
|
+
doing the arithmetic in the transcript**: shape the per-mode values as
|
|
318
|
+
`[{ mode, values: { "<dotted token path>": "#rrggbb" } }]` and pass them to a
|
|
319
|
+
single `node` invocation that imports `CONTRAST_PAIRS`, `contrastRatio`,
|
|
320
|
+
`composite` and `AA_NORMAL_TEXT` from
|
|
321
|
+
`.throughline/scripts/lib/contrast.mjs` and prints one line per pair per
|
|
322
|
+
mode:
|
|
323
|
+
|
|
324
|
+
node --input-type=module -e "<import the module, read the JSON from
|
|
325
|
+
process.argv[1], loop CONTRAST_PAIRS × modes, print mode/fg/bg/ratio>" '<the JSON>'
|
|
326
|
+
|
|
327
|
+
The module ships precisely so the maths has one home: read the table and call the
|
|
328
|
+
function, and restate neither. The pairs are the semantic roles the system
|
|
329
|
+
already promises (`text/onEmphasis` on `bg/emphasis`, `text/primary` on
|
|
330
|
+
`bg/default`, each status text on its own status background); a role this system
|
|
331
|
+
does not have is not asserted.
|
|
332
|
+
|
|
333
|
+
**The keys are dotted token paths, not Figma variable names.** Figma calls the
|
|
334
|
+
role `text/onEmphasis` in the `Color/Semantic` collection; `CONTRAST_PAIRS`
|
|
335
|
+
spells it `color.text.onEmphasis`, and no normalized fold bridges the two —
|
|
336
|
+
`colorsemantictextonemphasis` is not `colortextonemphasis`. Apply the **Name
|
|
337
|
+
mapping** rule the sync already defines
|
|
338
|
+
(`.throughline/skills/token-sync-layer/SKILL.md`): the category drives
|
|
339
|
+
the top-level group and the tier is dropped, so `Color/Semantic` +
|
|
340
|
+
`text/onEmphasis` → `color.text.onEmphasis`. Both halves of this check have to
|
|
341
|
+
agree on the spelling, or the Figma-time half asserts pairs the CLI half never
|
|
342
|
+
sees, and the two never disagree because they never meet.
|
|
343
|
+
|
|
344
|
+
**A failing pair stops the tier.** Do not proceed to the checkpoint or to Step 4
|
|
345
|
+
with a pair below 4.5:1. Report it in guide voice
|
|
346
|
+
(`.throughline/references/guide-voice.md`): name the two roles, the
|
|
347
|
+
mode, the measured ratio and the required one, then give **one** recommended fix
|
|
348
|
+
— re-point that mode's alias at a primitive with more separation, naming a
|
|
349
|
+
specific step in the ramp that clears — rather than a menu of options. This is
|
|
350
|
+
the "modes can't be built with poor colour contrast" promise, and it is the
|
|
351
|
+
cheapest place in the whole system to catch it: the mode is being defined right
|
|
352
|
+
now, and nothing is built on it yet.
|
|
353
|
+
|
|
354
|
+
**If the user explicitly accepts a failing pair**, continue, and say plainly in
|
|
355
|
+
the same breath what it costs downstream — describing the mechanism that actually
|
|
356
|
+
exists, not a registration that cannot reach the sync:
|
|
357
|
+
|
|
358
|
+
1. The acceptance is recorded in this stage's entry (`contrast-baseline` at
|
|
359
|
+
`result: "fail"`, `advancedBecause` naming it, and the pair itself listed in
|
|
360
|
+
`accepted`), and `token-sync-layer` subtracts exactly that pair from its own
|
|
361
|
+
gate run — so **the next token sync is not blocked** by it, while any *other*
|
|
362
|
+
failing pair, including one introduced later, still stops the sync.
|
|
363
|
+
2. The repo's own registered `verify:check` — the one
|
|
364
|
+
`storybook-chromatic-builder` wires into CI later — derives the same ratio off
|
|
365
|
+
the DTCG source and would fail there. That skill runs the gate itself when it
|
|
366
|
+
wires the script, and if every failure it sees is a pair recorded here as
|
|
367
|
+
accepted, it registers the script with `--skip color-contrast` and says so at
|
|
368
|
+
the time; if the pair has since been fixed, it registers the script without the
|
|
369
|
+
flag. Nothing is owed to a script that does not exist yet, and nothing is
|
|
370
|
+
switched off on the strength of a record alone.
|
|
371
|
+
|
|
372
|
+
Say both, in that order, and then say how each one ends, because they do not end
|
|
373
|
+
the same way. Fixing the pair in Figma **retires the sync's side by itself**: the
|
|
374
|
+
failure stops appearing, the recorded triple matches nothing, and the sync goes
|
|
375
|
+
back to normal on its own — with one expected line on that first sync, where the
|
|
376
|
+
previously recorded `fail` disagrees with a rerun that now passes. That line does
|
|
377
|
+
not stop anything, and the same run re-records the pass. The registered
|
|
378
|
+
`--skip color-contrast` does **not** heal: it is a line in the repo's
|
|
379
|
+
`package.json` and stays until someone removes it, which is the one thing this
|
|
380
|
+
override leaves behind. Say that plainly rather than letting them find it — a
|
|
381
|
+
user who fixes the pair months later and still sees contrast skipped in CI has
|
|
382
|
+
been told the gate is live when it is not. Say the scope too: `--skip` is
|
|
383
|
+
rule-wide, so while that flag is registered CI checks **no** pair, including one
|
|
384
|
+
introduced after the acceptance. The sync is what catches that one, since every
|
|
385
|
+
token change goes through it — but the user should know CI is not the thing
|
|
386
|
+
standing guard meanwhile.
|
|
387
|
+
|
|
388
|
+
Then checkpoint: show the semantic layer and demonstrate the cascade if useful
|
|
389
|
+
("change `gray/50` and `bg/default` follows").
|
|
311
390
|
|
|
312
391
|
Update the manifest: `tokens.semanticBuilt` = `true`, add every semantic
|
|
313
392
|
collection to `tokens.collections`.
|
|
@@ -347,6 +426,34 @@ Update the manifest: set `tokens.stylesBuilt` = `true` and record which style
|
|
|
347
426
|
groups were created in `tokens.styleGroups`. Append `token-builder` to
|
|
348
427
|
`completedSkills`.
|
|
349
428
|
|
|
429
|
+
**Record the stage entry:** `node .throughline/scripts/verify-check.mjs
|
|
430
|
+
--record --stage token-builder --entry <tmp>.json` (subject defaults to
|
|
431
|
+
`"system"`; this stage is not per-component). Build `<tmp>.json` with `changed`
|
|
432
|
+
summarizing the collections and modes created, the attested check
|
|
433
|
+
`contrast-baseline` carrying `result` and a one-line `evidence` naming the
|
|
434
|
+
tightest ratio per mode (`"Light 5.2:1, Dark 4.9:1 — tightest is
|
|
435
|
+
text/onEmphasis on bg/emphasis"`), and `advancedBecause`.
|
|
436
|
+
|
|
437
|
+
When the user accepted a failing pair, the check's `result` is `"fail"`,
|
|
438
|
+
`advancedBecause` names the acceptance, and the check carries
|
|
439
|
+
`accepted: [{ fg, bg, mode }]` — one entry per accepted pair, `fg` and `bg` as the
|
|
440
|
+
dotted token paths `CONTRAST_PAIRS` uses, and `mode` as **the Figma mode name this
|
|
441
|
+
skill just read** (`Light`, `Dark` — the names in the collection it built), not a
|
|
442
|
+
file name. At this point in the pipeline no DTCG file exists to name a mode
|
|
443
|
+
after: the sync writes those later and chooses their names, so a filename here
|
|
444
|
+
would be a guess that never matches. The sync translates this name into its own
|
|
445
|
+
file label before matching, and recording what was actually observed is what
|
|
446
|
+
makes that translation possible. **Write the pair structurally as well as in
|
|
447
|
+
prose** — `advancedBecause` is for whoever reads the bundle later, and `accepted`
|
|
448
|
+
is what `token-sync-layer` matches against, so an acceptance recorded only in
|
|
449
|
+
prose would stop the next sync exactly as if it had never been given. Accept only
|
|
450
|
+
the pairs the user actually accepted; that list is the scope of the override.
|
|
451
|
+
|
|
452
|
+
The entry shape is `.throughline/references/proof-bundle.md` — do not
|
|
453
|
+
restate it here. Write the entry only when the tier actually completed: a run that
|
|
454
|
+
stopped on a failing pair and was not resumed records nothing, the same rule
|
|
455
|
+
`component-builder` follows for a `BLOCKED` component.
|
|
456
|
+
|
|
350
457
|
## Step 5 — Hand off
|
|
351
458
|
|
|
352
459
|
Tell the user what's unlocked and offer natural next steps without forcing them:
|
|
@@ -71,6 +71,8 @@ Copy these from `.throughline/scripts/` into the user's repo **verbatim**
|
|
|
71
71
|
- `lib/crosswalk.mjs` → `packages/tokens/scripts/lib/crosswalk.mjs`
|
|
72
72
|
- `lib/dtcg.mjs` → `packages/tokens/scripts/lib/dtcg.mjs` (required by
|
|
73
73
|
`validate-crosswalk.mjs` — copying the validator without it breaks the gate at import)
|
|
74
|
+
- `lib/source-scan.mjs` → `packages/tokens/scripts/lib/source-scan.mjs` (required by
|
|
75
|
+
`guard-token-removal.mjs` — copying the guard without it breaks it at import)
|
|
74
76
|
- `validate-crosswalk.mjs` → `packages/tokens/scripts/validate-crosswalk.mjs`
|
|
75
77
|
- `build-reverse-index.mjs` → `packages/tokens/scripts/build-reverse-index.mjs`
|
|
76
78
|
- `guard-token-removal.mjs` → `packages/tokens/scripts/guard-token-removal.mjs`
|
|
@@ -50,6 +50,10 @@ variables via `figma_get_variables`, not just a screenshot), gated by a
|
|
|
50
50
|
`.throughline/references/agent-routing.md`; never parallelize Figma
|
|
51
51
|
work. If your host has no subagent dispatch, build and verify the page inline,
|
|
52
52
|
sequentially, as the steps below describe.
|
|
53
|
+
Inline, run the active-file preflight in
|
|
54
|
+
`.throughline/references/figma-scripting.md` before the first write and
|
|
55
|
+
again before replacing anything — the executor's file check does not come with
|
|
56
|
+
you.
|
|
53
57
|
|
|
54
58
|
## Step 1 — Brainstorm the layout (lightly)
|
|
55
59
|
|
|
@@ -103,9 +103,17 @@ Three rules for the multi-collection structure:
|
|
|
103
103
|
and token-sync-layer are a **matched pair** on this — neither is correct alone.
|
|
104
104
|
|
|
105
105
|
This DTCG JSON is the canonical intermediate. Write it to a known location in
|
|
106
|
-
`packages/tokens
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
`packages/tokens/`, **one file per mode**, because every gate downstream reads a
|
|
107
|
+
file as a mode. A single-mode system writes `packages/tokens/dtcg/tokens.json`; a
|
|
108
|
+
light/dark system writes `packages/tokens/dtcg/primitives.json`,
|
|
109
|
+
`packages/tokens/dtcg/semantic.light.json` and
|
|
110
|
+
`packages/tokens/dtcg/semantic.dark.json` — the ramps once, then each mode's
|
|
111
|
+
semantic aliases in its own file. Nesting the modes as groups inside a single
|
|
112
|
+
file (`color.light.*` / `color.dark.*`) is the layout `color-contrast` abstains
|
|
113
|
+
on, and writing a multi-mode system to one `tokens.json` is what leaves the
|
|
114
|
+
registered `verify:check` checking a single mode and reporting that it checked
|
|
115
|
+
one. Do not trust the MCP's built-in CSS/Tailwind exporters for final output —
|
|
116
|
+
route through Style Dictionary so the adapter system governs the result.
|
|
109
117
|
|
|
110
118
|
### Brownfield transforms (learned the hard way)
|
|
111
119
|
|
|
@@ -186,12 +194,12 @@ list. See `.throughline/references/native-adapter-config.md`.
|
|
|
186
194
|
**Execution model — subagent dispatch with model routing.** Generating each
|
|
187
195
|
platform's output is independent and verifiable. If your host supports subagent
|
|
188
196
|
dispatch, dispatch **one `code-executor` per adapter** — each produces its
|
|
189
|
-
platform's files and verifies them (for web
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
platform's files and verifies them (for web and native alike:
|
|
198
|
+
`tokens:validate-output` passes — a clean build is not verification, it is the
|
|
199
|
+
condition under which every known failure mode, web or native, ships silently)
|
|
200
|
+
— then a **`reviewer`** to check each before combining. Choose each subagent's
|
|
201
|
+
model from its role tier per
|
|
202
|
+
`.throughline/references/agent-routing.md` (`code-executor` → fast,
|
|
195
203
|
`reviewer` → balanced), and only dispatch once each adapter's spec is complete
|
|
196
204
|
enough to transcribe. If your host has no subagent dispatch, generate and verify
|
|
197
205
|
each adapter inline instead. For a single platform, run inline either way. This
|
|
@@ -205,23 +213,34 @@ as **build artifacts** — regenerated every sync, never hand-edited. Wire
|
|
|
205
213
|
`packages/tokens/package.json` to export them so the UI package, Storybook, and
|
|
206
214
|
any future app consume them.
|
|
207
215
|
|
|
208
|
-
**Install the
|
|
209
|
-
`.throughline/scripts
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
`packages/tokens/scripts/lib
|
|
215
|
-
`
|
|
216
|
-
|
|
216
|
+
**Install the token toolkit — all four files, as a set, for web and native
|
|
217
|
+
targets alike.** Copy these from `.throughline/scripts/` into the
|
|
218
|
+
user's repo verbatim:
|
|
219
|
+
|
|
220
|
+
- `validate-token-output.mjs` → `packages/tokens/scripts/validate-token-output.mjs`
|
|
221
|
+
- `lib/dtcg.mjs` → `packages/tokens/scripts/lib/dtcg.mjs`
|
|
222
|
+
- `lib/native-literal.mjs` → `packages/tokens/scripts/lib/native-literal.mjs`
|
|
223
|
+
- `lib/sd-native.mjs` → `packages/tokens/scripts/lib/sd-native.mjs`
|
|
224
|
+
|
|
225
|
+
`sd-native.mjs` and the validator both import `dtcg.mjs` and
|
|
226
|
+
`native-literal.mjs`, so copying any of them without the others breaks at
|
|
227
|
+
import. Then register the gate so it stays live on every future sync:
|
|
217
228
|
|
|
218
229
|
```json
|
|
219
230
|
"tokens:validate-output": "node scripts/validate-token-output.mjs --min-match 1"
|
|
220
231
|
```
|
|
221
232
|
|
|
222
233
|
Invoke it once per native output file, passing the same `--source` list that
|
|
223
|
-
file's build used.
|
|
224
|
-
|
|
234
|
+
file's build used. For web output (`shadcn`, `tailwind`, `vanilla-css`), invoke
|
|
235
|
+
it once per mode block instead: name the block with `--block` and pass the
|
|
236
|
+
sources that block was built from. Pass the generated **CSS** file, the one that
|
|
237
|
+
declares the custom properties. For `tailwind` that is the CSS the theme config
|
|
238
|
+
points at, never the config itself: the gate reads custom properties, and a JS
|
|
239
|
+
config has none, so it would match nothing and fail. A MUI theme, or a Tailwind
|
|
240
|
+
build whose only output is a JS config, isn't checked yet (#127). The
|
|
241
|
+
full contract is in `.throughline/scripts/README.md`. `--min-match 1`
|
|
242
|
+
is what makes it a gate: the flag defaults to `0.5`, so without it a 60% match
|
|
243
|
+
rate exits `0`.
|
|
225
244
|
|
|
226
245
|
## Step 4.5 — Icon code sync (install check + custom SVGR)
|
|
227
246
|
|
|
@@ -256,6 +275,69 @@ color.surface.default?`). Surface these prominently. Also surface plain
|
|
|
256
275
|
deletions (consumers will break — that's intentional, but the human should see
|
|
257
276
|
it).
|
|
258
277
|
|
|
278
|
+
### Check contrast before it ships
|
|
279
|
+
|
|
280
|
+
Run the gate once against the DTCG sources this sync just wrote — **always**, with
|
|
281
|
+
no accepted-pair exception. The paragraphs below filter the result, never the run:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
node .throughline/scripts/verify-check.mjs --root <repo root> \
|
|
285
|
+
--tokens <each DTCG source, one flag per mode file> \
|
|
286
|
+
--skip orphan-token --skip state-incomplete --skip name-drift
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**One `--tokens` flag per mode file** — that is what makes each pair get checked
|
|
290
|
+
in every mode. Passing a single file of a multi-mode system checks one mode, and
|
|
291
|
+
says so on the report's `contrast:` line.
|
|
292
|
+
|
|
293
|
+
**Why those three rules are skipped, so nobody removes the flags.** A sync that
|
|
294
|
+
adds a token is adding one nothing in the repo names yet, which is
|
|
295
|
+
`orphan-token`'s definition of an orphan — running it here would fail nearly
|
|
296
|
+
every sync that does its job. `state-incomplete` and `name-drift` are about doc
|
|
297
|
+
records and code surfaces this skill does not touch. All three stay live in
|
|
298
|
+
`storybook-chromatic-builder`'s run of the same gate.
|
|
299
|
+
|
|
300
|
+
**Honour an accepted pair, and only an accepted one.** After the gate has run,
|
|
301
|
+
read `design-system/proof/token-builder.json` under the same `--root` (it may not
|
|
302
|
+
exist, in which case there is nothing to honour). Collect the `accepted` array
|
|
303
|
+
from its `system` entry's `contrast-baseline` check, then drop from this run's
|
|
304
|
+
`color-contrast` failures every one whose `(fg, bg, mode)` matches an entry in it,
|
|
305
|
+
comparing `fg` and `bg` on the same normalized fold the rule matches roles with
|
|
306
|
+
(lowercase, alphanumerics only). **Translate `mode` before comparing it:** the
|
|
307
|
+
recorded value is the Figma mode name `token-builder` saw (`Dark`), and the gate
|
|
308
|
+
labels a mode by its DTCG file's basename (`semantic.dark`). This skill is the one
|
|
309
|
+
that knows both — it read the Figma modes and it wrote the files — so map each
|
|
310
|
+
Figma mode to the file it generated for that mode, and compare against that label.
|
|
311
|
+
Untranslated, the triple can never match and the override path silently does not
|
|
312
|
+
exist. Match the triple exactly, and subtract nothing else.
|
|
313
|
+
|
|
314
|
+
**What this reads.** The gate has no machine-readable output, so this operates on
|
|
315
|
+
`formatReport`'s failure lines — `<fg> on <bg> is <ratio>:1 in <mode> …` — which
|
|
316
|
+
carry all three fields in fixed positions. That is machine-formatted output with a
|
|
317
|
+
stated shape, not prose. Do **not** treat `result: "fail"` on its own as the
|
|
318
|
+
condition, and do **not** pattern-match `advancedBecause`: the first switches off a
|
|
319
|
+
whole rule on the strength of one pair, and the second reads prose written for a
|
|
320
|
+
human as though it were a test.
|
|
321
|
+
|
|
322
|
+
**Then judge what is left.** Nothing remaining → proceed, and say in one line which
|
|
323
|
+
pair was accepted and is not being re-litigated, both in the sync's message and in
|
|
324
|
+
the PR body. That line is where the override stays visible, and it is the only
|
|
325
|
+
place, since a subtracted failure is by definition not on the report. Anything
|
|
326
|
+
remaining → stop, exactly as if there had been no acceptance: a pair the user never
|
|
327
|
+
saw, in any mode, is the case this rule exists for, and an acceptance given for one
|
|
328
|
+
pair must not carry another one through. An `accepted` list that matches nothing —
|
|
329
|
+
because the pair was fixed, or was never recorded properly — subtracts nothing and
|
|
330
|
+
the sync behaves as though there were no acceptance at all, which is both the
|
|
331
|
+
fail-safe direction and how an acceptance retires itself once the pair clears.
|
|
332
|
+
|
|
333
|
+
**A `color-contrast` failure stops the sync.** Do not open the PR. Report the
|
|
334
|
+
failing pair and mode in guide voice
|
|
335
|
+
(`.throughline/references/guide-voice.md`), and say the fix is in Figma —
|
|
336
|
+
re-point that mode's alias — not in the generated output, which is a build
|
|
337
|
+
artifact. **Any other failure in the report does not stop the sync**: surface it to
|
|
338
|
+
the user and summarize it in the PR body, but the token sync owns the token tier,
|
|
339
|
+
and a component's missing proof entry is not its to block on.
|
|
340
|
+
|
|
259
341
|
## Step 6 — Land it as a reviewable PR (never silent)
|
|
260
342
|
|
|
261
343
|
Never overwrite token files silently. The output of a sync is a **pull request**
|
|
@@ -278,6 +360,44 @@ TurboSnap section in `storybook-chromatic-builder`.
|
|
|
278
360
|
Update the manifest: `sync.lastRun` (timestamp), `tokens.lastSync`, confirm
|
|
279
361
|
`sync.platforms`. Append `token-sync-layer` to `completedSkills`.
|
|
280
362
|
|
|
363
|
+
Alongside that, record the stage entry: `node
|
|
364
|
+
.throughline/scripts/verify-check.mjs --record --stage
|
|
365
|
+
token-sync-layer --entry <tmp>.json` (subject defaults to `"system"`, since this
|
|
366
|
+
stage isn't per-component). Build `<tmp>.json` with `changed` summarizing tokens
|
|
367
|
+
added, changed, deleted and probable renames, and the attested
|
|
368
|
+
`tokens:validate-output` result as a check, plus `advancedBecause`. The entry
|
|
369
|
+
shape is `.throughline/references/proof-bundle.md` — don't restate it
|
|
370
|
+
here.
|
|
371
|
+
|
|
372
|
+
The entry also carries the **derived** check `color-contrast`, its result read off
|
|
373
|
+
the Step 5 report, with `evidence` naming the modes checked and the tightest
|
|
374
|
+
ratio. Record it whenever the report's `contrast:` line compared at least one
|
|
375
|
+
pair. When it compared none (the report says `contrast-rule-inert`, or the run
|
|
376
|
+
passed `--skip color-contrast`) there is no result to record: omit the check, and
|
|
377
|
+
say why in `advancedBecause`. A `pass` written there would describe a comparison
|
|
378
|
+
that never happened, and the gate could never contradict it.
|
|
379
|
+
|
|
380
|
+
**When a failure was subtracted, record `result: "fail"` — what the gate actually
|
|
381
|
+
reported** — with `advancedBecause` naming the accepted pair as the reason the sync
|
|
382
|
+
proceeded anyway, and `evidence` saying which pair was subtracted. Do not record
|
|
383
|
+
`pass` on the grounds that nothing remained after subtraction: the gate does not
|
|
384
|
+
know about subtraction, the pair still fails on disk, and the next sync's own gate
|
|
385
|
+
run recomputes it, so a recorded `pass` would raise `proof-contradicted` on every
|
|
386
|
+
later sync report. A stored result that disagrees with the file it claims to
|
|
387
|
+
describe is an attestation wearing a derived label.
|
|
388
|
+
|
|
389
|
+
**Expect one `proof-contradicted` line on the first sync after the user fixes the
|
|
390
|
+
pair**, and say why when you relay it: the stored `fail` is now disagreed with by a
|
|
391
|
+
rerun that passes, which `checkProof` reports in both directions. It does not stop
|
|
392
|
+
the sync, and this step re-records the pass on that same run, so it clears itself
|
|
393
|
+
in one pass. Relaying an expected, self-healing line as a problem is how a user is
|
|
394
|
+
taught to distrust the gate.
|
|
395
|
+
|
|
396
|
+
**The ordering is the point.** The gate runs in Step 5 and the record is written
|
|
397
|
+
here in Step 6, and it is that order which makes recording a derived result honest:
|
|
398
|
+
this stage has the gate's output in hand rather than asserting it, which is the one
|
|
399
|
+
and only reason a stage may record `method: "derived"`.
|
|
400
|
+
|
|
281
401
|
## Step 7 — Install the `/sync-figma-tokens` command
|
|
282
402
|
|
|
283
403
|
Set up the reusable command (the plugin ships it in `commands/`) so the user can
|
|
@@ -27,6 +27,9 @@ Ask which component to document (e.g. "Button"), then:
|
|
|
27
27
|
verify its returned summary and stamp `surfaces.docCard.{src,render,renderer}`
|
|
28
28
|
from it), and (if the repo/code side exists) render MDX/JSDoc and run
|
|
29
29
|
`docs:digest` per the `storybook-chromatic-builder` render step.
|
|
30
|
+
Before the Figma writes, run the active-file preflight in
|
|
31
|
+
`.throughline/references/figma-scripting.md` — this command writes
|
|
32
|
+
inline, so no executor checks the file for you.
|
|
30
33
|
3. **Reconcile drift.** Before trusting `docs:check`, confirm the repo's copy of
|
|
31
34
|
the doc scripts is current: compare `DOC_CARD_RENDERER_VERSION` in the repo's
|
|
32
35
|
`scripts/lib/doc-card-plan.mjs` against the same constant in
|
|
@@ -36,7 +39,13 @@ Ask which component to document (e.g. "Button"), then:
|
|
|
36
39
|
doc scripts from the plugin copy. Refresh the whole set and re-check the npm
|
|
37
40
|
registrations, both per **Documentation scripts — install as a set** in
|
|
38
41
|
`.throughline/scripts/README.md` — a refreshed file whose script was
|
|
39
|
-
never registered is the same failure in a new place.
|
|
42
|
+
never registered is the same failure in a new place. Two of the registrations,
|
|
43
|
+
`adherence:check` and `verify:check`, carry placeholder paths and a
|
|
44
|
+
`--package` specifier: substitute the repo's real ones per the paragraphs under
|
|
45
|
+
that table, run each once, and use the documented `--skip` for a rule the
|
|
46
|
+
system doesn't fit, rather than registering a placeholder that fails every
|
|
47
|
+
run. A repo refreshed from before 0.20.0 gets both for the first time, so say
|
|
48
|
+
so. Run `docs:check` (with the
|
|
40
49
|
refreshed scripts, if any). For each drifted surface, offer a per-item
|
|
41
50
|
choice — **re-render** (canonical wins) or **pull-back**
|
|
42
51
|
(fold the surface edit into the record) — and land the result as a reviewable
|