baldart 4.64.0 → 4.65.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/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/VERSION +1 -1
- package/bin/baldart.js +15 -0
- package/framework/.claude/agents/code-reviewer.md +25 -11
- package/framework/.claude/agents/codebase-architect.md +13 -5
- package/framework/.claude/agents/doc-reviewer.md +26 -12
- package/framework/.claude/agents/ui-expert.md +18 -10
- package/framework/.claude/skills/context-primer/SKILL.md +7 -0
- package/framework/.claude/skills/design-system-init/SKILL.md +164 -156
- package/framework/.claude/skills/design-system-init/scripts/component-spec.template.md +26 -3
- package/framework/.claude/skills/design-system-init/scripts/extract-manifest.mjs +151 -0
- package/framework/agents/component-manifest-schema.md +145 -0
- package/framework/agents/design-system-protocol.md +63 -23
- package/framework/agents/index.md +2 -0
- package/framework/docs/COMPONENT-MANIFEST-LAYER.md +100 -0
- package/framework/routines/ds-drift.routine.yml +19 -12
- package/framework/templates/baldart.config.template.yml +22 -0
- package/package.json +1 -1
- package/src/commands/configure.js +54 -0
- package/src/commands/doctor.js +85 -0
- package/src/commands/tokens.js +80 -0
- package/src/commands/update.js +7 -0
- package/src/utils/token-emitters/README.md +36 -0
- package/src/utils/token-emitters/css.js +18 -0
- package/src/utils/token-emitters/index.js +47 -0
- package/src/utils/token-emitters/ts.js +32 -0
- package/src/utils/tokens-generator.js +165 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Component Manifest Schema
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Define the **machine-readable HEAD** every per-component spec
|
|
6
|
+
(`${paths.design_system}/components/<Name>.md`) carries so agents discover and
|
|
7
|
+
reuse primitives by reading a small, structured frontmatter **on demand** — not
|
|
8
|
+
by parsing a monolithic `INDEX.md` or the component source. This module is the
|
|
9
|
+
**single source of truth** for *which fields a component spec needs* and *in what
|
|
10
|
+
state*; every writer and every consumer cites it.
|
|
11
|
+
|
|
12
|
+
It is the design-system analogue of `agents/card-schema.md`: a schema that ≥6
|
|
13
|
+
surfaces produce or consume (`design-system-init`, `doc-reviewer`, `ui-expert`,
|
|
14
|
+
`code-reviewer`, `codebase-architect`, `context-primer`, `ds-drift`) and must
|
|
15
|
+
therefore live in one place that none of them re-describe.
|
|
16
|
+
|
|
17
|
+
## Scope
|
|
18
|
+
|
|
19
|
+
**In**: the frontmatter field table, the deterministic-vs-agentic split, the
|
|
20
|
+
regeneration contract, the on-demand read contract, the thin-INDEX router shape,
|
|
21
|
+
and the transition-leniency rule.
|
|
22
|
+
|
|
23
|
+
**Out**: the *enforcement* (when a missing/stale spec blocks) and the drift codes
|
|
24
|
+
(`DS_INDEX_DRIFT` / `DS_COMPONENT_STALE` / `DS_TOKENS_DRIFT`) — those stay in
|
|
25
|
+
`agents/design-system-protocol.md` (§ Post-Intervention Coherence Check). This
|
|
26
|
+
module defines the *fields those codes are checked against*; it **never copies**
|
|
27
|
+
the codes or the gate (that would re-introduce the drift the SSOT split prevents).
|
|
28
|
+
Token *values* and the DTCG source live in the token layer
|
|
29
|
+
(`${paths.design_tokens}` → `.tokens.json`); this module references token **IDs**,
|
|
30
|
+
never values.
|
|
31
|
+
|
|
32
|
+
## Gating
|
|
33
|
+
|
|
34
|
+
Same as the design-system protocol: meaningful only when
|
|
35
|
+
`features.has_design_system: true`. There is **no separate feature flag** — the
|
|
36
|
+
manifest rides on the design-system layer. When the flag is `false`, component
|
|
37
|
+
specs (and this schema) do not apply.
|
|
38
|
+
|
|
39
|
+
## The frontmatter HEAD
|
|
40
|
+
|
|
41
|
+
Additive: a YAML frontmatter block is prepended to the EXISTING prose spec. The
|
|
42
|
+
prose body (Purpose, Props table, Accessibility, Anti-patterns, …) is preserved
|
|
43
|
+
verbatim — the HEAD makes the *mechanical* half machine-addressable; the prose
|
|
44
|
+
stays the human-readable long form.
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
---
|
|
48
|
+
# === DETERMINISTIC — extracted from source (TS types) + git; regenerable, never hand-curated ===
|
|
49
|
+
name: Button # exported component name
|
|
50
|
+
source: components/ui/Button.tsx # path relative to repo root
|
|
51
|
+
source_sha: a1b2c3d # git blob sha of source at last generation (drift anchor)
|
|
52
|
+
status: stable # stable | experimental | deprecated (default stable; `deprecated` is a hand override)
|
|
53
|
+
category: primitive # primitive | composite | feature (heuristic on import fan-in; hand-overridable)
|
|
54
|
+
props: # from the TS prop type / interface
|
|
55
|
+
variant: { type: "'primary'|'ghost'|'destructive'", required: false, default: "'primary'" }
|
|
56
|
+
size: { type: "'sm'|'md'|'lg'", required: false, default: "'md'" }
|
|
57
|
+
disabled: { type: boolean, required: false, default: false }
|
|
58
|
+
variants: [primary, ghost, destructive] # enumerated literal union of the `variant` prop
|
|
59
|
+
composes: [Slot, Spinner] # in-registry primitives imported by this component
|
|
60
|
+
# === AGENTIC — curated by doc-reviewer; survives regeneration, never clobbered when non-empty ===
|
|
61
|
+
purpose: "Primary action trigger; one primary per surface."
|
|
62
|
+
token_bindings: [color.action.primary, radius.md, space.2, motion.fast] # DTCG token IDs (not values)
|
|
63
|
+
a11y: "Icon-only requires aria-label; ≥44px target; respects prefers-reduced-motion."
|
|
64
|
+
related: [IconButton, Link] # sibling primitives (not composition)
|
|
65
|
+
must_rules: ["MUST consume color.action.primary", "MUST set aria-label when icon-only"]
|
|
66
|
+
last_verified: 2026-06-23 # set by doc-reviewer / ds-drift on semantic re-verify
|
|
67
|
+
owner: doc-reviewer # curation owner (constant; documents the SSOT)
|
|
68
|
+
---
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Field origin
|
|
72
|
+
|
|
73
|
+
| Origin | Fields | Rule |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| **Deterministic** (TS/git) | `name`, `source`, `source_sha`, `status` (default), `category` (heuristic), `props`, `variants`, `composes` | Always safe to **overwrite** on regeneration — they are derived. Extracted by a pure Node script (no agent), so the path is **portable to Codex**. |
|
|
76
|
+
| **Agentic** (doc-reviewer) | `purpose`, `token_bindings`, `a11y`, `related`, `must_rules`, `last_verified`; plus `status: deprecated` as a judgment | **Never clobbered** when non-empty. Empty agentic fields = "not yet enriched", filled later by `doc-reviewer` / `ds-drift`, never auto-invented. |
|
|
77
|
+
|
|
78
|
+
## Read contract (the context win)
|
|
79
|
+
|
|
80
|
+
1. An agent doing reuse-discovery reads the thin `INDEX.md` (router) → then **only
|
|
81
|
+
the frontmatter** of the N specs in scope (front-load the first `---…---`
|
|
82
|
+
block), NOT the prose body and NOT the source.
|
|
83
|
+
2. The prose body is read only when the agent is about to **modify** that
|
|
84
|
+
component.
|
|
85
|
+
3. `token_bindings` are IDs into `.tokens.json`, so one already-read tokens file
|
|
86
|
+
answers every component's bindings — no per-component token re-read, and the
|
|
87
|
+
binding stays stable when a token *value* changes.
|
|
88
|
+
|
|
89
|
+
This is the whole point: replace "read a 33KB monolith + fall to source" with
|
|
90
|
+
"read a small router + N small frontmatters".
|
|
91
|
+
|
|
92
|
+
## Thin INDEX router
|
|
93
|
+
|
|
94
|
+
`${paths.design_system}/INDEX.md` is **generated** and holds only routing data —
|
|
95
|
+
one row per primitive: `name | source | one-line purpose | spec link`. It carries
|
|
96
|
+
NO human-curated judgment (the Authority Matrix moved to
|
|
97
|
+
`design-system-protocol.md`; per-component detail lives in each spec). Because it
|
|
98
|
+
is generated, it is always safe to regenerate from the specs — never hand-edit it.
|
|
99
|
+
|
|
100
|
+
## Regeneration contract
|
|
101
|
+
|
|
102
|
+
- **Anchor**: `source_sha`. A regeneration run skips a component whose source is
|
|
103
|
+
unchanged AND whose spec already has a HEAD (no-op); regenerates only
|
|
104
|
+
stale/missing ones.
|
|
105
|
+
- **Deterministic fields**: overwritten from current source every run.
|
|
106
|
+
- **Agentic fields**: preserved unless empty; an empty field is a candidate for
|
|
107
|
+
enrichment, never a reason to clobber a filled one.
|
|
108
|
+
- **Idempotent + resumable**: a kill mid-run leaves a mix of HEAD-bearing and
|
|
109
|
+
prose-only specs — all valid (see leniency below). No half-written state.
|
|
110
|
+
|
|
111
|
+
## Transition leniency (do not raise the floor)
|
|
112
|
+
|
|
113
|
+
The HEAD is an **upgrade target, not a new floor**. During and after adoption:
|
|
114
|
+
|
|
115
|
+
- A **prose-only spec** (no HEAD) is still a valid spec — it does NOT trip
|
|
116
|
+
`DS_COMPONENT_STALE` merely for lacking a HEAD.
|
|
117
|
+
- **Empty deterministic fields** (e.g. a JS/Vue/Svelte consumer with no TS types,
|
|
118
|
+
or an unresolved prop) are **advisory** — "not yet enriched", never a blocking
|
|
119
|
+
finding.
|
|
120
|
+
- A **missing** spec for a new primitive remains exactly the `DS_INDEX_DRIFT` it
|
|
121
|
+
is today — the manifest adds no new failure mode, it only makes the existing
|
|
122
|
+
spec richer where the data is available.
|
|
123
|
+
|
|
124
|
+
This keeps consumers that have not yet upgraded (and non-TS stacks) from becoming
|
|
125
|
+
*more* broken than before the manifest existed.
|
|
126
|
+
|
|
127
|
+
## Producers & consumers
|
|
128
|
+
|
|
129
|
+
- **Bootstrap / upgrade**: `/design-system-init` (deterministic extract + agentic
|
|
130
|
+
enrich; greenfield and upgrade modes).
|
|
131
|
+
- **Per-task write**: `ui-expert` / `ui-design` / `frontend-design` in the
|
|
132
|
+
Post-Intervention Coherence Check — a new/modified primitive regenerates its
|
|
133
|
+
HEAD in the same change.
|
|
134
|
+
- **Curation owner**: `doc-reviewer` (agentic fields).
|
|
135
|
+
- **Per-merge gate**: `code-reviewer` (reads HEAD-first; enforces the drift codes
|
|
136
|
+
per the protocol).
|
|
137
|
+
- **Read-first discovery**: `codebase-architect`, `context-primer`.
|
|
138
|
+
- **Weekly net**: `ds-drift` (compares `source_sha`/HEAD against source).
|
|
139
|
+
|
|
140
|
+
## See also
|
|
141
|
+
|
|
142
|
+
- `agents/design-system-protocol.md` — the enforcement gate + drift codes + read
|
|
143
|
+
cascade this schema feeds.
|
|
144
|
+
- `agents/card-schema.md` — the sibling schema module this one mirrors.
|
|
145
|
+
- `agents/project-context.md` — `baldart.config.yml` + overlay resolution.
|
|
@@ -47,13 +47,21 @@ To bootstrap a registry on a project that does not yet have one, invoke the
|
|
|
47
47
|
|
|
48
48
|
## Canonical sources (resolve via `baldart.config.yml`)
|
|
49
49
|
|
|
50
|
-
- `${paths.design_system}/INDEX.md` —
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
- `${paths.design_system}/INDEX.md` — **generated** thin router: one row per
|
|
51
|
+
primitive (`name | source | one-line purpose | spec link`). Entry point for
|
|
52
|
+
every UI task. Human-curated judgement does NOT live here (it moved to the
|
|
53
|
+
Authority Matrix below + each spec's frontmatter) — never hand-edit the INDEX.
|
|
54
|
+
- `${paths.design_tokens}` → `.tokens.json` — **the token SSOT**, W3C DTCG
|
|
55
|
+
format (`$type`/`$value`). The stack-native artifact (`tokens.ts` / CSS custom
|
|
56
|
+
properties) is **generated** from it via `baldart tokens build`; editing that
|
|
57
|
+
generated output by hand is drift (see Token Contract).
|
|
58
|
+
- `${paths.design_system}/tokens-reference.md` — **generated/narrative** human
|
|
59
|
+
view of the tokens. NOT a third source of values: either generated from
|
|
60
|
+
`.tokens.json` or pure narrative ("how to use tokens"), never hand-authored
|
|
61
|
+
values that duplicate the DTCG source.
|
|
62
|
+
- `${paths.design_system}/components/<Name>.md` — per-component spec. Carries a
|
|
63
|
+
machine-readable frontmatter HEAD (see `agents/component-manifest-schema.md`)
|
|
64
|
+
+ prose body (props, variants, accessibility, anti-patterns). One per primitive.
|
|
57
65
|
- `${paths.design_system}/patterns/*.md` — opt-in pattern docs (theming,
|
|
58
66
|
overlays, motion choreography, multi-tenant pairing rules).
|
|
59
67
|
- `${paths.ui_guidelines}` — brand voice, typography, philosophy. Always read.
|
|
@@ -64,9 +72,12 @@ To bootstrap a registry on a project that does not yet have one, invoke the
|
|
|
64
72
|
|
|
65
73
|
Before producing any new UI code, mockup, or design review:
|
|
66
74
|
|
|
67
|
-
1. **BLOCKING** — read `${paths.design_system}/INDEX.md
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
1. **BLOCKING** — read `${paths.design_system}/INDEX.md` (the thin router) to
|
|
76
|
+
locate the primitives in scope, then read **only the frontmatter HEAD** of
|
|
77
|
+
each in-scope `components/<Name>.md` (per `agents/component-manifest-schema.md`)
|
|
78
|
+
— not the prose body, not the source. Read the prose body only when you are
|
|
79
|
+
about to modify that primitive. Also read the Authority Matrix (below) + any
|
|
80
|
+
pattern docs the router points to.
|
|
70
81
|
2. **BLOCKING** — read `${paths.ui_guidelines}` for visual language, typography,
|
|
71
82
|
accessibility, and brand voice. The `identity.design_philosophy` (when set)
|
|
72
83
|
is the lens through which these guidelines apply.
|
|
@@ -120,10 +131,26 @@ any code or mockup produced under this protocol:
|
|
|
120
131
|
the `motion-design` skill).
|
|
121
132
|
|
|
122
133
|
The exact token consumption mechanism (CSS custom properties, Tailwind theme
|
|
123
|
-
extend, theme provider) is project-specific — defer to
|
|
124
|
-
`${paths.design_system}/tokens-reference.md` and the project's chosen
|
|
134
|
+
extend, theme provider) is project-specific — defer to the project's chosen
|
|
125
135
|
mechanism.
|
|
126
136
|
|
|
137
|
+
**Token SSOT inversion (when `paths.design_tokens` is set).** The canonical
|
|
138
|
+
token source is the DTCG `.tokens.json` at `${paths.design_tokens}`. The
|
|
139
|
+
stack-native artifact agents/components consume (`tokens.ts`, a CSS custom-property
|
|
140
|
+
sheet, …) is **generated** from it by `baldart tokens build` and carries a
|
|
141
|
+
`// baldart-generated from .tokens.json — do not edit` marker. Consequences:
|
|
142
|
+
|
|
143
|
+
- To add/change a token, edit `.tokens.json` and regenerate — never hand-edit the
|
|
144
|
+
generated output.
|
|
145
|
+
- `DS_TOKENS_DRIFT` is now mechanically checkable: the generated output must equal
|
|
146
|
+
`baldart tokens build` from the current `.tokens.json`. A hand-edit to the
|
|
147
|
+
generated file (output ≠ rebuild) is the violation — port it to `.tokens.json`.
|
|
148
|
+
- A `token_bindings` ID in a component HEAD must resolve to a real node in
|
|
149
|
+
`.tokens.json`; a dangling ID is `DS_TOKENS_DRIFT`.
|
|
150
|
+
|
|
151
|
+
When `paths.design_tokens` is unset, the project has not adopted the DTCG SSOT —
|
|
152
|
+
fall back to the project's existing token file as the source (no inversion).
|
|
153
|
+
|
|
127
154
|
## Reference Tables (embed)
|
|
128
155
|
|
|
129
156
|
These tables are the SSOT for the *numeric ranges* every agent/skill should
|
|
@@ -306,13 +333,16 @@ visually uneven steps because HSL lightness is not perceptual.
|
|
|
306
333
|
|
|
307
334
|
## Authority Matrix
|
|
308
335
|
|
|
309
|
-
|
|
310
|
-
|
|
336
|
+
This matrix is **canonical here** (protocol-level), not in `INDEX.md` — the INDEX
|
|
337
|
+
is a generated router and cannot hold curated judgement. For each primitive, it
|
|
338
|
+
declares who is authoritative over which dimension:
|
|
311
339
|
|
|
312
|
-
- **Visual authority** — colors, spacing, shadows, radii: the
|
|
313
|
-
tokens
|
|
340
|
+
- **Visual authority** — colors, spacing, shadows, radii, motion: the DTCG token
|
|
341
|
+
SSOT (`${paths.design_tokens}` → `.tokens.json`, surfaced via the generated
|
|
342
|
+
token reference). Agents/skills may not deviate.
|
|
314
343
|
- **Behavioral authority** — props, variants, accessibility hooks: the
|
|
315
|
-
per-component spec
|
|
344
|
+
per-component spec (its frontmatter HEAD for the machine-readable contract, its
|
|
345
|
+
prose body for rationale).
|
|
316
346
|
- **Composition authority** — how primitives are combined into patterns: the
|
|
317
347
|
pattern docs.
|
|
318
348
|
|
|
@@ -441,12 +471,19 @@ For each item in the diff that touched a visual surface, run these 4 checks:
|
|
|
441
471
|
in the same change. Missing spec = `DS_INDEX_DRIFT` finding, task blocked.
|
|
442
472
|
2. **Modified primitive ⇒ updated spec**: did the change alter a primitive's
|
|
443
473
|
props / variants / accessibility behavior? If yes, the corresponding
|
|
444
|
-
`components/<Name>.md` MUST be updated to match
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
474
|
+
`components/<Name>.md` MUST be updated to match — including its **frontmatter
|
|
475
|
+
HEAD** (deterministic fields `props`/`variants`/`composes`/`source_sha`
|
|
476
|
+
regenerated from source; agentic fields re-verified). Stale spec =
|
|
477
|
+
`DS_COMPONENT_STALE` finding, task blocked. (A prose-only spec lacking a HEAD
|
|
478
|
+
is NOT stale merely for that — see `agents/component-manifest-schema.md`
|
|
479
|
+
§ Transition leniency.)
|
|
480
|
+
3. **New / changed token ⇒ updated SSOT**: did the change introduce or modify a
|
|
481
|
+
token (color, spacing, shadow, radius, motion)? If `paths.design_tokens` is
|
|
482
|
+
set, the change MUST be made in `.tokens.json` and the stack-native output
|
|
483
|
+
regenerated via `baldart tokens build` (a hand-edit to the generated output is
|
|
484
|
+
the drift). Otherwise update the project's token source + reference. Either
|
|
485
|
+
way, a dangling `token_bindings` ID is drift. Stale = `DS_TOKENS_DRIFT`,
|
|
486
|
+
task blocked.
|
|
450
487
|
4. **Surfaced silent primitive ⇒ added to INDEX**: during the work, did you
|
|
451
488
|
reuse a primitive that exists in the source tree but is **not** in
|
|
452
489
|
`INDEX.md`? If yes, add it to the INDEX in the same change (or open a
|
|
@@ -529,6 +566,9 @@ When changing the BLOCKING cascade, change it here — not in the consumers.
|
|
|
529
566
|
|
|
530
567
|
## See also
|
|
531
568
|
|
|
569
|
+
- `framework/agents/component-manifest-schema.md` — the machine-readable
|
|
570
|
+
frontmatter HEAD on each `components/<Name>.md`, the on-demand read contract,
|
|
571
|
+
and the regeneration + transition-leniency rules this gate is checked against.
|
|
532
572
|
- `framework/agents/project-context.md` — always-ask / never-assume contract
|
|
533
573
|
and the `baldart.config.yml` resolution flow.
|
|
534
574
|
- `framework/agents/design-review.md` — design-review workflow and report
|
|
@@ -20,6 +20,7 @@ Route agents to the right module with minimal reading.
|
|
|
20
20
|
- If touching database schema or fields -> read `agents/data-model.md` and `${paths.references_dir}/data-model.md`.
|
|
21
21
|
- If touching UI pages/routes or flows -> read `${paths.references_dir}/ui/index.md` (then specific UI module for your domain).
|
|
22
22
|
- If touching ANY visual surface (component, page, mockup, design review) and `features.has_design_system: true` -> read `agents/design-system-protocol.md` and treat the BLOCKING cascade (`${paths.design_system}/INDEX.md` + `tokens-reference.md` + `components/<Name>.md`) as mandatory pre-work. When the flag is `false`, recommend `/design-system-init` to scaffold a registry.
|
|
23
|
+
- If CREATING, UPDATING, or READING a per-component spec (`${paths.design_system}/components/<Name>.md`) -> read `agents/component-manifest-schema.md` for the machine-readable frontmatter HEAD (deterministic-from-TS + agentic fields) and the on-demand read contract (INDEX router → frontmatter-first, prose only when modifying). The drift codes + gate stay in `design-system-protocol.md`.
|
|
23
24
|
- If touching design-review workflows or UI guidelines -> read `agents/design-review.md` and project-specific UI guidelines.
|
|
24
25
|
- If touching architecture, auth, or tech stack -> read `agents/architecture.md`.
|
|
25
26
|
- If touching workflow/process/commits/backlog -> read `agents/workflows.md`.
|
|
@@ -72,6 +73,7 @@ When adding or updating agents, update REGISTRY.md — not this file.
|
|
|
72
73
|
- `agents/i18n-protocol.md` — Internationalization discipline: no hardcoded user-facing strings, context registry (`paths.i18n_registry`) + ICU + context-aware translation; BLOCKING anti-hardcoded lint gate (since v4.52.0, gated on `features.has_i18n`)
|
|
73
74
|
- `agents/design-system-protocol.md` — Registry-first discipline for UI work: BLOCKING cascade on `INDEX.md` + `tokens-reference.md` + `components/<Name>.md` (since v3.11.0, gated on `features.has_design_system`)
|
|
74
75
|
- `agents/card-schema.md` — Atomic Card Baseline Schema: the universal, profile-aware (epic/child/standalone) field contract every backlog card satisfies, plus the consumer HALT/BACK-FILL/WARN contract (since v4.35.0)
|
|
76
|
+
- `agents/component-manifest-schema.md` — Component Manifest Schema: the machine-readable frontmatter HEAD on each `components/<Name>.md` (deterministic-from-TS + agentic fields), on-demand read contract, regeneration + transition-leniency rules (since v4.65.0, gated on `features.has_design_system`)
|
|
75
77
|
- `agents/return-contract-protocol.md` — Subagent return-message economy: COMPACT (bounded headline + `path:line` findings + disk pointer) vs FULL, persist-then-summarize — the input-side twin of `effort-protocol.md` (since v4.59.0)
|
|
76
78
|
|
|
77
79
|
## Where to Document (Decision Tree)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Component Manifest + DTCG Token Layer
|
|
2
|
+
|
|
3
|
+
Authoritative design, lifecycle, and invariants for the machine-readable
|
|
4
|
+
component-manifest layer and the DTCG token SSOT (since v4.65.0). Companion to
|
|
5
|
+
the textual SSOTs: `framework/agents/component-manifest-schema.md` (the HEAD
|
|
6
|
+
schema) and `framework/agents/design-system-protocol.md` (the gate + cascade).
|
|
7
|
+
|
|
8
|
+
## 1. Why this exists
|
|
9
|
+
|
|
10
|
+
On a real consumer, UI component discovery was slow: the registry's `INDEX.md`
|
|
11
|
+
had grown to a **33KB monolith** read in full on every UI task, there was no
|
|
12
|
+
`tokens-reference.md` (tokens parsed from a large hand-authored token file), and
|
|
13
|
+
**0 of ~135 components** had a per-component spec — so every "what does this
|
|
14
|
+
primitive do / can I reuse it" lookup degraded to reading component **source**.
|
|
15
|
+
Graphify is
|
|
16
|
+
the wrong lever here: component lookup is a single-symbol query, which the
|
|
17
|
+
code-graph protocol routes to LSP/Grep, not the graph.
|
|
18
|
+
|
|
19
|
+
The fix is not a new layer — the framework already prescribes per-component specs
|
|
20
|
+
(`components/<Name>.md`, owned by `doc-reviewer`, gated by `code-reviewer`,
|
|
21
|
+
audited by `ds-drift`). It is **(a)** making those specs machine-readable and
|
|
22
|
+
read **on demand**, and **(b)** giving tokens a real, interoperable SSOT.
|
|
23
|
+
|
|
24
|
+
## 2. What BALDART adds vs what the stack already ships
|
|
25
|
+
|
|
26
|
+
| Concern | Stack / status quo | What BALDART adds |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| Component contract | prose `components/<Name>.md` (often absent) | a YAML **frontmatter HEAD** — deterministic (from TS) + agentic (curated) — read on demand |
|
|
29
|
+
| Discovery entry | monolithic INDEX read in full | **thin generated INDEX router** → frontmatter-first reads of only the N specs in scope |
|
|
30
|
+
| Token source | ad-hoc `tokens.ts` / CSS / Tailwind | **W3C DTCG `.tokens.json`** as SSOT |
|
|
31
|
+
| Token output | hand-authored | **generated** via `baldart tokens build` (own thin emitter registry — no Style Dictionary) |
|
|
32
|
+
| Generation | hand-written specs that don't scale | **hybrid**: deterministic Node extractor + agentic enrichment |
|
|
33
|
+
|
|
34
|
+
BALDART wraps the **standard** (DTCG) and owns only a thin generator — the same
|
|
35
|
+
"wrap best-in-class, own the adapter" ethos as the i18n and graph layers.
|
|
36
|
+
|
|
37
|
+
## 3. The moving parts
|
|
38
|
+
|
|
39
|
+
| Piece | Path | Role |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| HEAD schema | `framework/agents/component-manifest-schema.md` | SSOT for the frontmatter fields + read/regeneration/leniency contract |
|
|
42
|
+
| Gate + cascade | `framework/agents/design-system-protocol.md` | BLOCKING reads, drift codes, token SSOT inversion, Authority Matrix |
|
|
43
|
+
| Deterministic extractor | `framework/.claude/skills/design-system-init/scripts/extract-manifest.mjs` | zero-dep Node; name/source/source_sha/props/variants/composes (Codex-portable) |
|
|
44
|
+
| Spec template | `…/design-system-init/scripts/component-spec.template.md` | HEAD + prose scaffold |
|
|
45
|
+
| Bootstrap/upgrade | `framework/.claude/skills/design-system-init/SKILL.md` | greenfield + upgrade modes |
|
|
46
|
+
| Token emitters | `src/utils/token-emitters/{index,ts,css}.js` | DTCG → stack-native output (REGISTRY pattern) |
|
|
47
|
+
| Token generator | `src/utils/tokens-generator.js` | parse `.tokens.json`, resolve `{aliases}`, build/isStale |
|
|
48
|
+
| CLI verb | `src/commands/tokens.js` (`baldart tokens build`) | the operation skills/doctor CALL |
|
|
49
|
+
| Config | `paths.design_tokens` + `design_tokens.outputs` | propagated template→configure→update→doctor |
|
|
50
|
+
|
|
51
|
+
## 4. Install / adoption lifecycle
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
has_design_system: true (no separate flag — the manifest rides on it)
|
|
55
|
+
│
|
|
56
|
+
├─ configure: autodetect .tokens.json → paths.design_tokens; propose ts/css outputs; baldart tokens build
|
|
57
|
+
│
|
|
58
|
+
├─ /design-system-init (greenfield OR upgrade):
|
|
59
|
+
│ deterministic extract (no subagent) → agentic enrich (doc-reviewer)
|
|
60
|
+
│ → .tokens.json bootstrap + round-trip verify → thin INDEX router
|
|
61
|
+
│
|
|
62
|
+
├─ per task: ui-expert/ui-design write the HEAD in the Post-Intervention Check
|
|
63
|
+
│ coder/architect/context-primer READ frontmatter-first
|
|
64
|
+
│
|
|
65
|
+
├─ per merge: code-reviewer Rule 8 gates (HEAD-first; transition-lenient)
|
|
66
|
+
│
|
|
67
|
+
├─ weekly: ds-drift (source_sha + token-output-vs-DTCG)
|
|
68
|
+
│
|
|
69
|
+
└─ doctor backfill: tokens-build (stale output) + ds-manifest-nudge (specs missing/prose-only)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 5. Invariants (do not break)
|
|
73
|
+
|
|
74
|
+
- **Codex portability**: the READ path and the deterministic half (extractor,
|
|
75
|
+
`baldart tokens build`) run with **no subagent**. Only agentic enrichment needs
|
|
76
|
+
Claude; under Codex it degrades to empty/TODO fields — never a hard failure.
|
|
77
|
+
- **No twin**: ONE artifact per component (the spec, evolved) and ONE token SSOT
|
|
78
|
+
(`.tokens.json`); `tokens-reference.md` and `tokens.ts`/CSS are **generated
|
|
79
|
+
views/outputs**, never hand-authored sources. The INDEX is a generated router.
|
|
80
|
+
- **Token SSOT inversion**: when `paths.design_tokens` is set, edit `.tokens.json`
|
|
81
|
+
and regenerate; a hand-edit to a generated output (output ≠ `baldart tokens
|
|
82
|
+
build`) is `DS_TOKENS_DRIFT`. Generated files carry a `baldart-generated` banner.
|
|
83
|
+
- **Transition leniency (no new floor)**: a prose-only spec lacking a HEAD is
|
|
84
|
+
valid (not `DS_COMPONENT_STALE`); empty deterministic fields (non-TS stack) are
|
|
85
|
+
advisory; a missing spec for a new primitive is the same `DS_INDEX_DRIFT` as
|
|
86
|
+
before. The manifest is an upgrade target, not a stricter gate.
|
|
87
|
+
- **Ownership**: `doc-reviewer` owns the agentic HEAD fields (never clobbered on
|
|
88
|
+
regeneration); the deterministic fields are regenerated from source; never
|
|
89
|
+
hand-curate them.
|
|
90
|
+
- **Authority Matrix** lives in `design-system-protocol.md`, not the (generated)
|
|
91
|
+
INDEX.
|
|
92
|
+
|
|
93
|
+
## 6. Adding capability later (deferred, documented)
|
|
94
|
+
|
|
95
|
+
- Extra emitters (`scss`, `tailwind`, `json`) — drop a file in `token-emitters/`.
|
|
96
|
+
- A git pre-commit hook to regenerate tokens on `.tokens.json` change (opt-in).
|
|
97
|
+
- In-place replacement of a hand-authored `tokens.ts` (after the round-trip is
|
|
98
|
+
trusted on real consumers).
|
|
99
|
+
- `framework-edit-gate` hard-deny on editing a generated token output (after one
|
|
100
|
+
release of the advisory banner).
|
|
@@ -14,24 +14,31 @@ prompt: |
|
|
|
14
14
|
Run the weekly design-system drift check (only if `${paths.design_system}/`
|
|
15
15
|
exists in the project — exit cleanly if absent):
|
|
16
16
|
|
|
17
|
-
1. **INDEX coverage**:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
2. **Per-component accuracy**: for each
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
1. **INDEX coverage**: the `${paths.design_system}/INDEX.md` router must list
|
|
18
|
+
every primitive that has a spec. Flag `DS_INDEX_DRIFT` for any mismatch
|
|
19
|
+
(fix by regenerating the router, never hand-editing it).
|
|
20
|
+
2. **Per-component HEAD accuracy**: for each
|
|
21
|
+
`${paths.design_system}/components/<Name>.md`, compare its frontmatter HEAD
|
|
22
|
+
against the source — `source_sha` vs the current git blob, `props`/`variants`
|
|
23
|
+
vs the TS types (schema: `framework/agents/component-manifest-schema.md`).
|
|
24
|
+
Flag `DS_COMPONENT_STALE` when `source_sha` is stale (or >7 days unverified
|
|
25
|
+
with a source change). A prose-only spec with no HEAD is an upgrade candidate
|
|
26
|
+
(recommend `/design-system-init`), NOT itself stale.
|
|
24
27
|
3. **Animations reconciliation**: `${paths.design_system}/patterns/animations.md`
|
|
25
28
|
must match keyframes in the project's global styles. Flag
|
|
26
29
|
`DS_ANIMATIONS_DRIFT`.
|
|
27
|
-
4. **Tokens reconciliation**: when
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
4. **Tokens reconciliation**: when `paths.design_tokens` is set (DTCG SSOT),
|
|
31
|
+
verify the generated outputs (`design_tokens.outputs`) + `tokens-reference.md`
|
|
32
|
+
equal `baldart tokens build` — a hand-edited generated file or a dangling
|
|
33
|
+
`token_bindings` ID is `DS_TOKENS_DRIFT`. When unset, fall back to verifying
|
|
34
|
+
the project token source vs `tokens-reference.md`.
|
|
30
35
|
5. Emit a consolidated report under
|
|
31
36
|
`docs/reports/{{YYYYMMDD}}-ds-drift.md`.
|
|
32
37
|
|
|
33
|
-
Apply trivial fixes inline (
|
|
34
|
-
|
|
38
|
+
Apply trivial fixes inline (regenerate the INDEX router; `baldart tokens build`
|
|
39
|
+
for token-output drift; refresh a stale deterministic HEAD via the extractor).
|
|
40
|
+
Anything that requires design judgement (agentic HEAD fields, new variant
|
|
41
|
+
semantics) is left as a finding.
|
|
35
42
|
|
|
36
43
|
output:
|
|
37
44
|
path: docs/reports/{{YYYYMMDD}}-ds-drift.md
|
|
@@ -35,6 +35,13 @@ paths:
|
|
|
35
35
|
components_root: "" # e.g. src/components
|
|
36
36
|
global_styles: "" # e.g. src/app/globals.css
|
|
37
37
|
|
|
38
|
+
# Design-token SSOT (since v4.65.0). The W3C DTCG `.tokens.json` file that is
|
|
39
|
+
# the SINGLE source of truth for design tokens; the stack-native artifact
|
|
40
|
+
# (tokens.ts / CSS vars) is GENERATED from it via `baldart tokens build` (see
|
|
41
|
+
# the design_tokens: block below). Only meaningful when has_design_system: true.
|
|
42
|
+
# Leave empty if the project has not adopted the DTCG SSOT (no inversion).
|
|
43
|
+
design_tokens: "" # e.g. src/ui/tokens.tokens.json
|
|
44
|
+
|
|
38
45
|
# Workflow infrastructure (set the matching `features.*` flag).
|
|
39
46
|
backlog_dir: "" # e.g. backlog
|
|
40
47
|
adrs_dir: "" # e.g. docs/decisions
|
|
@@ -312,6 +319,21 @@ graph:
|
|
|
312
319
|
# without it, so this is an enhancement, not a requirement.
|
|
313
320
|
register_mcp: false
|
|
314
321
|
|
|
322
|
+
# ─── DESIGN TOKENS (DTCG) ──────────────────────────────────────────────────
|
|
323
|
+
# Generation targets for the DTCG token SSOT (since v4.65.0). The source is
|
|
324
|
+
# `paths.design_tokens` (a W3C DTCG `.tokens.json`). `baldart tokens build`
|
|
325
|
+
# reads it, resolves `{aliases}`, and regenerates each output below. The
|
|
326
|
+
# generated files carry a `baldart-generated` banner — edit `.tokens.json`, never
|
|
327
|
+
# the output. Only meaningful when has_design_system: true AND
|
|
328
|
+
# paths.design_tokens is set; otherwise `tokens build` is a no-op.
|
|
329
|
+
design_tokens:
|
|
330
|
+
# Each output is { format, path }. format ∈ token-emitters registry
|
|
331
|
+
# (src/utils/token-emitters/: ts | css — extend by dropping a new emitter).
|
|
332
|
+
outputs: []
|
|
333
|
+
# Example:
|
|
334
|
+
# - { format: ts, path: src/ui/tokens.ts }
|
|
335
|
+
# - { format: css, path: src/ui/tokens.css }
|
|
336
|
+
|
|
315
337
|
# ─── TOOLCHAIN ─────────────────────────────────────────────────────────────
|
|
316
338
|
# State of the curated dev toolchain. Populated by `baldart configure` / the
|
|
317
339
|
# `/toolchain-bootstrap` skill. Only meaningful when `features.has_toolchain:
|
package/package.json
CHANGED
|
@@ -308,6 +308,23 @@ function detect(cwd = process.cwd()) {
|
|
|
308
308
|
)
|
|
309
309
|
);
|
|
310
310
|
|
|
311
|
+
// ---- Design-token SSOT (DTCG `.tokens.json`, since v4.65.0) -------------
|
|
312
|
+
// Detection-only: a `.tokens.json` / `.tokens` file at a conventional spot
|
|
313
|
+
// means the project already speaks DTCG. If absent we leave design_tokens
|
|
314
|
+
// empty (no inversion) — adoption is an explicit /design-system-init step,
|
|
315
|
+
// never auto-forced (the project's existing tokens.ts stays the source).
|
|
316
|
+
const designTokensPath = findFirst(
|
|
317
|
+
...expandCandidates(
|
|
318
|
+
'tokens.tokens.json', 'design/tokens.tokens.json',
|
|
319
|
+
'src/ui/tokens.tokens.json', 'src/design-system/tokens.tokens.json',
|
|
320
|
+
'tokens.json', 'design-tokens.json'
|
|
321
|
+
),
|
|
322
|
+
...(designSystemPath ? [
|
|
323
|
+
path.join(designSystemPath, 'tokens.tokens.json'),
|
|
324
|
+
path.join(designSystemPath, 'tokens.json'),
|
|
325
|
+
] : [])
|
|
326
|
+
) || '';
|
|
327
|
+
|
|
311
328
|
// ---- API docs (widened — OpenAPI, GraphQL, populated dir) --------------
|
|
312
329
|
const apiIndex = findFirst('docs/references/api/index.md', 'docs/api/index.md');
|
|
313
330
|
const apiSchemas = findFirst('docs/references/api/schemas.md', 'docs/api/schemas.md');
|
|
@@ -425,6 +442,7 @@ function detect(cwd = process.cwd()) {
|
|
|
425
442
|
components_primitives: componentsPrimitives,
|
|
426
443
|
components_root: componentsRoot,
|
|
427
444
|
global_styles: globalStyles,
|
|
445
|
+
design_tokens: designTokensPath,
|
|
428
446
|
backlog_dir: exists('backlog') ? 'backlog' : '',
|
|
429
447
|
adrs_dir: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0 ? 'docs/decisions' : '',
|
|
430
448
|
prd_dir: exists('docs/prd') ? 'docs/prd' : '',
|
|
@@ -732,6 +750,7 @@ async function interactivePrompts(merged, detected) {
|
|
|
732
750
|
['components_primitives', 'UI primitives directory (e.g. src/components/ui)', () => true],
|
|
733
751
|
['components_root', 'Components root directory', () => true],
|
|
734
752
|
['global_styles', 'Global stylesheet path', () => true],
|
|
753
|
+
['design_tokens', 'DTCG token SSOT (.tokens.json) — leave empty if not adopted', () => merged.features.has_design_system],
|
|
735
754
|
['backlog_dir', 'Backlog directory', () => merged.features.has_backlog],
|
|
736
755
|
['adrs_dir', 'ADR directory', () => merged.features.has_adrs],
|
|
737
756
|
['prd_dir', 'PRD directory', () => merged.features.has_prd_workflow],
|
|
@@ -750,6 +769,41 @@ async function interactivePrompts(merged, detected) {
|
|
|
750
769
|
merged.paths[key] = await promptForKey(`${label}`, proposed);
|
|
751
770
|
}
|
|
752
771
|
|
|
772
|
+
// ---- Design tokens (DTCG SSOT, since v4.65.0) -------------------------
|
|
773
|
+
// Only when the project adopted the DTCG source (paths.design_tokens set).
|
|
774
|
+
// The block holds the GENERATION targets; `baldart tokens build` (+ doctor
|
|
775
|
+
// backfill) regenerate them. We never invert an existing hand-authored
|
|
776
|
+
// tokens.ts here — that migration is an explicit /design-system-init step.
|
|
777
|
+
merged.design_tokens = merged.design_tokens || { outputs: [] };
|
|
778
|
+
if (merged.features.has_design_system === true && merged.paths.design_tokens) {
|
|
779
|
+
UI.section('Design tokens (DTCG SSOT → generated artifacts)');
|
|
780
|
+
const TokensGenerator = require('../utils/tokens-generator');
|
|
781
|
+
const gen = new TokensGenerator(process.cwd());
|
|
782
|
+
if (!gen.detect(merged.paths.design_tokens)) {
|
|
783
|
+
UI.info(`DTCG source \`${merged.paths.design_tokens}\` not found yet — run /design-system-init to scaffold it. Leaving design_tokens.outputs empty.`);
|
|
784
|
+
} else if (!(merged.design_tokens.outputs || []).length) {
|
|
785
|
+
// Propose the conventional outputs next to the existing token consumers.
|
|
786
|
+
const proposed = [];
|
|
787
|
+
const tsGuess = merged.paths.global_styles ? '' : 'src/ui/tokens.ts';
|
|
788
|
+
if (await UI.confirm('Generate a TypeScript token object (tokens.ts) from the DTCG source?', true)) {
|
|
789
|
+
const p = await promptForKey(' tokens.ts output path', tsGuess || 'src/ui/tokens.ts');
|
|
790
|
+
if (p) proposed.push({ format: 'ts', path: p });
|
|
791
|
+
}
|
|
792
|
+
if (await UI.confirm('Also generate a CSS custom-property sheet?', false)) {
|
|
793
|
+
const p = await promptForKey(' tokens.css output path', 'src/ui/tokens.css');
|
|
794
|
+
if (p) proposed.push({ format: 'css', path: p });
|
|
795
|
+
}
|
|
796
|
+
merged.design_tokens.outputs = proposed;
|
|
797
|
+
if (proposed.length) {
|
|
798
|
+
const res = gen.build({ source: merged.paths.design_tokens, outputs: proposed });
|
|
799
|
+
if (res.ok) UI.success(`Tokens built (${res.tokenCount}) → ${res.written.join(', ')}`);
|
|
800
|
+
else UI.warning(`Token build failed: ${res.error} (fix the .tokens.json then run \`baldart tokens build\`).`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} else {
|
|
804
|
+
merged.design_tokens.outputs = merged.design_tokens.outputs || [];
|
|
805
|
+
}
|
|
806
|
+
|
|
753
807
|
// ---- E2E review tuning (since v3.18.0) --------------------------------
|
|
754
808
|
// Only runs when the user opted in above. Populates the e2e_review.*
|
|
755
809
|
// sub-section with the severity floor, self-heal cap, pixel pre-filter
|