baldart 5.0.1 → 5.2.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 +157 -0
- package/README.md +6 -6
- package/VERSION +1 -1
- package/framework/.claude/agents/CHANGELOG.md +90 -0
- package/framework/.claude/agents/REGISTRY.md +5 -5
- package/framework/.claude/agents/coder.md +3 -3
- package/framework/.claude/agents/remotion-animator-orchestrator.md +4 -3
- package/framework/.claude/agents/senior-researcher.md +166 -151
- package/framework/.claude/agents/ui-expert.md +4 -10
- package/framework/.claude/agents/ui-quality-critic.md +27 -11
- package/framework/.claude/skills/design-system-init/CHANGELOG.md +5 -0
- package/framework/.claude/skills/design-system-init/SKILL.md +2 -2
- package/framework/.claude/skills/e2e-review/CHANGELOG.md +5 -0
- package/framework/.claude/skills/e2e-review/SKILL.md +2 -2
- package/framework/.claude/skills/frontend-design/CHANGELOG.md +20 -0
- package/framework/.claude/skills/frontend-design/SKILL.md +39 -216
- package/framework/.claude/skills/gamification-design/CHANGELOG.md +5 -0
- package/framework/.claude/skills/gamification-design/SKILL.md +2 -2
- package/framework/.claude/skills/prd/CHANGELOG.md +16 -0
- package/framework/.claude/skills/prd/SKILL.md +1 -1
- package/framework/.claude/skills/prd/references/discovery-phase.md +5 -1
- package/framework/.claude/skills/prd/references/research-phase.md +32 -12
- package/framework/.claude/skills/research/CHANGELOG.md +9 -0
- package/framework/.claude/skills/research/SKILL.md +94 -0
- package/framework/.claude/skills/research/assets/report-compare.template.md +50 -0
- package/framework/.claude/skills/research/assets/report-decision.template.md +42 -0
- package/framework/.claude/skills/research/assets/report-deep.template.md +61 -0
- package/framework/.claude/skills/research/assets/report-regulatory.template.md +44 -0
- package/framework/.claude/skills/research/references/playbook.md +112 -0
- package/framework/.claude/skills/research/scripts/rebuild-research-index.mjs +77 -0
- package/framework/.claude/skills/ui-design/CHANGELOG.md +60 -1
- package/framework/.claude/skills/ui-design/SKILL.md +127 -75
- package/framework/.claude/skills/ui-design/references/anti-slop.md +106 -0
- package/framework/.claude/skills/ui-design/references/craft-standards.md +259 -0
- package/framework/.claude/skills/ui-design/references/design-brief.md +92 -0
- package/framework/.claude/skills/ui-design/references/design-directions.md +188 -0
- package/framework/.claude/skills/ui-design/references/evaluation.md +125 -165
- package/framework/.claude/skills/ui-design/references/generation.md +125 -92
- package/framework/.claude/skills/ui-design/references/inventory.md +9 -2
- package/framework/.claude/skills/ui-design/scripts/craft-check.mjs +248 -0
- package/framework/agents/component-manifest-schema.md +1 -1
- package/framework/agents/design-system-protocol.md +6 -6
- package/framework/agents/index.md +3 -0
- package/framework/agents/research-protocol.md +228 -0
- package/framework/agents/skills-mapping.md +42 -23
- package/framework/docs/PROJECT-CONFIGURATION.md +28 -5
- package/framework/templates/baldart.config.template.yml +6 -0
- package/framework/templates/research-index.template.md +13 -0
- package/framework/templates/research-sources.CHANGELOG.md +14 -0
- package/framework/templates/research-sources.template.md +31 -0
- package/package.json +1 -1
- package/src/commands/configure.js +27 -1
- package/src/commands/doctor.js +82 -0
- package/src/commands/update.js +48 -1
- package/src/utils/__tests__/classify-divergence.test.js +42 -0
- package/src/utils/git.js +45 -9
- package/src/utils/research-library.js +92 -0
- package/src/utils/symlinks.js +3 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* craft-check.mjs — deterministic anti-slop / craft gate for ui-design mockups.
|
|
4
|
+
*
|
|
5
|
+
* Scans self-contained mockup HTML for the mechanically-checkable subset of
|
|
6
|
+
* references/anti-slop.md + references/craft-standards.md. Runs identically on
|
|
7
|
+
* Claude Code and Codex (zero deps, Node >= 18) — it is Gate 0 of Step D.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node craft-check.mjs <file.html> [more.html ...] [--json] [--strict-palette]
|
|
11
|
+
*
|
|
12
|
+
* --strict-palette escalate palette-reflex findings (AI-indigo, beige
|
|
13
|
+
* battery, trust gradient) from warn to block. Use in
|
|
14
|
+
* exploration regime; leave off in-context (the project's
|
|
15
|
+
* own brand may legitimately use these families).
|
|
16
|
+
*
|
|
17
|
+
* Exit codes: 0 = PASS (no blockers), 1 = FAIL (>=1 blocker), 2 = usage error.
|
|
18
|
+
*
|
|
19
|
+
* Rule provenance: distilled from impeccable (Apache-2.0, © Paul Bakaus),
|
|
20
|
+
* taste-skill (MIT, © Leonxlnx), open-design (Apache-2.0, nexu-io).
|
|
21
|
+
* Heuristics are conservative: binary rules block, judgment-adjacent rules warn.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const asJson = args.includes('--json');
|
|
28
|
+
const strictPalette = args.includes('--strict-palette');
|
|
29
|
+
const files = args.filter((a) => !a.startsWith('--'));
|
|
30
|
+
|
|
31
|
+
if (files.length === 0) {
|
|
32
|
+
console.error('usage: node craft-check.mjs <file.html> [...] [--json] [--strict-palette]');
|
|
33
|
+
process.exit(2);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const INDIGO_HEXES = ['#6366f1', '#4f46e5', '#4338ca', '#3730a3', '#8b5cf6', '#7c3aed', '#a855f7'];
|
|
37
|
+
const BEIGE_BG_HEXES = ['#f5f1ea', '#f7f5f1', '#fbf8f1', '#efeae0', '#ece6db', '#faf7f1', '#e8dfcb'];
|
|
38
|
+
const BEIGE_ACCENT_HEXES = ['#b08947', '#b6553a', '#9a2436', '#9c6e2a', '#bc7c3a', '#7d5621'];
|
|
39
|
+
const EMOJI_RE = /[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2B00}-\u{2BFF}\u{FE0F}]/u;
|
|
40
|
+
|
|
41
|
+
function lineOf(src, index) {
|
|
42
|
+
return src.slice(0, index).split('\n').length;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function excerptAt(src, index, len = 80) {
|
|
46
|
+
const start = Math.max(0, src.lastIndexOf('\n', index) + 1);
|
|
47
|
+
const end = src.indexOf('\n', index);
|
|
48
|
+
return src.slice(start, end === -1 ? start + len : Math.min(end, start + len)).trim();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Collect matches of a regex as {line, excerpt} evidence (capped). */
|
|
52
|
+
function findAll(src, re, cap = 5) {
|
|
53
|
+
const out = [];
|
|
54
|
+
let m;
|
|
55
|
+
const rex = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g');
|
|
56
|
+
while ((m = rex.exec(src)) !== null && out.length < cap) {
|
|
57
|
+
out.push({ line: lineOf(src, m.index), excerpt: excerptAt(src, m.index) });
|
|
58
|
+
if (m.index === rex.lastIndex) rex.lastIndex++;
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Strip script/style/comments, then tags → visible text (index-preserving via spaces). */
|
|
64
|
+
function visibleText(src) {
|
|
65
|
+
const blank = (s) => s.replace(/[^\n]/g, ' ');
|
|
66
|
+
return src
|
|
67
|
+
.replace(/<script[\s\S]*?<\/script>/gi, blank)
|
|
68
|
+
.replace(/<style[\s\S]*?<\/style>/gi, blank)
|
|
69
|
+
.replace(/<!--[\s\S]*?-->/g, blank)
|
|
70
|
+
.replace(/<[^>]+>/g, blank);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function checkFile(path) {
|
|
74
|
+
const src = readFileSync(path, 'utf8');
|
|
75
|
+
const text = visibleText(src);
|
|
76
|
+
const findings = [];
|
|
77
|
+
const add = (rule, severity, message, evidence) => {
|
|
78
|
+
if (evidence.length > 0) findings.push({ rule, severity, message, count: evidence.length, evidence });
|
|
79
|
+
};
|
|
80
|
+
const palSev = strictPalette ? 'block' : 'warn';
|
|
81
|
+
|
|
82
|
+
// ---- Absolute bans (binary) ------------------------------------------
|
|
83
|
+
add('em-dash', 'block',
|
|
84
|
+
'Em-dash (or en-dash separator) in visible copy — zero tolerance (anti-slop.md).',
|
|
85
|
+
findAll(text, /—|\s–\s/));
|
|
86
|
+
|
|
87
|
+
add('filler-copy', 'block',
|
|
88
|
+
'Filler copy (lorem ipsum / "Feature One") — an empty section is a composition problem.',
|
|
89
|
+
findAll(text, /lorem ipsum|\bfeature (one|two|three)\b/i));
|
|
90
|
+
|
|
91
|
+
add('gradient-text', 'block',
|
|
92
|
+
'Gradient text (background-clip: text) — decorative, never meaningful.',
|
|
93
|
+
findAll(src, /background-clip:\s*text|bg-clip-text/i));
|
|
94
|
+
|
|
95
|
+
add('scroll-cue', 'block',
|
|
96
|
+
'"Scroll to explore" cue — they know what scroll is.',
|
|
97
|
+
findAll(text, /scroll (down )?to (explore|discover|see)/i));
|
|
98
|
+
|
|
99
|
+
add('scroll-listener', 'block',
|
|
100
|
+
'window scroll listener in script — use IntersectionObserver / animation-library scroll values.',
|
|
101
|
+
findAll(src, /addEventListener\(\s*['"]scroll['"]/));
|
|
102
|
+
|
|
103
|
+
if (!/<meta[^>]+name=["']viewport["']/i.test(src)) {
|
|
104
|
+
findings.push({ rule: 'missing-viewport', severity: 'block', message: 'Missing <meta name="viewport"> — the mockup cannot be judged responsively.', count: 1, evidence: [{ line: 1, excerpt: '<head>' }] });
|
|
105
|
+
}
|
|
106
|
+
if (!/<html[^>]+lang=/i.test(src)) {
|
|
107
|
+
findings.push({ rule: 'missing-lang', severity: 'block', message: 'Missing lang attribute on <html> (identity.language).', count: 1, evidence: [{ line: 1, excerpt: '<html>' }] });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Emoji as icons inside headings/buttons
|
|
111
|
+
{
|
|
112
|
+
const ev = [];
|
|
113
|
+
const rex = /<(h[1-6]|button)\b[^>]*>([\s\S]*?)<\/\1>/gi;
|
|
114
|
+
let m;
|
|
115
|
+
while ((m = rex.exec(src)) !== null && ev.length < 5) {
|
|
116
|
+
const inner = m[2].replace(/<[^>]+>/g, '');
|
|
117
|
+
if (EMOJI_RE.test(inner)) ev.push({ line: lineOf(src, m.index), excerpt: excerptAt(src, m.index) });
|
|
118
|
+
}
|
|
119
|
+
add('emoji-icons', 'block',
|
|
120
|
+
'Emoji as feature icons in headings/buttons — use monoline SVG with currentColor.', ev);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---- Rationed patterns (count-bounded) --------------------------------
|
|
124
|
+
{
|
|
125
|
+
const sections = (src.match(/<section\b/gi) || []).length || 1;
|
|
126
|
+
const eyebrows = findAll(src, /class=["'][^"']*\buppercase\b[^"']*\btracking-/g, 50);
|
|
127
|
+
const cap = Math.ceil(sections / 3);
|
|
128
|
+
if (eyebrows.length > cap) {
|
|
129
|
+
findings.push({
|
|
130
|
+
rule: 'eyebrow-overuse', severity: 'block',
|
|
131
|
+
message: `Eyebrow kickers: ${eyebrows.length} for ${sections} section(s) — cap is ceil(sections/3) = ${cap}. Drop them; the headline alone is enough.`,
|
|
132
|
+
count: eyebrows.length, evidence: eyebrows.slice(0, 5),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
add('numbered-markers', 'warn',
|
|
138
|
+
'Numbered section markers (01 / 02 · Label) — the eyebrow trope one tier deeper.',
|
|
139
|
+
findAll(text, /\b0\d\s*[\/·]\s*\S/));
|
|
140
|
+
|
|
141
|
+
add('status-dots', 'warn',
|
|
142
|
+
'Pulsing decorative status dots — zero by default.',
|
|
143
|
+
findAll(src, /class=["'][^"']*animate-pulse[^"']*["']/));
|
|
144
|
+
|
|
145
|
+
add('marquee-overuse', 'warn',
|
|
146
|
+
'More than one marquee/auto-scroll strip per page.',
|
|
147
|
+
findAll(src, /marquee|animate-\[?scroll/gi).slice(1));
|
|
148
|
+
|
|
149
|
+
// ---- Palette reflexes (warn; block with --strict-palette) -------------
|
|
150
|
+
add('ai-indigo', palSev,
|
|
151
|
+
'Default-Tailwind indigo/violet accent — the textbook AI tell (LILA rule).',
|
|
152
|
+
findAll(src, new RegExp(INDIGO_HEXES.join('|'), 'i')));
|
|
153
|
+
|
|
154
|
+
add('beige-battery', palSev,
|
|
155
|
+
'Premium-consumer beige/brass battery — banned as a default; rotate palette families (design-directions.md).',
|
|
156
|
+
findAll(src, new RegExp([...BEIGE_BG_HEXES, ...BEIGE_ACCENT_HEXES].join('|'), 'i')));
|
|
157
|
+
|
|
158
|
+
add('trust-gradient', palSev,
|
|
159
|
+
'Two-stop purple→blue/cyan/pink "trust" gradient — a flat surface + intentional type beats it.',
|
|
160
|
+
findAll(src, /from-(purple|indigo|violet)-\d+[^"']*to-(blue|cyan|pink|fuchsia)-\d+/i));
|
|
161
|
+
|
|
162
|
+
// ---- Craft warnings ----------------------------------------------------
|
|
163
|
+
add('side-stripe', 'warn',
|
|
164
|
+
'Colored side-stripe accent (border-left/right > 1px) — the canonical AI dashboard tile.',
|
|
165
|
+
findAll(src, /border-l-[2-8]\b|border-r-[2-8]\b|border-left:\s*[2-9]px\s+solid\s+(?!transparent)/i));
|
|
166
|
+
|
|
167
|
+
add('h-screen', 'warn',
|
|
168
|
+
'h-screen used — prefer min-h-[100dvh] (iOS Safari).',
|
|
169
|
+
findAll(src, /\bh-screen\b/));
|
|
170
|
+
|
|
171
|
+
add('transition-all', 'warn',
|
|
172
|
+
'`transition: all` — animate transform/opacity explicitly.',
|
|
173
|
+
findAll(src, /transition:\s*all|\btransition-all\b/i));
|
|
174
|
+
|
|
175
|
+
add('br-in-heading', 'warn',
|
|
176
|
+
'<br> inside a heading — plan font scale with the copy instead of breaking lines manually.',
|
|
177
|
+
findAll(src, /<h[1-3][^>]*>(?:(?!<\/h[1-3]>)[\s\S])*?<br\s*\/?>/gi));
|
|
178
|
+
|
|
179
|
+
add('caps-no-tracking', 'warn',
|
|
180
|
+
'ALL-CAPS element without letter-spacing — uppercase requires +0.06–0.1em tracking.',
|
|
181
|
+
findAll(src, /class=["'](?![^"']*tracking-)[^"']*\buppercase\b[^"']*["']/g));
|
|
182
|
+
|
|
183
|
+
add('justify-text', 'warn',
|
|
184
|
+
'Justified text — never text-align: justify in UI.',
|
|
185
|
+
findAll(src, /text-align:\s*justify|\btext-justify\b/i));
|
|
186
|
+
|
|
187
|
+
add('fake-metrics', 'warn',
|
|
188
|
+
'Fake-perfect metric (99.9%, 10×) — real source, labelled mock, or absent (Jane Doe rule).',
|
|
189
|
+
findAll(text, /\b99\.9+\s*%|\b10×|\b100\s*%\s+(faster|better)/i));
|
|
190
|
+
|
|
191
|
+
// First <img> lazy / dimensionless images (CLS)
|
|
192
|
+
{
|
|
193
|
+
const imgs = [...src.matchAll(/<img\b[^>]*>/gi)];
|
|
194
|
+
if (imgs.length > 0 && /loading=["']lazy["']/i.test(imgs[0][0])) {
|
|
195
|
+
findings.push({ rule: 'lcp-lazy', severity: 'warn', message: 'First image is loading="lazy" — never lazy-load the LCP candidate.', count: 1, evidence: [{ line: lineOf(src, imgs[0].index), excerpt: excerptAt(src, imgs[0].index) }] });
|
|
196
|
+
}
|
|
197
|
+
const dimless = imgs.filter((m) => !/\bwidth=|\baspect-ratio|aspect-\[/i.test(m[0]));
|
|
198
|
+
add('img-no-dimensions', 'warn',
|
|
199
|
+
'<img> without width/height or aspect-ratio — reserves no space (CLS).',
|
|
200
|
+
dimless.slice(0, 5).map((m) => ({ line: lineOf(src, m.index), excerpt: excerptAt(src, m.index) })));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Token discipline: distinct raw hexes + off-scale arbitrary values
|
|
204
|
+
{
|
|
205
|
+
const hexes = new Set((src.match(/#[0-9a-fA-F]{6}\b/g) || []).map((h) => h.toLowerCase()));
|
|
206
|
+
if (hexes.size > 12) {
|
|
207
|
+
findings.push({ rule: 'hex-sprawl', severity: 'warn', message: `${hexes.size} distinct raw hex colors — an incomplete palette; consolidate to CSS custom properties.`, count: hexes.size, evidence: [] });
|
|
208
|
+
}
|
|
209
|
+
const offScale = [...src.matchAll(/\b(?:[mp][trblxy]?|gap|space-[xy])-\[(\d+)px\]/g)]
|
|
210
|
+
.filter((m) => Number(m[1]) % 4 !== 0);
|
|
211
|
+
add('off-scale-spacing', 'warn',
|
|
212
|
+
'Arbitrary spacing off the base-4 scale (e.g. p-[13px]) — token violation.',
|
|
213
|
+
offScale.slice(0, 5).map((m) => ({ line: lineOf(src, m.index), excerpt: m[0] })));
|
|
214
|
+
const sizes = new Set([
|
|
215
|
+
...[...src.matchAll(/text-\[(\d+)px\]/g)].map((m) => m[1]),
|
|
216
|
+
...[...src.matchAll(/font-size:\s*(\d+)px/gi)].map((m) => m[1]),
|
|
217
|
+
]);
|
|
218
|
+
if (sizes.size > 9) {
|
|
219
|
+
findings.push({ rule: 'type-scale-sprawl', severity: 'warn', message: `${sizes.size} distinct px font sizes — one scale, 6–9 deliberate steps.`, count: sizes.size, evidence: [] });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const blockers = findings.filter((f) => f.severity === 'block');
|
|
224
|
+
return { file: path, verdict: blockers.length === 0 ? 'PASS' : 'FAIL', blockers: blockers.length, warnings: findings.length - blockers.length, findings };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const results = files.map((f) => {
|
|
228
|
+
try {
|
|
229
|
+
return checkFile(f);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return { file: f, verdict: 'ERROR', error: e.message, findings: [] };
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (asJson) {
|
|
236
|
+
console.log(JSON.stringify({ strictPalette, results }, null, 2));
|
|
237
|
+
} else {
|
|
238
|
+
for (const r of results) {
|
|
239
|
+
console.log(`\n${r.verdict === 'PASS' ? '✓' : '✗'} ${r.file} — ${r.verdict}` + (r.error ? ` (${r.error})` : ''));
|
|
240
|
+
for (const f of r.findings || []) {
|
|
241
|
+
console.log(` [${f.severity.toUpperCase()}] ${f.rule} ×${f.count}: ${f.message}`);
|
|
242
|
+
for (const ev of f.evidence.slice(0, 3)) console.log(` L${ev.line}: ${ev.excerpt}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
console.log('');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
process.exit(results.some((r) => r.verdict === 'FAIL' || r.verdict === 'ERROR') ? 1 : 0);
|
|
@@ -220,7 +220,7 @@ This keeps consumers that have not yet upgraded (and non-TS stacks) from becomin
|
|
|
220
220
|
- **Single-element edit**: `/ds-edit` (update one existing component/token —
|
|
221
221
|
resync / extend / breaking / re-govern; regenerates the HEAD from the new source
|
|
222
222
|
via the same scripts, **preserving non-empty agentic fields + prose**).
|
|
223
|
-
- **Per-task write**: `ui-expert` / `ui-design` / `
|
|
223
|
+
- **Per-task write**: `ui-expert` / `ui-design` / `ui-implement` in the
|
|
224
224
|
Post-Intervention Coherence Check — a new/modified primitive regenerates its
|
|
225
225
|
HEAD in the same change.
|
|
226
226
|
- **Curation owner**: `doc-reviewer` (agentic fields).
|
|
@@ -12,7 +12,7 @@ twofold:
|
|
|
12
12
|
implementation, so existing primitives are reused instead of duplicated.
|
|
13
13
|
|
|
14
14
|
This is the textual SSOT for the registry-first protocol. Skills (`ui-design`,
|
|
15
|
-
`
|
|
15
|
+
`ui-implement`, `design-system-init`), agents (`ui-expert`, `code-reviewer`),
|
|
16
16
|
and the `/design-review` command all reference this module instead of
|
|
17
17
|
duplicating the BLOCKING-read cascade.
|
|
18
18
|
|
|
@@ -468,7 +468,7 @@ its own — it **proposes, a human confirms**:
|
|
|
468
468
|
## Functional Traceability Gate (BLOCKING — unconditional)
|
|
469
469
|
|
|
470
470
|
A mockup is a fidelity target, **not** a requirements oracle. Design tools
|
|
471
|
-
(Claude Design, Figma, the `ui-design
|
|
471
|
+
(Claude Design, Figma, the `ui-design` generator) routinely
|
|
472
472
|
add interactive-looking chrome — buttons, icons, menu entries, toggles, badges —
|
|
473
473
|
that no requirement asked for. Implementing a mockup **1:1 for fidelity**
|
|
474
474
|
materialises that chrome into real markup + dead handlers + unused icon imports,
|
|
@@ -524,7 +524,7 @@ For every **orphan**, never decide silently (consistent with the framework's
|
|
|
524
524
|
|
|
525
525
|
### Where it runs
|
|
526
526
|
|
|
527
|
-
1. **Design / generation time** (`ui-design`, `
|
|
527
|
+
1. **Design / generation time** (`ui-design`, `ui-expert`
|
|
528
528
|
designing) — when producing a mockup, do not invent interactive chrome with
|
|
529
529
|
no backing requirement; mark genuinely decorative elements as such in the
|
|
530
530
|
inventory so the downstream gate is deterministic, not a guess.
|
|
@@ -698,7 +698,7 @@ complete** when `features.has_design_system: true` until the registry has
|
|
|
698
698
|
been verified against the change that was just made.
|
|
699
699
|
|
|
700
700
|
The check runs at end-of-task by the agent that performed the work (`ui-expert`,
|
|
701
|
-
`ui-design`, `
|
|
701
|
+
`ui-design`, `code-reviewer`). It is **not** delegated to
|
|
702
702
|
the weekly `ds-drift` routine — that routine is a safety net, not the primary
|
|
703
703
|
mechanism. Drift introduced today must be reconciled today, not next Monday.
|
|
704
704
|
|
|
@@ -820,9 +820,9 @@ The following files reference this module instead of duplicating its rules:
|
|
|
820
820
|
- `framework/.claude/agents/ui-expert.md`
|
|
821
821
|
- `framework/.claude/agents/code-reviewer.md`
|
|
822
822
|
- `framework/.claude/agents/ui-quality-critic.md` (Design Quality Rubric — the
|
|
823
|
-
intrinsic-quality critic consumed by `/e2e-review` Phase 4b
|
|
823
|
+
intrinsic-quality critic consumed by `/e2e-review` Phase 4b and `/ui-design`
|
|
824
|
+
Step D)
|
|
824
825
|
- `framework/.claude/skills/ui-design/SKILL.md`
|
|
825
|
-
- `framework/.claude/skills/frontend-design/SKILL.md`
|
|
826
826
|
- `framework/.claude/skills/design-system-init/SKILL.md` (bulk registry bootstrap/upgrade)
|
|
827
827
|
- `framework/.claude/skills/ds-new/SKILL.md` (single-element guided creation)
|
|
828
828
|
- `framework/.claude/skills/ds-edit/SKILL.md` (single-element guided edit — resync / extend / breaking / re-govern)
|
|
@@ -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 the task is a DESIGN DECISION (new page/screen/component to design, redesign/restyle, design options to explore, a local prototype/mockup) -> route to the `/ui-design` skill (the local design studio: Design Read & direction lock, deterministic craft gate, dual-lens evaluation). An ALREADY-APPROVED mockup to implement routes to `/ui-implement`; a direct in-code UI change with no design decision delegates to the `ui-expert` agent (`AGENTS.md` § delegation). NEVER route design work to `frontend-design` (a router since v5.2.0) or `ui-ux-pro-max` (superseded by `ui-design/references/`).
|
|
23
24
|
- 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
25
|
- 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`.
|
|
25
26
|
- If EDITING one existing design-system element (add a variant/prop, breaking change, re-govern, or resync the spec) and `features.has_design_system: true` -> use the `/ds-edit` skill (classify the change → apply source via `ui-expert` → regenerate the spec via the serializer, PRESERVING agentic fields + prose, same canonical template as `/ds-new` → verify). The pure mechanical doc-resync after a code change is already automatic (serializer + `DS_COMPONENT_STALE` gate); `/ds-edit` is for the deliberate change.
|
|
@@ -43,6 +44,7 @@ Route agents to the right module with minimal reading.
|
|
|
43
44
|
- If authoring/auditing a subagent whose return would flood the orchestrator context (free-form analysis/audit/research) -> read `agents/return-contract-protocol.md` and bound its final message: COMPACT headline + `path:line` findings + disk pointer, persist the long form. The input-side twin of the effort protocol.
|
|
44
45
|
- If authoring/editing an agent definition -> the shared operating procedures (injection guard, retrieval, memory, tool budget) live in `agents/agent-operating-protocol.md` and the reviewer verification passes (challenge/simulation/CoVe/risk) in `agents/review-protocol.md` — cite their sections, never re-inline them; keep only the 1-line binding versions in the agent body.
|
|
45
46
|
- If a skill performs runtime-mechanical operations (spawn an agent, gate on the user, track state, accelerate a fan-out, run an adversarial review) and must work on BOTH Claude Code and Codex -> read `agents/runtime-portability-protocol.md`: bind each abstract operation to the runtime (Claude `Task`/`Workflow`/`AskUserQuestion`/task-spine ↔ Codex named-agent spawn / inline / plain prompt / file-backed queue), detect the runtime once at kickoff, never probe Claude-only primitives on Codex. Shared policy (delegation, no-silent-fallback, worktree isolation) stays in `AGENTS.md` — cited, not restated.
|
|
47
|
+
- If conducting research (technology evaluation, best practices, compliance survey), spawning `senior-researcher`, or managing the research library (`${paths.research_dir}/`) -> read `agents/research-protocol.md` (the matching `SECTION=` only): pass a `PROFILE=<decision|deep|compare|regulatory>` token, run the library reuse pre-flight BEFORE searching, and route sources by nature via the source matrix. Interactive channel: the `/research` skill.
|
|
46
48
|
- If building or maintaining a derived LLM wiki overlay (`${paths.wiki_dir}/`) -> read `agents/llm-wiki-methodology.md` and invoke `wiki-curator` / `/capture`.
|
|
47
49
|
- For day-to-day status -> read and update `${paths.references_dir}/project-status.md`.
|
|
48
50
|
- For legacy context -> read `archive/project_full_legacy.md` if it exists.
|
|
@@ -85,6 +87,7 @@ When adding or updating agents, update REGISTRY.md — not this file.
|
|
|
85
87
|
- `agents/agent-operating-protocol.md` — Shared operating procedures for the core agents (injection guard, doc-retrieval consumption, persistent-memory hygiene, tool-budget discipline), `SECTION=` dispatch, read only the matching section; agents keep 1-line binding versions inline (since v5.0.0)
|
|
86
88
|
- `agents/review-protocol.md` — The shared verification engine for reviewers: Challenge + Actionability, Simulation (diff-walk / plan-walk), Chain-of-Verification, quantified risk scoring + absolute severity calibration, specialist-spawn discipline incl. orchestrated-mode `specialist_dispatch` suppression; `SECTION=` dispatch (since v5.0.0)
|
|
87
89
|
- `agents/doc-audit-protocol.md` — doc-reviewer's audit-mode procedures (drift validator suite, topological generation, epistemic metadata, SCIP anchors, schema/registry drift, coverage gauges); read ONLY when invoked without card context; `SECTION=` dispatch (since v5.0.0)
|
|
90
|
+
- `agents/research-protocol.md` — Research discipline: `PROFILE=<decision|deep|compare|regulatory>` output contracts (+ `DEPTH=` iterative loop), the research library (`paths.research_dir` — layout, report frontmatter, INDEX, archive-not-delete), the reuse pre-flight (`FULL_REUSE`/`DELTA`/`NEW` + per-category TTLs), and the versioned source matrix with its growth loop (`SOURCE_MATRIX_CANDIDATE`); `SECTION=` dispatch. The research-side sibling of `analysis-profiles.md` (since v5.1.0)
|
|
88
91
|
|
|
89
92
|
## Where to Document (Decision Tree)
|
|
90
93
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Research Protocol — profiles, library, reuse, and the source matrix
|
|
2
|
+
|
|
3
|
+
**Purpose**: the single SSOT for how BALDART conducts research. Consumed by the
|
|
4
|
+
`senior-researcher` agent (worker) and the `/research` skill (interactive
|
|
5
|
+
orchestrator); `/prd` Step 2.5 spawns the worker directly with the tokens
|
|
6
|
+
defined here. One research contract used to fit every request — a
|
|
7
|
+
publication-grade literature review — which conflicted with what orchestrators
|
|
8
|
+
actually need (fast, stack-aware, decision-ready answers) and buried finished
|
|
9
|
+
research where it was never reused. This module fixes both: **profiles** shape
|
|
10
|
+
the output to the purpose, the **library** makes every report a reusable,
|
|
11
|
+
indexed asset, and the **source matrix** routes each research nature to the
|
|
12
|
+
right sources and grows over time.
|
|
13
|
+
|
|
14
|
+
This is the research-side sibling of `analysis-profiles.md` (same mechanism:
|
|
15
|
+
one agent + a `PROFILE=` prompt token + one SSOT module — never a twin agent).
|
|
16
|
+
Prompt-level, zero new config keys beyond `paths.research_dir`, zero runtime
|
|
17
|
+
dependency.
|
|
18
|
+
|
|
19
|
+
## Contract
|
|
20
|
+
|
|
21
|
+
- **Dispatch**: consumers cite a section as
|
|
22
|
+
`agents/research-protocol.md SECTION=<profiles|library|reuse|sources>`.
|
|
23
|
+
When you (an agent or skill) need the full procedure, Grep this file for the
|
|
24
|
+
heading `### SECTION: <name>` and Read ONLY that section — never this module
|
|
25
|
+
end-to-end.
|
|
26
|
+
- **Degrade-safe**: everything here degrades, never aborts. No
|
|
27
|
+
`paths.research_dir` → deliver to the prompt-supplied path (or
|
|
28
|
+
`${paths.docs_dir}/` as last resort) and skip the library steps. No
|
|
29
|
+
`SOURCES.md` → fall back to the framework default matrix. No matrix at all →
|
|
30
|
+
proceed with judgment and say so in the report.
|
|
31
|
+
- The agent body keeps 1-line BINDING versions of these rules inline; if the
|
|
32
|
+
inline line and this module diverge, **this module wins** — fix the agent.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### SECTION: profiles
|
|
37
|
+
|
|
38
|
+
The `PROFILE=<decision|deep|compare|regulatory>` token in the spawn prompt
|
|
39
|
+
selects the research contract. **Token absent → `deep`** (preserves the legacy
|
|
40
|
+
full-report semantics for direct invocation and for existing spawners that
|
|
41
|
+
pass no token).
|
|
42
|
+
|
|
43
|
+
| Profile | Typical consumer | Output contract | Depth |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| `decision` | `/prd` Step 2.5, orchestrators mid-pipeline | Per-topic blocks, **recommendation first**: Recommendation (for THIS stack) → Best practices → Gotchas / anti-patterns → **Contradictions & Unknowns (mandatory section)** → Sources. Matches the `/prd` "Processing Findings" shape byte-for-byte. | Time-boxed; 1–3 questions; no ceremony sections |
|
|
46
|
+
| `deep` | direct user invocation, `/research` | The full §0–§11 report (Retrieval Index, TOC, Executive Summary, Taxonomy, Comparative Analysis, Key Findings, Recommendation, Risks, Evidence Map, Annotated Bibliography, Appendix with Search Log) | Full survey; see the DEPTH token below |
|
|
47
|
+
| `compare` | technology / vendor evaluations | Comparison matrix on fixed axes — performance, cost, complexity, risk, maturity, adoption — then Recommendation + explicit "when NOT to use" per option | Medium; one matrix, no unbounded exploration |
|
|
48
|
+
| `regulatory` | `/prd` S2 signal, compliance questions | Obligation checklist (requirement → source → deadline/threshold → implementation note), jurisdiction-aware; normative sources only for the obligations themselves | High rigor, tightly scoped to the named jurisdiction(s) |
|
|
49
|
+
|
|
50
|
+
Rules that apply to EVERY profile:
|
|
51
|
+
|
|
52
|
+
- **AI-readable minimum**: every report — not just `deep` — is consumed by an
|
|
53
|
+
AI reader with limited context: short paragraphs, dense factual bullets,
|
|
54
|
+
modular self-contained sections, consistent terminology. The full ceremony
|
|
55
|
+
(Retrieval Index, Evidence Map, Search Log) belongs to `deep` only; the
|
|
56
|
+
lighter profiles carry traceability through their per-topic **Sources**
|
|
57
|
+
lists (dated + evidence-labeled).
|
|
58
|
+
- **Recommendation is stack-bound**: evaluate against the stack signature and
|
|
59
|
+
the internal-repo findings supplied in the prompt — "best for THIS project",
|
|
60
|
+
never "best in the abstract".
|
|
61
|
+
- **Recency bias**: for fast-moving topics (frameworks, APIs, tooling) prefer
|
|
62
|
+
sources < 18 months old; date-stamp every source in the bibliography.
|
|
63
|
+
- **Contradictions surface early**: when evidence is split or absent, say so
|
|
64
|
+
near the top (in `decision` it is a mandatory section) — a hidden
|
|
65
|
+
disagreement is the failure mode that sends a project in the wrong
|
|
66
|
+
direction.
|
|
67
|
+
|
|
68
|
+
**`DEPTH=<1..3>` (deep profile only, default 1)** — the iterative
|
|
69
|
+
breadth/depth loop, opt-in so legacy spawns cost what they cost today:
|
|
70
|
+
|
|
71
|
+
- `DEPTH=1` (default): single research pass — today's behavior.
|
|
72
|
+
- `DEPTH=2..3`: before searching, generate 3–5 expert *perspectives* on the
|
|
73
|
+
topic (architect, operator, security, cost-owner, end-user — pick what fits)
|
|
74
|
+
and derive each perspective's questions. Then per round: search → extract
|
|
75
|
+
*learnings* + *follow-up directions* → recurse into the most promising
|
|
76
|
+
directions until DEPTH is exhausted. Each round is bounded by the tool
|
|
77
|
+
budget (`agents/agent-operating-protocol.md SECTION=tool-budget`).
|
|
78
|
+
- At `DEPTH>=2`, before finalizing run a citation spot-check (CoVe-lite):
|
|
79
|
+
WebFetch the sources behind the decision-driving claims and verify they
|
|
80
|
+
actually support them; demote or drop any that don't.
|
|
81
|
+
|
|
82
|
+
### SECTION: library
|
|
83
|
+
|
|
84
|
+
The research library lives at `${paths.research_dir}` (key in
|
|
85
|
+
`baldart.config.yml`; seeded by the BALDART CLI):
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
${paths.research_dir}/
|
|
89
|
+
INDEX.md <- THE map. Agents do lookup HERE first, never by walking dirs.
|
|
90
|
+
SOURCES.md <- live source matrix (consumer-owned; see SECTION: sources)
|
|
91
|
+
architecture/ integrations/ regulatory/ ux-patterns/ algorithms/ tooling/
|
|
92
|
+
_archive/ <- superseded reports: banner added, row removed from INDEX
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **Category**: pick the closest of the six; a report that genuinely fits none
|
|
96
|
+
goes in the closest match with a distinguishing tag (do NOT invent new
|
|
97
|
+
top-level directories — the closed set keeps lookup deterministic).
|
|
98
|
+
- **Report frontmatter** (every report, mandatory — this is the source of
|
|
99
|
+
truth; INDEX.md is a regenerable view over it):
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
---
|
|
103
|
+
id: RES-<YYYYMMDD>-<slug> # date + slug, NEVER max+1 (see Concurrency)
|
|
104
|
+
title: <one line>
|
|
105
|
+
category: architecture|integrations|regulatory|ux-patterns|algorithms|tooling
|
|
106
|
+
tags: [<free-form, lowercase, for findability — be generous>]
|
|
107
|
+
profile: decision|deep|compare|regulatory
|
|
108
|
+
nature: scientific|dev|regulatory|ux|market
|
|
109
|
+
stack_signature: "<framework + database + auth, from baldart.config.yml>"
|
|
110
|
+
date: <YYYY-MM-DD>
|
|
111
|
+
valid_until: <YYYY-MM-DD> # date + category TTL (see SECTION: reuse)
|
|
112
|
+
status: current # current | superseded
|
|
113
|
+
questions:
|
|
114
|
+
- <the research questions answered>
|
|
115
|
+
sources_count: <n>
|
|
116
|
+
related: [] # ids of related reports
|
|
117
|
+
---
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- **INDEX.md row format** (append-only; one row per report):
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
| <id> | <title> | <category> | <profile> | <tags csv> | <date> | <valid_until> | <path> |
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- **Archive, never delete**: a superseded report moves to `_archive/`, gets a
|
|
127
|
+
first-line banner `> [!SUPERSEDED] Replaced by <id>. Non-authoritative.`,
|
|
128
|
+
`status: superseded` in frontmatter, and its INDEX row is removed (out of
|
|
129
|
+
the index = invisible to lookup, history preserved in git).
|
|
130
|
+
- **Concurrency**: IDs are `RES-<YYYYMMDD>-<slug>` (add a short unique suffix
|
|
131
|
+
only on a real collision) — never "max existing + 1", which races under
|
|
132
|
+
parallel spawns. When an orchestrator (the `/research` skill, `/prd`) runs
|
|
133
|
+
the show, **the orchestrator pre-allocates id + full path in each spawn
|
|
134
|
+
prompt and serializes the INDEX appends itself** in its filing step; a
|
|
135
|
+
worker spawned solo appends its own row. Reconciliation is deterministic:
|
|
136
|
+
`rebuild-research-index.mjs` (shipped with the `/research` skill)
|
|
137
|
+
regenerates INDEX.md from report frontmatter.
|
|
138
|
+
- **Delivery-path resolution** (total, in order — never undefined, never
|
|
139
|
+
abort): (1) an explicit output path in the spawn prompt is AUTHORITATIVE;
|
|
140
|
+
(2) else `${paths.research_dir}/<category>/<id>-<slug>.md`;
|
|
141
|
+
(3) else (no path, no key) `${paths.docs_dir}/`.
|
|
142
|
+
|
|
143
|
+
### SECTION: reuse
|
|
144
|
+
|
|
145
|
+
Research repeats across features and projects. Before ANY new research, run
|
|
146
|
+
this pre-flight (skip it only when `paths.research_dir` is missing/empty —
|
|
147
|
+
then use the prompt-supplied path and proceed as a plain one-shot run):
|
|
148
|
+
|
|
149
|
+
1. **Lookup**: Read `INDEX.md` (only the index — not the reports). Match the
|
|
150
|
+
topic against `title`, `tags`, `category`.
|
|
151
|
+
2. **On hit, judge fitness**:
|
|
152
|
+
- **Freshness**: compare today against `valid_until`. Category TTLs (used
|
|
153
|
+
to SET `valid_until` at write time): `regulatory` 6 months ·
|
|
154
|
+
`integrations`/`tooling` 6–12 · `ux-patterns` 12 ·
|
|
155
|
+
`architecture`/`algorithms` 24.
|
|
156
|
+
- **Stack match**: compare the report's `stack_signature` with the current
|
|
157
|
+
one.
|
|
158
|
+
3. **Decide and DECLARE the decision in your report/return** (`REUSE:` line):
|
|
159
|
+
- `FULL_REUSE` — fresh + same stack + questions covered → return the
|
|
160
|
+
existing report; do not search again.
|
|
161
|
+
- `DELTA` — partially stale, stack drifted, or questions partially covered
|
|
162
|
+
→ update ONLY what expired, publish as a new report that `related:`-links
|
|
163
|
+
the old one; the old report is archived per SECTION: library.
|
|
164
|
+
- `NEW` — no usable hit.
|
|
165
|
+
- **Anti-false-reuse guard**: doubtful staleness or a different stack →
|
|
166
|
+
`DELTA`, never `FULL_REUSE`. Reuse must never silently serve a stale
|
|
167
|
+
answer to a decision-making consumer.
|
|
168
|
+
4. **Orchestrator polling contract**: when the spawn prompt pre-allocates an
|
|
169
|
+
output path, ALWAYS write a file there — the new report (`NEW`/`DELTA`), or
|
|
170
|
+
a stub for `FULL_REUSE`:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
RESEARCH_OUTPUT_PATH: <this file's own path>
|
|
174
|
+
REUSE: FULL_REUSE -> <path of the existing report>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
so a file-exists poll never times out and never matches a stale artifact.
|
|
178
|
+
|
|
179
|
+
### SECTION: sources
|
|
180
|
+
|
|
181
|
+
The source matrix routes each research **nature** to the right sources. Schema
|
|
182
|
+
(rows = natures; the columns are fixed):
|
|
183
|
+
|
|
184
|
+
| Column | Meaning |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `nature` | scientific · dev · regulatory · ux · market |
|
|
187
|
+
| `primary` | search these first; sufficient alone for [STRONG]/[MODERATE] claims |
|
|
188
|
+
| `secondary` | corroboration and leads; never sole support for a decision-driving claim |
|
|
189
|
+
| `avoid` | known-poor signal for this nature (content farms, marketing, SEO spam) |
|
|
190
|
+
| `notes` | nature-specific heuristics (recency windows, venue quality cues) |
|
|
191
|
+
|
|
192
|
+
**Resolution order** (no dual-SSOT — the default matrix lives in ONE file):
|
|
193
|
+
|
|
194
|
+
1. `${paths.research_dir}/SOURCES.md` — the live, consumer-owned matrix
|
|
195
|
+
(framework defaults + the project's `## Local additions`).
|
|
196
|
+
2. `.framework/framework/templates/research-sources.template.md` — the
|
|
197
|
+
framework default matrix (readable inside every consumer install).
|
|
198
|
+
3. Neither readable → proceed with judgment and state in the report that no
|
|
199
|
+
source matrix was available.
|
|
200
|
+
|
|
201
|
+
**Growth loop** (how the matrix improves project after project): when a run
|
|
202
|
+
hits a matrix gap — an unmapped nature, a newly discovered source that proved
|
|
203
|
+
high-quality in THIS run, or a mapped source that proved poor — append to the
|
|
204
|
+
END of the report:
|
|
205
|
+
|
|
206
|
+
```markdown
|
|
207
|
+
## SOURCE_MATRIX_CANDIDATE
|
|
208
|
+
|
|
209
|
+
- nature: <matrix row>
|
|
210
|
+
change: add-source | demote-source | new-nature
|
|
211
|
+
source: <name + URL>
|
|
212
|
+
tier: primary | secondary | avoid
|
|
213
|
+
evidence: <what happened in this run that proves it, 1-3 lines>
|
|
214
|
+
|
|
215
|
+
### Prompt for BALDART (copy-paste)
|
|
216
|
+
|
|
217
|
+
Aggiorna la matrice fonti in `framework/templates/research-sources.template.md`:
|
|
218
|
+
<change> per nature `<nature>`: `<source>` come `<tier>` — evidenza: <evidence>.
|
|
219
|
+
Bump `matrix_version` (minor per add, patch per demote/note) e aggiungi la voce
|
|
220
|
+
a `framework/templates/research-sources.CHANGELOG.md`.
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
(the research twin of skill-improver's `## Upstream Candidates`). Also mention
|
|
224
|
+
the candidate in your COMPACT return so the orchestrator can act on it. The
|
|
225
|
+
`/research` skill then offers two applications: (a) immediately to the local
|
|
226
|
+
`SOURCES.md` `## Local additions`, and/or (b) the copy-paste prompt upstream to
|
|
227
|
+
the BALDART maintainer session. Emit a candidate ONLY with run-local evidence —
|
|
228
|
+
never from general knowledge.
|