baldart 4.68.2 → 4.69.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 +17 -0
- package/README.md +2 -2
- package/VERSION +1 -1
- package/framework/.claude/skills/design-system-init/SKILL.md +4 -3
- package/framework/.claude/skills/design-system-init/scripts/extract-manifest.mjs +41 -28
- package/framework/.claude/skills/design-system-init/scripts/extract-one.mjs +39 -0
- package/framework/.claude/skills/design-system-init/scripts/serialize-spec.mjs +46 -5
- package/framework/.claude/skills/ds-new/SKILL.md +199 -0
- package/framework/.claude/skills/prd/references/ui-design-phase.md +10 -0
- package/framework/agents/component-manifest-schema.md +4 -1
- package/framework/agents/design-system-protocol.md +6 -2
- package/framework/agents/index.md +1 -0
- package/framework/docs/COMPONENT-MANIFEST-LAYER.md +2 -2
- package/framework/{.claude/skills/design-system-init/scripts → templates}/component-spec.template.md +33 -13
- package/package.json +1 -1
- package/src/utils/symlinks.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to BALDART will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.69.0] - 2026-06-24
|
|
9
|
+
|
|
10
|
+
**`/ds-new` — the fifth corner of the design-system discipline: guided creation of ONE canonical element + a canonical, ENFORCED doc template.** Discovery/reuse, prevention (`/prd`), enforcement (`ds-gate`), and bulk bootstrap (`/design-system-init`) all existed — but there was no user-invocable entry point to create a single new component or token *correctly* on-the-fly (research → create → document → register → govern → verify); `ui-expert` does it inside tasks, `/design-system-init` is bulk-only. Second gap the user named: the docs were **not standardized** — the rich prose template existed but was buried in `scripts/` and (critically) **not wired into the serializer** (`emitSpec` stubbed a one-liner on empty prose), so freshly-created specs were untemplated.
|
|
11
|
+
|
|
12
|
+
**MINOR** — adds a skill + a thin script wrapper; no new config key (rides on `features.has_design_system`). A pure orchestrator: every rule stays in its existing SSOT.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`/ds-new` skill** (`framework/.claude/skills/ds-new/SKILL.md`) — single-element guided creation, the on-the-fly twin of `/design-system-init`, also callable by `/prd` at its reconciliation gate's "NEW (governed)" branch. THIN orchestrator: reuse-first (Component Discovery Cascade + Selection-Policy-by-role + `signatureDuplicates`) → optional scaffold (delegated to `ui-expert`, opt-in; default documents an existing source) → extract HEAD (`extract-one.mjs`) → enrich agentic fields (`doc-reviewer`) → govern closures → document+register (serializer) → verify (`ds-gate`) → coherence report. Token path: DTCG node → `baldart tokens build` → bind. Gated on `features.has_design_system` (refuse + suggest `/design-system-init` when false). Codex-portable (deterministic spine zero-subagent; agentic steps degrade to TODO). No rule re-described — cross-pointers to `design-system-protocol.md` + `component-manifest-schema.md`.
|
|
17
|
+
- **`extract-one.mjs`** — single-component wrapper around `extract-manifest.mjs`'s now-exported `extractFile` (the bulk extractor was `--roots <dir>` only). One extraction implementation, zero-dep, Codex-portable; verified to produce byte-identical output to the bulk path.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Canonical doc template promoted + ENFORCED.** Moved `component-spec.template.md` from `design-system-init/scripts/` to the visible **`framework/templates/component-spec.template.md`** (single SSOT, framework-internal, excluded from the consumer-copy loop) and enhanced it to explicitly encode the structural-retrieval contract (HEAD-first discovery, token IDs not values, tables>prose, cross-ref-over-repetition, one-owner-per-fact — cross-pointing `doc-writing-for-rag` + `component-manifest-schema.md` § Read contract). **Wired into the serializer**: `serialize-spec.mjs`'s `emitSpec()` now FILLS the template on empty prose (placeholders from `props`/`variants`/`token_bindings`, unfillable ones keep `<!-- TODO -->`), via a new `--prose-template` flag (default resolves to the promoted path). Non-empty prose is still preserved verbatim. One change → three beneficiaries: `/ds-new`, `/design-system-init` greenfield, `ui-expert` Post-Intervention regen — every newly-created spec now follows the standardized shape.
|
|
22
|
+
- **`extract-manifest.mjs`** — per-file extraction factored into exported `extractFile`; CLI guarded with `import.meta` so importing has no side effects.
|
|
23
|
+
- **`/prd` reconciliation** (`ui-design-phase.md`) gains an OPT-IN "create now" arm at the NEW-governed branch (invoke `/ds-new`); default stays bind-and-defer. `/ds-new` registered as the single-element create path in `design-system-protocol.md` (§ When a primitive is missing + Consumers), `component-manifest-schema.md` (Producers & consumers), and `agents/index.md` routing.
|
|
24
|
+
|
|
8
25
|
## [4.68.2] - 2026-06-24
|
|
9
26
|
|
|
10
27
|
**A change's OWN documentation now closes before merge — no more "post-merge, non-blocking" doc follow-ups for what the change itself touched.** Observed on a real `mayo` FEAT-0055 run that merged with two deferred doc items: a full-sync of the `/products` screen doc (the exact route it changed, beyond a stub) and a `must_rule` clarification on `PageLayout.md` (the component it modified). Both are *own-docs* — documenting what you just shipped — yet `/new` permitted deferring them (the DS Decision Matrix routed "needs judgement" to a follow-up, and Phase 6b force-marks cards DONE regardless). Leaving the doc of the thing you just changed stale at merge is the drift entry door the Post-Intervention philosophy exists to close.
|
package/README.md
CHANGED
|
@@ -211,14 +211,14 @@ Skills always-ask when required keys are missing — never silently default.
|
|
|
211
211
|
never overwrites your file. Full guide:
|
|
212
212
|
[`framework/docs/PROJECT-CONFIGURATION.md`](framework/docs/PROJECT-CONFIGURATION.md).
|
|
213
213
|
|
|
214
|
-
### Skills (
|
|
214
|
+
### Skills (36 portable skills)
|
|
215
215
|
|
|
216
216
|
Skills live under `.claude/skills/` and are auto-discovered by Claude Code.
|
|
217
217
|
Bundled skills:
|
|
218
218
|
|
|
219
219
|
- **Workflow**: `new`, `new2` (v4.16.0 — EXPERIMENTAL workflow-hosted `/new`, Claude-only, for A/B testing context economy), `prd`, `prd-add`, `bug`, `simplify`, `worktree-manager`, `issue-review`, `context-primer`
|
|
220
220
|
- **Code quality**: `skill-creator`, `find-skills`, `webapp-testing`, `playwright-skill`, `lsp-bootstrap` (v3.10.0), `graphify-bootstrap` (v4.21.0 — code knowledge graph), `graph-align` (v4.21.0 — doc↔graph alignment), `toolchain-bootstrap` (v4.41.0 — curated dev toolchain), `e2e-review` (v3.18.0)
|
|
221
|
-
- **Design**: `frontend-design`, `ui-design`, `motion-design`, `gamification-design`, `design-system-init` (v3.11.0)
|
|
221
|
+
- **Design**: `frontend-design`, `ui-design`, `motion-design`, `gamification-design`, `design-system-init` (v3.11.0 — bulk registry bootstrap/upgrade), `ds-new` (v4.69.0 — single-element guided creation: one component or token, reuse-first → optional scaffold → document + register + govern + verify; the on-the-fly twin of `design-system-init`)
|
|
222
222
|
- **Product**: `seo-audit`, `copywriting`, `api-design-principles`
|
|
223
223
|
- **Knowledge**: `doc-writing-for-rag`, `capture` (LLM wiki overlay)
|
|
224
224
|
- **Internationalization**: `i18n` (v4.52.0 — audit the context registry + context-aware translation into native locale files), `i18n-adopt` (v4.52.0 — one-shot migration that externalizes all hardcoded strings on an existing codebase); both gated on `features.has_i18n`
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.69.0
|
|
@@ -371,8 +371,9 @@ hardcode literals that duplicate these tokens.
|
|
|
371
371
|
- `framework/agents/project-context.md` — config + overlay contract.
|
|
372
372
|
- `framework/.claude/skills/ui-design/SKILL.md` — primary consumer (reads
|
|
373
373
|
the registry BLOCKING).
|
|
374
|
-
- `
|
|
375
|
-
- `scripts/extract-manifest.mjs` — deterministic, zero-dep HEAD extractor (Codex-portable).
|
|
376
|
-
- `scripts/
|
|
374
|
+
- `framework/templates/component-spec.template.md` — the CANONICAL prose-body template (SSOT for the spec shape; the serializer fills it on creation, HEAD is serializer-owned). The `--prose-template` default resolves here; no flag needed.
|
|
375
|
+
- `scripts/extract-manifest.mjs` — deterministic, zero-dep HEAD extractor (`extractFile` shared with `extract-one.mjs`; Codex-portable).
|
|
376
|
+
- `scripts/extract-one.mjs` — single-component wrapper around `extractFile` (used by `/ds-new`).
|
|
377
|
+
- `scripts/serialize-spec.mjs` — deterministic HEAD serializer (valid-YAML, fail-closed) + INDEX router + Selection Policy + canonical prose-body fill.
|
|
377
378
|
- `framework/agents/component-manifest-schema.md` — the frontmatter HEAD schema.
|
|
378
379
|
- `framework/docs/COMPONENT-MANIFEST-LAYER.md` — layer design + lifecycle.
|
|
@@ -25,9 +25,6 @@ function arg(name, def = '') {
|
|
|
25
25
|
return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : def;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const roots = arg('--roots').split(',').map((s) => s.trim()).filter(Boolean);
|
|
29
|
-
const asJson = process.argv.includes('--json');
|
|
30
|
-
|
|
31
28
|
/** Recursively collect .tsx/.ts files (skip tests, stories, node_modules, index barrels). */
|
|
32
29
|
function walk(dir, out = []) {
|
|
33
30
|
let entries;
|
|
@@ -120,32 +117,48 @@ function extractComposes(src) {
|
|
|
120
117
|
return [...out];
|
|
121
118
|
}
|
|
122
119
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Extract ONE component's deterministic manifest fields from a source file.
|
|
122
|
+
* Returns null when the file is unreadable or exports no PascalCase component.
|
|
123
|
+
* This is the SINGLE extraction implementation — shared by the bulk extractor
|
|
124
|
+
* (CLI below) and `extract-one.mjs` (the single-component wrapper /ds-new uses),
|
|
125
|
+
* so the two never fork.
|
|
126
|
+
*/
|
|
127
|
+
export function extractFile(file) {
|
|
128
|
+
let src;
|
|
129
|
+
try { src = fs.readFileSync(file, 'utf8'); } catch (_) { return null; }
|
|
130
|
+
const name = exportedName(src, file);
|
|
131
|
+
if (!name) return null;
|
|
132
|
+
const props = extractProps(src, name);
|
|
133
|
+
return {
|
|
134
|
+
name,
|
|
135
|
+
source: path.relative(process.cwd(), file),
|
|
136
|
+
source_sha: gitSha(file),
|
|
137
|
+
status: 'stable',
|
|
138
|
+
category: 'primitive',
|
|
139
|
+
props,
|
|
140
|
+
variants: extractVariants(props),
|
|
141
|
+
composes: extractComposes(src).filter((c) => c !== name),
|
|
142
|
+
};
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
// CLI — guarded so importing `extractFile` (from extract-one.mjs) has NO side effects.
|
|
146
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
147
|
+
const roots = arg('--roots').split(',').map((s) => s.trim()).filter(Boolean);
|
|
148
|
+
const asJson = process.argv.includes('--json');
|
|
149
|
+
const components = [];
|
|
150
|
+
for (const root of roots) {
|
|
151
|
+
for (const file of walk(root)) {
|
|
152
|
+
const c = extractFile(file);
|
|
153
|
+
if (c) components.push(c);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const result = { schema: 'baldart.component-manifest-extract/1', count: components.length, components };
|
|
157
|
+
if (asJson) process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
158
|
+
else {
|
|
159
|
+
for (const c of components) {
|
|
160
|
+
process.stdout.write(`${c.name} (${c.source}) props:${Object.keys(c.props).length} variants:${c.variants.length} composes:${c.composes.length}\n`);
|
|
161
|
+
}
|
|
162
|
+
process.stdout.write(`\n${components.length} components.\n`);
|
|
149
163
|
}
|
|
150
|
-
process.stdout.write(`\n${components.length} components.\n`);
|
|
151
164
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Single-component deterministic manifest extractor (since v4.69.0).
|
|
3
|
+
//
|
|
4
|
+
// The thin single-element wrapper around `extract-manifest.mjs`'s `extractFile`
|
|
5
|
+
// (the bulk extractor is `--roots <dir>` only). Used by `/ds-new` to extract one
|
|
6
|
+
// component's deterministic HEAD fields (name, source, source_sha, props,
|
|
7
|
+
// variants, composes) WITHOUT a subagent — the Codex-portable spine. ZERO
|
|
8
|
+
// dependencies; imports the ONE shared extraction implementation so it can never
|
|
9
|
+
// drift from the bulk path.
|
|
10
|
+
//
|
|
11
|
+
// Usage:
|
|
12
|
+
// node extract-one.mjs --source <path.tsx> [--json]
|
|
13
|
+
|
|
14
|
+
import { extractFile } from './extract-manifest.mjs';
|
|
15
|
+
|
|
16
|
+
function arg(name, def = '') {
|
|
17
|
+
const i = process.argv.indexOf(name);
|
|
18
|
+
return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : def;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const source = arg('--source');
|
|
22
|
+
const asJson = process.argv.includes('--json');
|
|
23
|
+
|
|
24
|
+
if (!source) {
|
|
25
|
+
console.error('usage: extract-one.mjs --source <path.tsx> [--json]');
|
|
26
|
+
process.exit(2);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const component = extractFile(source);
|
|
30
|
+
if (!component) {
|
|
31
|
+
console.error(`extract-one: no exported PascalCase component found in ${source} (or file unreadable)`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (asJson) {
|
|
36
|
+
process.stdout.write(JSON.stringify({ schema: 'baldart.component-manifest-extract-one/1', component }, null, 2) + '\n');
|
|
37
|
+
} else {
|
|
38
|
+
process.stdout.write(`${component.name} (${component.source}) props:${Object.keys(component.props).length} variants:${component.variants.length} composes:${component.composes.length}\n`);
|
|
39
|
+
}
|
|
@@ -91,9 +91,44 @@ export function emitHead(c) {
|
|
|
91
91
|
// Date is fine in a CLI script (this is not a resumable workflow).
|
|
92
92
|
const TODAY = new Date().toISOString().slice(0, 10);
|
|
93
93
|
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Fill the canonical prose-body template (framework/templates/component-spec.template.md)
|
|
96
|
+
* for a freshly-created component. Takes the template TEXT, strips its leading
|
|
97
|
+
* comment + documented `---…---` HEAD block (the serializer owns the real HEAD),
|
|
98
|
+
* and substitutes the deterministically-fillable `{{…}}` placeholders. Unfillable
|
|
99
|
+
* ones keep the template's `<!-- TODO -->` so a human/doc-reviewer finishes them.
|
|
100
|
+
* Zero-dep, pure string ops. This is the wiring that makes EVERY newly-created
|
|
101
|
+
* spec follow the standardized shape (the gap: emitSpec used to stub a one-liner).
|
|
102
|
+
*/
|
|
103
|
+
export function fillProseTemplate(c, templateText) {
|
|
104
|
+
// Body = everything after the second `---` fence (drop the leading comment + HEAD doc block).
|
|
105
|
+
const body = templateText.replace(/^[\s\S]*?\n---\n[\s\S]*?\n---\n/, '');
|
|
106
|
+
const propsTable = (c.props && Object.keys(c.props).length)
|
|
107
|
+
? Object.entries(c.props).map(([k, p]) => `| ${k} | ${String((p && p.type) || '').replace(/\|/g, '\\|')} | ${(p && p.default !== undefined && p.default !== '') ? p.default : '—'} | ${(p && p.required) ? 'required' : '—'} |`).join('\n')
|
|
108
|
+
: '';
|
|
109
|
+
const variants = (c.variants && c.variants.length) ? c.variants.map((v) => `- \`${v}\``).join('\n') : '';
|
|
110
|
+
const tokens = (c.token_bindings && c.token_bindings.length) ? c.token_bindings.map((t) => `- \`${t}\``).join('\n') : '';
|
|
111
|
+
return body
|
|
112
|
+
.replace(/\{\{ComponentName\}\}/g, c.name || '')
|
|
113
|
+
.replace(/\{\{exported_name\}\}/g, c.name || '')
|
|
114
|
+
.replace(/\{\{file_path\}\}/g, c.source || '')
|
|
115
|
+
.replace(/\{\{today\}\}/g, c.last_verified || TODAY)
|
|
116
|
+
.replace(/\{\{props_table\}\}/g, propsTable)
|
|
117
|
+
.replace(/\{\{variants_section\}\}/g, variants)
|
|
118
|
+
.replace(/\{\{tokens_section\}\}/g, tokens);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Full spec file = HEAD fences + prose body. Prose resolution:
|
|
123
|
+
* 1. existing `c.prose` → preserved verbatim (never clobbered on regeneration);
|
|
124
|
+
* 2. else, when `proseTemplate` text is provided → the filled canonical template;
|
|
125
|
+
* 3. else → a minimal one-line stub (fallback when no template is resolvable).
|
|
126
|
+
*/
|
|
127
|
+
export function emitSpec(c, proseTemplate) {
|
|
128
|
+
let prose;
|
|
129
|
+
if (c.prose && c.prose.trim()) prose = c.prose.replace(/^\s+/, '');
|
|
130
|
+
else if (proseTemplate) prose = fillProseTemplate(c, proseTemplate).replace(/^\s+/, '');
|
|
131
|
+
else prose = `# ${c.name}\n\n> Per-component spec. The HEAD above is the machine-readable contract.\n`;
|
|
97
132
|
return `---\n${emitHead(c)}\n---\n\n${prose.replace(/\s*$/, '')}\n`;
|
|
98
133
|
}
|
|
99
134
|
|
|
@@ -286,8 +321,14 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
286
321
|
const tokensPath = arg('--tokens'); // optional DTCG .tokens.json → binding vocab guard
|
|
287
322
|
const verifyUsage = process.argv.includes('--verify-usage'); // advisory source-usage check
|
|
288
323
|
const cwd = arg('--cwd', process.cwd());
|
|
324
|
+
// Canonical prose-body template (framework/templates/component-spec.template.md).
|
|
325
|
+
// Default resolves relative to this script; callers may override with --prose-template.
|
|
326
|
+
const proseTemplatePath = arg('--prose-template', new URL('../../../../templates/component-spec.template.md', import.meta.url).pathname);
|
|
327
|
+
let proseTemplate = '';
|
|
328
|
+
try { proseTemplate = fs.readFileSync(proseTemplatePath, 'utf8'); }
|
|
329
|
+
catch (_) { /* template unresolvable → emitSpec falls back to the one-line stub */ }
|
|
289
330
|
if (!bundlePath || !outDir) {
|
|
290
|
-
console.error('usage: serialize-spec.mjs --bundle bundle.json --out-dir <dir> [--index <path>] [--brand <name>] [--tokens <.tokens.json>] [--verify-usage] [--cwd <dir>]');
|
|
331
|
+
console.error('usage: serialize-spec.mjs --bundle bundle.json --out-dir <dir> [--index <path>] [--brand <name>] [--tokens <.tokens.json>] [--verify-usage] [--prose-template <path>] [--cwd <dir>]');
|
|
291
332
|
process.exit(2);
|
|
292
333
|
}
|
|
293
334
|
const bundle = JSON.parse(fs.readFileSync(bundlePath, 'utf8'));
|
|
@@ -325,7 +366,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
325
366
|
fs.mkdirSync(outDir, { recursive: true });
|
|
326
367
|
let invalid = 0;
|
|
327
368
|
for (const c of components) {
|
|
328
|
-
const spec = emitSpec(c);
|
|
369
|
+
const spec = emitSpec(c, proseTemplate);
|
|
329
370
|
fs.writeFileSync(path.join(outDir, `${c.name}.md`), spec, 'utf8');
|
|
330
371
|
const head = spec.match(/^---\n([\s\S]*?)\n---/)[1];
|
|
331
372
|
const v = tryValidate(head);
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ds-new
|
|
3
|
+
effort: medium
|
|
4
|
+
description: >
|
|
5
|
+
Create ONE canonical design-system element — a component OR a token — end to
|
|
6
|
+
end with the full discipline: reuse-first research, (optional) scaffold, then
|
|
7
|
+
document + register + govern + verify. The single-element, on-the-fly twin of
|
|
8
|
+
the bulk `/design-system-init`: usable standalone by you, and callable by `/prd`
|
|
9
|
+
when its reconciliation gate confirms a NEW component. A THIN orchestrator — it
|
|
10
|
+
delegates every rule to the existing SSOTs (Component Discovery Cascade, the
|
|
11
|
+
manifest HEAD schema, the closed-set Selection Policy, the token cascade) and
|
|
12
|
+
produces STANDARDIZED docs by filling the canonical template
|
|
13
|
+
(framework/templates/component-spec.template.md) via the deterministic
|
|
14
|
+
serializer — it never hand-writes a spec HEAD. Use when the user says /ds-new,
|
|
15
|
+
"nuovo componente design system", "aggiungi un token", "crea un componente
|
|
16
|
+
canonico", "rendi canonico questo componente", "registra una primitive",
|
|
17
|
+
"documenta un componente nel design system". Gated on features.has_design_system.
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# DS New — create one canonical design-system element
|
|
21
|
+
|
|
22
|
+
The fifth corner of the design-system discipline: discovery/reuse, prevention
|
|
23
|
+
(`/prd`), enforcement (`baldart ds-gate`), and bulk bootstrap
|
|
24
|
+
(`/design-system-init`) already exist — this is the **guided creation of ONE
|
|
25
|
+
element** done correctly. It is a conductor: each step DELEGATES to an existing
|
|
26
|
+
module and cross-pointers to it; **no cascade/schema/closed-set/token rule is
|
|
27
|
+
re-described here** (re-describing it is how it drifts from the SSOT).
|
|
28
|
+
|
|
29
|
+
## Project Context
|
|
30
|
+
|
|
31
|
+
**Reads from `baldart.config.yml`:** `paths.design_system`,
|
|
32
|
+
`paths.components_primitives`, `paths.components_root`, `paths.design_tokens`,
|
|
33
|
+
`design_tokens.outputs`, `paths.ui_guidelines`, `paths.global_styles`,
|
|
34
|
+
`identity.brand_name`, `git.trunk_branch`.
|
|
35
|
+
|
|
36
|
+
**Writes:** files under `${paths.design_system}` (the spec, the regenerated INDEX
|
|
37
|
+
+ Selection Policy), the component HEAD fields, and — for tokens — the DTCG
|
|
38
|
+
`${paths.design_tokens}` + the regenerated outputs. Like `/design-system-init`,
|
|
39
|
+
it is **source-read-only by default** (it does not author component source);
|
|
40
|
+
scaffolding a `.tsx` is opt-in and delegated to `ui-expert`.
|
|
41
|
+
|
|
42
|
+
**Schema:** the frontmatter HEAD is defined in
|
|
43
|
+
`framework/agents/component-manifest-schema.md` and is **emitted by the
|
|
44
|
+
serializer, never hand-written**. The standardized prose body is the canonical
|
|
45
|
+
template `framework/templates/component-spec.template.md` (the serializer fills
|
|
46
|
+
it). The creation/governance rules are the SSOT in
|
|
47
|
+
`framework/agents/design-system-protocol.md` (Component Discovery Cascade, §
|
|
48
|
+
Closed-Set Selection Policy, § Token Cascade, Post-Intervention Coherence Check).
|
|
49
|
+
|
|
50
|
+
**Gated by features:** `features.has_design_system` (BLOCKING). When `false`,
|
|
51
|
+
REFUSE and recommend `/design-system-init` (you need a registry to add to). See
|
|
52
|
+
`design-system-protocol.md` § Gating.
|
|
53
|
+
|
|
54
|
+
**Overlay:** loads `.baldart/overlays/ds-new.md` if present (e.g. mandated stack
|
|
55
|
+
for scaffolds, brand-voice for the prose, a project token-naming convention).
|
|
56
|
+
|
|
57
|
+
**On missing keys:** ask the user; do not assume defaults. See
|
|
58
|
+
`framework/agents/project-context.md` § 3.
|
|
59
|
+
|
|
60
|
+
## Effort
|
|
61
|
+
|
|
62
|
+
**Baseline:** `effort: medium` (frontmatter). **Inline override:** pass
|
|
63
|
+
`effort=<low|medium|high|xhigh|max>` anywhere in the invocation to scale
|
|
64
|
+
reasoning depth for this run — detect it once at kickoff and strip the token
|
|
65
|
+
before consuming user input. Mapping + parsing contract:
|
|
66
|
+
`framework/agents/effort-protocol.md`.
|
|
67
|
+
|
|
68
|
+
## Invocation
|
|
69
|
+
|
|
70
|
+
`/ds-new <Name>` (component) · `/ds-new token <id>` (token) · `/ds-new` (ask).
|
|
71
|
+
Optional: `scaffold` (also generate the source), a mockup/figma ref, a one-line
|
|
72
|
+
intent. When called by `/prd` (the reconciliation gate's "NEW (governed)"
|
|
73
|
+
branch), the role/closure are pre-resolved — skip the reuse step (P1/step 1).
|
|
74
|
+
|
|
75
|
+
## Pre-flight
|
|
76
|
+
|
|
77
|
+
- **P0 — gate.** `features.has_design_system: false` → REFUSE + suggest
|
|
78
|
+
`/design-system-init`. (DELEGATE `design-system-protocol.md` § Gating.)
|
|
79
|
+
- **P1 — kind.** component | token (from the arg, else ask one question).
|
|
80
|
+
- **P2 — source mode (component only).** Does the source already exist, or should
|
|
81
|
+
the skill scaffold it? Ask once; **default = document an existing file.**
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Component path
|
|
86
|
+
|
|
87
|
+
Each step is a delegation. Read the cited SSOT; do not paraphrase it.
|
|
88
|
+
|
|
89
|
+
1. **Reuse-first (BLOCKING).** Walk the Component Discovery Cascade AND query the
|
|
90
|
+
Selection Policy **by role** (`INDEX.md` § Selection Policy), per
|
|
91
|
+
`design-system-protocol.md`. Surface duplicates with the gate's own detector —
|
|
92
|
+
`node -e` over `src/utils/ds-reuse-gate.js → signatureDuplicates` (or `baldart
|
|
93
|
+
ds-gate` once a spec exists). Ask the user **only** on a real fork (a
|
|
94
|
+
suspected-duplicate, or a closed-family hit) — one consolidated
|
|
95
|
+
`AskUserQuestion`. On a clean match → **STOP and reuse the existing component**
|
|
96
|
+
(creating it anyway is the exact failure this skill prevents).
|
|
97
|
+
2. **Create (conditional).** *Scaffold mode* → DELEGATE `ui-expert` (Task tool):
|
|
98
|
+
it runs its own BLOCKING Functional Traceability + Token Contract pre-work and
|
|
99
|
+
writes a tokens-compliant source under `${paths.components_primitives}/<Name>`.
|
|
100
|
+
*Document mode* → take the existing path. Under Codex (no subagents), scaffold
|
|
101
|
+
degrades to a tokens-compliant-skeleton TODO and requires you to supply the
|
|
102
|
+
path. The skill itself NEVER writes component source.
|
|
103
|
+
3. **Extract the deterministic HEAD.**
|
|
104
|
+
`node .framework/framework/.claude/skills/design-system-init/scripts/extract-one.mjs --source <path.tsx> --json`
|
|
105
|
+
→ `name`/`source`/`source_sha`/`props`/`variants`/`composes` (zero-dep,
|
|
106
|
+
Codex-portable, no subagent).
|
|
107
|
+
4. **Enrich the agentic fields AS DATA.** DELEGATE `doc-reviewer` (Task tool) to
|
|
108
|
+
produce `purpose` / `token_bindings` (picked from the REAL ids in
|
|
109
|
+
`${paths.design_tokens}`) / `a11y` / `related` / `must_rules` as JSON values —
|
|
110
|
+
NEVER typed into YAML. Codex → leave empty + TODO. (Schema:
|
|
111
|
+
`component-manifest-schema.md`.)
|
|
112
|
+
5. **Govern (conditional).** Set `canonical_for` / `use_when` / `selection_closed`
|
|
113
|
+
ONLY when a closure is confirmed by the user (or pre-resolved by `/prd`);
|
|
114
|
+
propose-only when unattended. DELEGATE `design-system-protocol.md` § Closed-Set
|
|
115
|
+
Selection Policy + § Governance. An agent never flips closure on its own.
|
|
116
|
+
6. **Document + register (the standardized doc).** Build a 1-component bundle
|
|
117
|
+
(`{ brand, components: [<the merged deterministic+agentic+policy fields>] }`)
|
|
118
|
+
**merged with the HEADs of ALL existing components** (read each
|
|
119
|
+
`${paths.design_system}/components/*.md` HEAD) so the regenerated `--index`
|
|
120
|
+
router + Selection Policy block stay complete (the same caveat as
|
|
121
|
+
`/design-system-init` step 6 — an INDEX from a partial bundle drops rows). Run:
|
|
122
|
+
```
|
|
123
|
+
node .framework/framework/.claude/skills/design-system-init/scripts/serialize-spec.mjs \
|
|
124
|
+
--bundle <bundle.json> --out-dir ${paths.design_system}/components \
|
|
125
|
+
--index ${paths.design_system}/INDEX.md --brand "${identity.brand_name}" \
|
|
126
|
+
--tokens ${paths.design_tokens} --verify-usage
|
|
127
|
+
```
|
|
128
|
+
It emits the valid-YAML HEAD + the **standardized prose body** (it fills
|
|
129
|
+
`framework/templates/component-spec.template.md` — the `--prose-template`
|
|
130
|
+
default; you never hand-write the spec) + the regenerated thin INDEX, then
|
|
131
|
+
self-validates and **fails closed**. Review `droppedBindings` /
|
|
132
|
+
`unverifiedBindings` per step 4.
|
|
133
|
+
7. **Verify.** Serializer exit 0. If a closure was written: confirm
|
|
134
|
+
`grep -q '## Selection Policy' ${paths.design_system}/INDEX.md` AND
|
|
135
|
+
`npx baldart ds-gate --json` does NOT report `"reason":"no-policy"`. The gate
|
|
136
|
+
reads the **committed** baseline → the INDEX (with the policy block) must be
|
|
137
|
+
committed for enforcement; surface that so the user commits it. (This is
|
|
138
|
+
`/design-system-init` step 6b reused.)
|
|
139
|
+
8. **Coherence report (never silent).** Emit the protocol's "Registry coherence
|
|
140
|
+
check: N findings" block (per § Post-Intervention Coherence Check): new spec
|
|
141
|
+
shipped, INDEX entry added, tokens reconciled, closed-set respected.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Token path
|
|
146
|
+
|
|
147
|
+
1. **Reuse-first.** Check the token already exists (in `${paths.design_tokens}` /
|
|
148
|
+
`tokens-reference.md`); ask **primitive vs semantic vs component** layer.
|
|
149
|
+
DELEGATE `design-system-protocol.md` § Token Cascade (components consume
|
|
150
|
+
semantic, never primitive; prefer OKLCH for palette generation).
|
|
151
|
+
2. **Edit the DTCG SSOT.** Add the node to `${paths.design_tokens}` (`.tokens.json`,
|
|
152
|
+
`$type` + `$value`, hierarchical id). NEVER hand-edit the generated output.
|
|
153
|
+
3. **Regenerate.** `npx baldart tokens build` (reads `.tokens.json`, regenerates
|
|
154
|
+
`design_tokens.outputs[]` + `tokens-reference.md`). DELEGATE — never reimplement.
|
|
155
|
+
4. **Round-trip verify.** A non-empty semantic diff between the generated output
|
|
156
|
+
and the prior is surfaced, **never auto-accepted** — the user ratifies.
|
|
157
|
+
5. **Bind (optional).** When a component should consume the new token, add its
|
|
158
|
+
**id** (not value) to that component's `token_bindings` and re-serialize (the
|
|
159
|
+
component path's step 6). The `--tokens` guard confirms the id resolves.
|
|
160
|
+
6. **Coherence report.** Per § Post-Intervention Coherence Check (check 3): a
|
|
161
|
+
dangling `token_bindings` id is `DS_TOKENS_DRIFT`.
|
|
162
|
+
|
|
163
|
+
**Token-doc standard (NOT a per-token file).** A token's one-owner home is its
|
|
164
|
+
DTCG node + its row in the generated `tokens-reference.md` + its id in component
|
|
165
|
+
`token_bindings`. Do NOT create a per-token `.md` (it would be a 4th source of
|
|
166
|
+
truth — violates `doc-writing-for-rag` one-owner-per-fact). Token *rationale*, if
|
|
167
|
+
any, goes in an existing `${paths.design_system}/patterns/*.md`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Inline fallbacks (portability + fail-safe)
|
|
172
|
+
|
|
173
|
+
- **No `Workflow` tool needed** — this skill is prose-orchestrated.
|
|
174
|
+
- **Codex (no subagents):** the deterministic spine (`extract-one` → `serialize-spec`
|
|
175
|
+
→ `tokens build` → `ds-gate`) is zero-subagent and runs unchanged; the agentic
|
|
176
|
+
enrichment (step 4) and `ui-expert` scaffold degrade to TODO + a clear note.
|
|
177
|
+
- **Older CLI:** `baldart ds-gate` / `baldart tokens build` absent →
|
|
178
|
+
SKIP-with-reason, never block (every BALDART gate's fail-safe contract). Surface
|
|
179
|
+
the SKIP; do not claim a gate ran when it did not.
|
|
180
|
+
|
|
181
|
+
## /prd integration
|
|
182
|
+
|
|
183
|
+
`/prd`'s reconciliation gate (`prd/references/ui-design-phase.md`, the "NEW
|
|
184
|
+
(governed) — confirmed with user" branch) OWNS the decision *whether* a new
|
|
185
|
+
element is needed; `/ds-new` EXECUTES the creation. When `/prd` invokes it with a
|
|
186
|
+
resolved `region → NewComponent — role: family@context — standard:y/n — closed:y/n`,
|
|
187
|
+
skip P1/P2/step 1 (already reconciled) and run from step 2. No rule is duplicated
|
|
188
|
+
across the two.
|
|
189
|
+
|
|
190
|
+
## See Also
|
|
191
|
+
|
|
192
|
+
- `framework/agents/design-system-protocol.md` — the cascade, closed-set, token
|
|
193
|
+
cascade, governance, and Post-Intervention rules this skill delegates to.
|
|
194
|
+
- `framework/agents/component-manifest-schema.md` — the HEAD schema.
|
|
195
|
+
- `framework/templates/component-spec.template.md` — the canonical prose template
|
|
196
|
+
the serializer fills (the standardized doc shape).
|
|
197
|
+
- `framework/.claude/skills/design-system-init/SKILL.md` — the bulk twin; shares
|
|
198
|
+
the same scripts (`extract-manifest`/`extract-one`/`serialize-spec`).
|
|
199
|
+
- `framework/docs/COMPONENT-MANIFEST-LAYER.md` — the layer overview.
|
|
@@ -186,6 +186,16 @@ Before invoking `ui-design`:
|
|
|
186
186
|
it from then on. A new standard for *all* views also opens a **migration
|
|
187
187
|
follow-up card** for the existing views (never migrate them in this PRD).
|
|
188
188
|
|
|
189
|
+
7. **Create now (OPT-IN) vs bind-and-defer (default).** For each `NEW (governed)`
|
|
190
|
+
element, the default is **bind-and-defer**: persist the binding (step 6) and let
|
|
191
|
+
`ui-expert` create the component at `/new` implement time. When the user wants it
|
|
192
|
+
materialized **now** (the canonical element + its standardized spec + INDEX entry
|
|
193
|
+
+ governance, before any card runs), invoke **`/ds-new`** with the resolved tuple
|
|
194
|
+
(`NewComponent — role: family@context — standard:y/n — closed:y/n`); it skips its
|
|
195
|
+
own reuse step (already reconciled here) and runs from its create step. `/prd`
|
|
196
|
+
owns the *decision*; `/ds-new` executes the *creation* — no rule is duplicated.
|
|
197
|
+
This is additive: omit it and the default bind-and-defer path is unchanged.
|
|
198
|
+
|
|
189
199
|
Pass the resolved inventory to `ui-design`:
|
|
190
200
|
|
|
191
201
|
```
|
|
@@ -192,7 +192,10 @@ This keeps consumers that have not yet upgraded (and non-TS stacks) from becomin
|
|
|
192
192
|
## Producers & consumers
|
|
193
193
|
|
|
194
194
|
- **Bootstrap / upgrade**: `/design-system-init` (deterministic extract + agentic
|
|
195
|
-
enrich; greenfield and upgrade modes).
|
|
195
|
+
enrich; greenfield and upgrade modes; bulk).
|
|
196
|
+
- **Single-element create**: `/ds-new` (one component or token, on-the-fly or
|
|
197
|
+
called by `/prd`; same scripts — `extract-one`/`serialize-spec` — as the bulk
|
|
198
|
+
skill, so the HEAD it writes is identical).
|
|
196
199
|
- **Per-task write**: `ui-expert` / `ui-design` / `frontend-design` in the
|
|
197
200
|
Post-Intervention Coherence Check — a new/modified primitive regenerates its
|
|
198
201
|
HEAD in the same change.
|
|
@@ -360,7 +360,10 @@ agent has two choices:
|
|
|
360
360
|
- **Add to the registry** — preferred when the primitive is broadly useful.
|
|
361
361
|
Create the per-component spec, the implementation under
|
|
362
362
|
`${paths.components_primitives}/<Name>`, and an INDEX.md entry in the same
|
|
363
|
-
change.
|
|
363
|
+
change. The guided single-element path is the **`/ds-new`** skill (reuse-first →
|
|
364
|
+
optional scaffold → document + register + govern + verify); it produces the
|
|
365
|
+
standardized spec by filling the canonical template via the serializer. The bulk
|
|
366
|
+
analogue (whole registry at once) is `/design-system-init`.
|
|
364
367
|
- **Local one-off** — only when the use case is genuinely surface-specific
|
|
365
368
|
(e.g. a marketing-page hero illustration). Even then, prefer composing
|
|
366
369
|
existing primitives over hand-rolling a new one. Justify the local choice
|
|
@@ -653,7 +656,8 @@ The following files reference this module instead of duplicating its rules:
|
|
|
653
656
|
intrinsic-quality critic consumed by `/e2e-review` Phase 4b)
|
|
654
657
|
- `framework/.claude/skills/ui-design/SKILL.md`
|
|
655
658
|
- `framework/.claude/skills/frontend-design/SKILL.md`
|
|
656
|
-
- `framework/.claude/skills/design-system-init/SKILL.md`
|
|
659
|
+
- `framework/.claude/skills/design-system-init/SKILL.md` (bulk registry bootstrap/upgrade)
|
|
660
|
+
- `framework/.claude/skills/ds-new/SKILL.md` (single-element guided creation)
|
|
657
661
|
- `framework/.claude/commands/design-review.md`
|
|
658
662
|
- `framework/agents/design-review.md`
|
|
659
663
|
|
|
@@ -21,6 +21,7 @@ Route agents to the right module with minimal reading.
|
|
|
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
23
|
- If a UI change might INTRODUCE a component (designing from a mockup, building a new primitive) and `features.has_design_system: true` -> read `agents/design-system-protocol.md` § "Closed-Set Selection Policy": query the registry by ROLE (`INDEX.md` § Selection Policy), reuse the canonical member of any `closed` family, honor the card's `component_bindings`, and run `baldart ds-gate` (a `DS_CLOSED_SET_VIOLATION` — a new canonical in a closed family, the "third header" failure mode — BLOCKS). A new member of a closed family is a governance decision (`/prd` reconciliation + ADR), never an implementation act.
|
|
24
|
+
- If creating ONE new canonical design-system element on-the-fly (a component or a token) and `features.has_design_system: true` -> use the `/ds-new` skill (reuse-first → optional scaffold via `ui-expert` → document + register + govern + verify); it produces the standardized spec by filling `framework/templates/component-spec.template.md` via the serializer, never hand-writing a HEAD. The bulk analogue (whole registry) is `/design-system-init`.
|
|
24
25
|
- 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`.
|
|
25
26
|
- If touching design-review workflows or UI guidelines -> read `agents/design-review.md` and project-specific UI guidelines.
|
|
26
27
|
- If touching architecture, auth, or tech stack -> read `agents/architecture.md`.
|
|
@@ -40,8 +40,8 @@ BALDART wraps the **standard** (DTCG) and owns only a thin generator — the sam
|
|
|
40
40
|
|---|---|---|
|
|
41
41
|
| HEAD schema | `framework/agents/component-manifest-schema.md` | SSOT for the frontmatter fields + read/regeneration/leniency contract |
|
|
42
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 |
|
|
43
|
+
| Deterministic extractor | `framework/.claude/skills/design-system-init/scripts/extract-manifest.mjs` (`extractFile`) + `extract-one.mjs` (single-component wrapper) | zero-dep Node; name/source/source_sha/props/variants/composes (Codex-portable) |
|
|
44
|
+
| Spec template | `framework/templates/component-spec.template.md` | CANONICAL prose-body template (SSOT); serializer fills it on creation via `--prose-template` (default) |
|
|
45
45
|
| Bootstrap/upgrade | `framework/.claude/skills/design-system-init/SKILL.md` | greenfield + upgrade modes |
|
|
46
46
|
| Token emitters | `src/utils/token-emitters/{index,ts,css}.js` | DTCG → stack-native output (REGISTRY pattern) |
|
|
47
47
|
| Token generator | `src/utils/tokens-generator.js` | parse `.tokens.json`, resolve `{aliases}`, build/isStale |
|
package/framework/{.claude/skills/design-system-init/scripts → templates}/component-spec.template.md
RENAMED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<!-- CANONICAL per-component spec template (since v4.69.0). SSOT for the SHAPE of
|
|
2
|
+
every `${paths.design_system}/components/<Name>.md`. Consumed by
|
|
3
|
+
scripts/serialize-spec.mjs (it fills the PROSE BODY below the fences on
|
|
4
|
+
creation) and cited by /ds-new, /design-system-init, ui-expert, doc-reviewer.
|
|
5
|
+
This is framework-internal (machinery), not a hand-edit copy.
|
|
6
|
+
|
|
7
|
+
The frontmatter HEAD is EMITTED DETERMINISTICALLY by serialize-spec.mjs
|
|
8
|
+
(field data in, valid YAML out) — NEVER hand-write or hand-fill it (invalid
|
|
9
|
+
YAML, e.g. `props:{}` without a space, is exactly the bug the serializer
|
|
10
|
+
prevents). The block below documents the HEAD shape; the serializer owns it.
|
|
11
|
+
|
|
12
|
+
RETRIEVAL CONTRACT (why this shape — see `doc-writing-for-rag` SKILL.md +
|
|
13
|
+
`agents/component-manifest-schema.md` § "Read contract"): agents discover a
|
|
14
|
+
component by reading the thin INDEX router → then ONLY this HEAD frontmatter
|
|
15
|
+
(never this prose body, never the source) — the prose is read solely when
|
|
16
|
+
MODIFYING the component. So: keep the body scan-able (tables > prose), one
|
|
17
|
+
fact per owner (visual values live in tokens-reference.md, not here),
|
|
18
|
+
cross-ref instead of repeating, and store token IDs (not values). -->
|
|
6
19
|
---
|
|
7
20
|
# Machine-readable HEAD — schema: framework/agents/component-manifest-schema.md
|
|
8
21
|
# DETERMINISTIC fields (regenerated from source — do not hand-curate):
|
|
@@ -20,6 +33,10 @@ token_bindings: {{token_bindings_yaml}} # DTCG token IDs (not values)
|
|
|
20
33
|
a11y: "{{a11y}}"
|
|
21
34
|
related: {{related_yaml}}
|
|
22
35
|
must_rules: {{must_rules_yaml}}
|
|
36
|
+
# Selection policy (closed-set — see design-system-protocol.md § Closed-Set Selection Policy):
|
|
37
|
+
canonical_for: {{canonical_for_yaml}} # ["<family>@<context>"] roles this is THE canonical answer for ([] = none)
|
|
38
|
+
use_when: "{{use_when}}" # one-line predicate: pick this over its siblings when…
|
|
39
|
+
selection_closed: {{selection_closed}} # true ⇒ the role family is a CLOSED set (new member = governance, not code)
|
|
23
40
|
last_verified: {{today}}
|
|
24
41
|
owner: doc-reviewer
|
|
25
42
|
---
|
|
@@ -27,10 +44,12 @@ owner: doc-reviewer
|
|
|
27
44
|
# {{ComponentName}}
|
|
28
45
|
|
|
29
46
|
> Per-component spec. The HEAD above is the machine-readable contract (read it
|
|
30
|
-
> first for discovery); this prose body is the human long-form rationale
|
|
31
|
-
> Authoritative for props, variants,
|
|
32
|
-
> authority (color/shadow/radius/spacing)
|
|
33
|
-
> (`${paths.design_tokens}`), surfaced via
|
|
47
|
+
> first for discovery); this prose body is the human long-form rationale, read
|
|
48
|
+
> only when MODIFYING this component. Authoritative for props, variants,
|
|
49
|
+
> accessibility, and anti-patterns. Visual authority (color/shadow/radius/spacing)
|
|
50
|
+
> belongs to the DTCG token SSOT (`${paths.design_tokens}`), surfaced via
|
|
51
|
+
> [`../tokens-reference.md`](../tokens-reference.md) — never duplicate token
|
|
52
|
+
> values here.
|
|
34
53
|
|
|
35
54
|
## Source
|
|
36
55
|
|
|
@@ -67,9 +86,10 @@ when to use it. Example:
|
|
|
67
86
|
|
|
68
87
|
{{tokens_section}}
|
|
69
88
|
|
|
70
|
-
<!-- TODO:
|
|
71
|
-
|
|
72
|
-
the source
|
|
89
|
+
<!-- TODO: these are DTCG token IDs (not values) — one read of tokens-reference.md
|
|
90
|
+
resolves every value. List the color / spacing / shadow / radius / motion tokens
|
|
91
|
+
this component reads. A hardcoded literal in the source is a registry violation —
|
|
92
|
+
fix it at the source, add a token if one is missing. -->
|
|
73
93
|
|
|
74
94
|
## Accessibility
|
|
75
95
|
|
|
@@ -104,4 +124,4 @@ Cross-link to patterns/ docs when applicable. -->
|
|
|
104
124
|
<!-- Track breaking changes to the spec. Bump version when removing a variant
|
|
105
125
|
or changing a default. -->
|
|
106
126
|
|
|
107
|
-
- {{today}} — Spec scaffolded by `/design-system-init
|
|
127
|
+
- {{today}} — Spec scaffolded by BALDART (`/ds-new` / `/design-system-init`).
|
package/package.json
CHANGED
package/src/utils/symlinks.js
CHANGED
|
@@ -785,6 +785,7 @@ class SymlinkUtils {
|
|
|
785
785
|
const FRAMEWORK_INTERNAL = new Set([
|
|
786
786
|
'baldart.config.template.yml',
|
|
787
787
|
'skill-project-context.snippet.md',
|
|
788
|
+
'component-spec.template.md', // canonical prose-body template — consumed by serialize-spec.mjs, not hand-edited
|
|
788
789
|
'overlays', // example overlays directory — consumers reference, don't copy wholesale
|
|
789
790
|
]);
|
|
790
791
|
|