@hegemonart/get-design-done 1.50.1 → 1.52.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +93 -0
- package/README.md +4 -0
- package/SKILL.md +4 -1
- package/agents/a11y-mapper.md +30 -1
- package/agents/component-taxonomy-mapper.md +30 -1
- package/agents/design-debt-crawler.md +60 -60
- package/agents/design-reflector.md +33 -0
- package/agents/design-research-synthesizer.md +27 -1
- package/agents/motion-mapper.md +35 -13
- package/agents/token-mapper.md +30 -1
- package/agents/visual-hierarchy-mapper.md +30 -1
- package/dist/claude-code/.claude/skills/apply-reflections/SKILL.md +17 -0
- package/dist/claude-code/.claude/skills/context/SKILL.md +137 -0
- package/dist/claude-code/.claude/skills/extract-learnings/SKILL.md +16 -0
- package/dist/claude-code/.claude/skills/instinct/SKILL.md +111 -0
- package/dist/claude-code/.claude/skills/migrate-context/SKILL.md +123 -0
- package/dist/claude-code/.claude/skills/progress/SKILL.md +4 -0
- package/hooks/gdd-decision-injector.js +115 -6
- package/package.json +3 -2
- package/reference/design-context-schema.md +159 -0
- package/reference/design-context-tag-vocab.md +82 -0
- package/reference/instinct-format.md +120 -0
- package/reference/registry.json +21 -0
- package/reference/schemas/design-context.schema.json +130 -0
- package/reference/schemas/events.schema.json +1 -1
- package/reference/schemas/instinct.schema.json +91 -0
- package/reference/schemas/mcp-gdd-tools.schema.json +34 -1
- package/reference/skill-graph.md +4 -1
- package/scripts/lib/design-context/extract-a11y.mjs +188 -0
- package/scripts/lib/design-context/extract-components.mjs +243 -0
- package/scripts/lib/design-context/extract-motion.mjs +248 -0
- package/scripts/lib/design-context/extract-tokens.mjs +234 -0
- package/scripts/lib/design-context/extract-visual-hierarchy.mjs +178 -0
- package/scripts/lib/design-context/integration-map.mjs +251 -0
- package/scripts/lib/design-context/merge-fragments.mjs +227 -0
- package/scripts/lib/design-context-query.cjs +0 -0
- package/scripts/lib/instinct-store.cjs +677 -0
- package/scripts/lib/manifest/skills.json +24 -0
- package/scripts/lib/mcp-tools-lint/index.cjs +3 -1
- package/sdk/mcp/gdd-mcp/schemas/gdd_context_query.schema.json +60 -0
- package/sdk/mcp/gdd-mcp/server.js +474 -158
- package/sdk/mcp/gdd-mcp/server.ts +9 -5
- package/sdk/mcp/gdd-mcp/tools/gdd_context_query.ts +35 -0
- package/sdk/mcp/gdd-mcp/tools/index.ts +18 -13
- package/skills/apply-reflections/SKILL.md +17 -0
- package/skills/context/SKILL.md +137 -0
- package/skills/extract-learnings/SKILL.md +16 -0
- package/skills/instinct/SKILL.md +111 -0
- package/skills/migrate-context/SKILL.md +123 -0
- package/skills/progress/SKILL.md +4 -0
|
@@ -402,6 +402,96 @@ function buildAdrBlock(matched) {
|
|
|
402
402
|
return lines.join('\n');
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
+
/**
|
|
406
|
+
* Phase 51 — surface the project instinct store's top matches for the opened
|
|
407
|
+
* file. Instinct units are atomic, confidence-weighted design instincts learned
|
|
408
|
+
* across cycles (schema: reference/instinct-format.md), persisted + ranked by
|
|
409
|
+
* scripts/lib/instinct-store.cjs. The store is a SIBLING module — we reference
|
|
410
|
+
* it by name and never reimplement its query/decay logic.
|
|
411
|
+
*
|
|
412
|
+
* Two seams keep this testable + resilient:
|
|
413
|
+
* - `instinctTokens()` derives the query keyword(s) from the opened file's
|
|
414
|
+
* basename + relPath (mirrors the matchTokens derivation used for glossary
|
|
415
|
+
* /ADR matching, so an opened `color-tokens.md` queries on `color`+`tokens`).
|
|
416
|
+
* - `buildInstinctsBlock()` is a PURE renderer over already-fetched units, so
|
|
417
|
+
* the populated-path render is unit-testable without the store on disk.
|
|
418
|
+
*
|
|
419
|
+
* The store query itself (in main()) is wrapped in try/catch and is fully
|
|
420
|
+
* non-fatal: if the store module is absent (sibling not yet installed) or its
|
|
421
|
+
* data file is missing/corrupt, the block is silently skipped and the hook
|
|
422
|
+
* still returns { continue: true }.
|
|
423
|
+
*/
|
|
424
|
+
function instinctTokens(basename, relPath) {
|
|
425
|
+
const stem = basename.replace(/\.md$/i, '');
|
|
426
|
+
return Array.from(new Set([
|
|
427
|
+
stem,
|
|
428
|
+
...stem.split(/[-_./\\]/),
|
|
429
|
+
...String(relPath || '').split(/[-_./\\]/),
|
|
430
|
+
].filter((t) => t && t.length > 2)));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Pure renderer: format up to the top-3 instinct units as a compact block.
|
|
435
|
+
* Each line carries the trigger, confidence (2dp), and domain. Returns null when
|
|
436
|
+
* there are no units so main() can omit the heading entirely.
|
|
437
|
+
*
|
|
438
|
+
* Defensive: tolerates missing fields (trigger/confidence/domain) and non-array
|
|
439
|
+
* input so a malformed store payload can never throw on the Read hot path.
|
|
440
|
+
*
|
|
441
|
+
* @param {Array<{id?: string, trigger?: string, confidence?: number, domain?: string}>} units
|
|
442
|
+
* @returns {string | null}
|
|
443
|
+
*/
|
|
444
|
+
function buildInstinctsBlock(units) {
|
|
445
|
+
if (!Array.isArray(units) || units.length === 0) return null;
|
|
446
|
+
const top = units.slice(0, 3);
|
|
447
|
+
const lines = [];
|
|
448
|
+
lines.push('');
|
|
449
|
+
lines.push('### Relevant instincts');
|
|
450
|
+
for (const u of top) {
|
|
451
|
+
if (!u || typeof u !== 'object') continue;
|
|
452
|
+
const trigger = (u.trigger == null ? '' : String(u.trigger)).trim() || '(no trigger)';
|
|
453
|
+
const conf = typeof u.confidence === 'number' && Number.isFinite(u.confidence)
|
|
454
|
+
? u.confidence.toFixed(2)
|
|
455
|
+
: '?';
|
|
456
|
+
const domain = (u.domain == null ? '' : String(u.domain)).trim() || 'unknown';
|
|
457
|
+
lines.push(`> - ${trigger} (confidence ${conf}, ${domain})`);
|
|
458
|
+
}
|
|
459
|
+
// If every unit was malformed (no valid line emitted), drop the block.
|
|
460
|
+
if (lines.length <= 2) return null;
|
|
461
|
+
lines.push('');
|
|
462
|
+
return lines.join('\n');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Non-fatal glue: query the project instinct store for the opened file's tokens
|
|
467
|
+
* and render the block. Any failure (module absent, query throws, bad data)
|
|
468
|
+
* yields null so the caller simply omits the section. NEVER throws.
|
|
469
|
+
*
|
|
470
|
+
* @param {string} cwd — project root (passed to the store as baseDir)
|
|
471
|
+
* @param {string} basename
|
|
472
|
+
* @param {string} relPath
|
|
473
|
+
* @returns {string | null}
|
|
474
|
+
*/
|
|
475
|
+
function queryInstinctsBlock(cwd, basename, relPath) {
|
|
476
|
+
try {
|
|
477
|
+
// Sibling module — referenced by name, resolved relative to this hook.
|
|
478
|
+
// Absent in installs that predate Phase 51's instinct store.
|
|
479
|
+
// eslint-disable-next-line node/no-missing-require, global-require
|
|
480
|
+
const store = require(path.join(__dirname, '..', 'scripts', 'lib', 'instinct-store.cjs'));
|
|
481
|
+
if (!store || typeof store.query !== 'function') return null;
|
|
482
|
+
const tokens = instinctTokens(basename, relPath);
|
|
483
|
+
if (tokens.length === 0) return null;
|
|
484
|
+
// instinct-store.query() takes a STRING keyword (it splits on whitespace
|
|
485
|
+
// into terms) — pass the tokens space-joined, not the raw array.
|
|
486
|
+
const keyword = tokens.join(' ');
|
|
487
|
+
const units = store.query(keyword, { scope: 'project', baseDir: cwd, limit: 3 });
|
|
488
|
+
return buildInstinctsBlock(units);
|
|
489
|
+
} catch {
|
|
490
|
+
// Store missing or query failed — surface no instinct block, never crash.
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
405
495
|
function buildRecallBlock(matches, basename, backendLabel) {
|
|
406
496
|
if (!matches.length) return null;
|
|
407
497
|
const uniq = [];
|
|
@@ -526,21 +616,40 @@ async function main() {
|
|
|
526
616
|
adrBlock = buildAdrBlock(matchedAdrs);
|
|
527
617
|
} catch { /* defensive: never crash on ADR issues */ }
|
|
528
618
|
|
|
529
|
-
|
|
619
|
+
// Phase 51: surface the project instinct store's top-3 matches for the
|
|
620
|
+
// opened file. Fully non-fatal — queryInstinctsBlock swallows a missing
|
|
621
|
+
// store / bad data and returns null, so this can never break a Read.
|
|
622
|
+
const instinctsBlock = queryInstinctsBlock(cwd, basename, relPath);
|
|
623
|
+
|
|
624
|
+
if (!block && !protoBlock && !glossaryBlock && !adrBlock && !instinctsBlock) {
|
|
530
625
|
try { require('./_hook-emit.js').emitHookFired('gdd-decision-injector', 'no-hits', { backend: backendLabel }); } catch { /* swallow */ }
|
|
531
626
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
532
627
|
return;
|
|
533
628
|
}
|
|
534
629
|
|
|
535
|
-
const additionalContext = [block, protoBlock, glossaryBlock, adrBlock].filter(Boolean).join('\n');
|
|
630
|
+
const additionalContext = [block, protoBlock, glossaryBlock, adrBlock, instinctsBlock].filter(Boolean).join('\n');
|
|
536
631
|
|
|
537
|
-
try { require('./_hook-emit.js').emitHookFired('gdd-decision-injector', 'inject', { backend: backendLabel, hit_count: hits.length, prototyping: !!protoBlock, glossary: !!glossaryBlock, adr: !!adrBlock }); } catch { /* swallow */ }
|
|
632
|
+
try { require('./_hook-emit.js').emitHookFired('gdd-decision-injector', 'inject', { backend: backendLabel, hit_count: hits.length, prototyping: !!protoBlock, glossary: !!glossaryBlock, adr: !!adrBlock, instincts: !!instinctsBlock }); } catch { /* swallow */ }
|
|
538
633
|
process.stdout.write(JSON.stringify({
|
|
539
634
|
continue: true,
|
|
540
635
|
hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext },
|
|
541
636
|
}));
|
|
542
637
|
}
|
|
543
638
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
639
|
+
// Auto-run when invoked directly (the hooks.json registration runs this hook
|
|
640
|
+
// as `node hooks/gdd-decision-injector.js`, where require.main === module).
|
|
641
|
+
// Guarding the auto-run lets tests require() the module in-process to unit-test
|
|
642
|
+
// the pure helpers without triggering a stdin read.
|
|
643
|
+
if (require.main === module) {
|
|
644
|
+
main().catch(() => {
|
|
645
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Exported for tests — pure helpers only; main() owns the I/O + contract.
|
|
650
|
+
module.exports = {
|
|
651
|
+
buildInstinctsBlock,
|
|
652
|
+
instinctTokens,
|
|
653
|
+
queryInstinctsBlock,
|
|
654
|
+
main,
|
|
655
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hegemonart/get-design-done",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
4
4
|
"description": "A design-quality pipeline for AI coding agents: brief, plan, implement, and verify UI work against your design system.",
|
|
5
5
|
"author": "Hegemon",
|
|
6
6
|
"homepage": "https://github.com/hegemonart/get-design-done",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"test:behavior": "node scripts/run-behavior-tests.cjs",
|
|
60
60
|
"typecheck": "tsc --noEmit",
|
|
61
61
|
"codegen:schemas": "node --experimental-strip-types scripts/codegen-schema-types.ts",
|
|
62
|
-
"lint:md": "npx --yes markdownlint-cli2 \"**/*.md\" \"
|
|
62
|
+
"lint:md": "npx --yes markdownlint-cli2 \"**/*.md\" \"#**/node_modules\" \"#.planning\" \"#.claude\" \"#test/fixtures/baselines\"",
|
|
63
63
|
"lint:links": "npx --yes lychee --no-progress --accept 200,206,403,429 \"**/*.md\" || true",
|
|
64
64
|
"lint:agentskills": "node scripts/lint-agentskills-spec.cjs",
|
|
65
65
|
"lint:changelog": "node scripts/lint-changelog.cjs",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"check:domain-links": "node scripts/check-domain-cross-links.cjs",
|
|
75
75
|
"check:no-duplication": "node scripts/check-no-duplication.cjs",
|
|
76
76
|
"validate:composition-graph": "node scripts/validate-composition-graph.cjs",
|
|
77
|
+
"validate:design-context": "node scripts/validate-design-context.cjs",
|
|
77
78
|
"validate:skill-frontmatter": "node scripts/validate-skill-frontmatter.cjs",
|
|
78
79
|
"build:skill-graph": "node scripts/generate-skill-graph.cjs",
|
|
79
80
|
"build:skill-graph:check": "node scripts/generate-skill-graph.cjs --check",
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-context-schema
|
|
3
|
+
type: reference
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
phase: 52
|
|
6
|
+
tags: [design-context, knowledge-graph, schema, nodes, edges, mapper, two-phase]
|
|
7
|
+
last_updated: 2026-06-03
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# DesignContext Graph Schema
|
|
11
|
+
|
|
12
|
+
The DesignContext graph is the typed knowledge graph of a design system. It records design
|
|
13
|
+
entities as `nodes` and the relationships between them as `edges`, so an agent can reason about
|
|
14
|
+
a system the way an engineer reads a dependency graph: which token a component consumes, which
|
|
15
|
+
screen a flow transitions to, which pattern an anti-pattern conflicts with. The canonical graph
|
|
16
|
+
lives at `.design/context-graph.json`. It is validated by `scripts/validate-design-context.cjs`
|
|
17
|
+
and queried by `scripts/lib/design-context-query.cjs`.
|
|
18
|
+
|
|
19
|
+
This file documents the four shapes (Node, Edge, Fragment, Graph), the ten node types, the twelve
|
|
20
|
+
edge types, and the two-phase mapper pattern that builds the graph. The controlled `tags[]`
|
|
21
|
+
vocabulary lives in `./design-context-tag-vocab.md`; the JSON Schema lives at
|
|
22
|
+
`./schemas/design-context.schema.json`.
|
|
23
|
+
|
|
24
|
+
## Shapes
|
|
25
|
+
|
|
26
|
+
Four shapes make up the contract. A Graph is the assembled whole; a Fragment is one mapper's
|
|
27
|
+
contribution before merge; Nodes and Edges are the parts.
|
|
28
|
+
|
|
29
|
+
### Node
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
{ id, type, name, summary, tags[], complexity }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `id`: stable unique string; edges reference it. A duplicate id is a hard error.
|
|
36
|
+
- `type`: one of the ten node types below.
|
|
37
|
+
- `name`: human-readable name of the entity.
|
|
38
|
+
- `summary`: a one-line description of what the entity is and does. The deterministic pass may
|
|
39
|
+
leave this empty; the summary pass fills it. An empty summary, or one identical to `name`, is a
|
|
40
|
+
soft warning (a stub).
|
|
41
|
+
- `tags[]`: strings from the controlled vocabulary. An unknown tag is a soft warning.
|
|
42
|
+
- `complexity`: one of `simple`, `moderate`, `complex`.
|
|
43
|
+
- `subtype` *(optional)*: a finer class. For a `token` node one of
|
|
44
|
+
`color / spacing / typography / radius / shadow`; for a `layer` node one of
|
|
45
|
+
`Atomic / Molecular / Organism / Template`.
|
|
46
|
+
|
|
47
|
+
### Edge
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
{ source, target, type, direction, weight }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- `source`, `target`: node ids. Both must resolve to an existing node; a dangling endpoint is a
|
|
54
|
+
hard error.
|
|
55
|
+
- `type`: one of the twelve edge types below.
|
|
56
|
+
- `direction`: one of `forward`, `backward`, `bidirectional`. A forward edge reads
|
|
57
|
+
source-to-target; a backward edge reads target-to-source; a bidirectional edge reads both ways.
|
|
58
|
+
Traversal in the query library honors direction.
|
|
59
|
+
- `weight`: a number in the inclusive range 0 to 1. It expresses relationship strength, not
|
|
60
|
+
distance, so a stronger tie has a higher weight.
|
|
61
|
+
|
|
62
|
+
### Fragment
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
{ schema_version, mapper, generated_at, nodes[], edges[] }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A Fragment is the output of a single mapper (the Figma mapper, the codebase mapper, the docs
|
|
69
|
+
mapper). It carries a `mapper` field naming its origin and is merged into the Graph by union on
|
|
70
|
+
node `id`. Fragments let mappers run independently and write narrow outputs that later combine.
|
|
71
|
+
|
|
72
|
+
### Graph
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
{ schema_version, generated_at, nodes[], edges[] }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The Graph is the merged, canonical document at `.design/context-graph.json`. `schema_version` is
|
|
79
|
+
required; `generated_at` is an optional ISO-8601 timestamp recording when the graph was assembled.
|
|
80
|
+
|
|
81
|
+
## Node types
|
|
82
|
+
|
|
83
|
+
Ten node types cover the entities a design system holds. Each entry states what the type is and
|
|
84
|
+
when a mapper emits it.
|
|
85
|
+
|
|
86
|
+
- **token**: a design-token leaf such as a color, a spacing step, or a type ramp value. Emitted
|
|
87
|
+
for every resolvable token; carries a `subtype` naming the token family.
|
|
88
|
+
- **component**: a reusable UI unit (a button, a card, an input). Emitted once per component
|
|
89
|
+
regardless of how many variants it has.
|
|
90
|
+
- **variant**: a named configuration of a component (a button's `primary` or `ghost`). Emitted
|
|
91
|
+
when a component exposes distinct variants worth addressing on their own.
|
|
92
|
+
- **state**: an interaction or lifecycle state of a component (`hover`, `disabled`, `loading`).
|
|
93
|
+
Emitted when state changes carry their own styling or behavior.
|
|
94
|
+
- **motion-fragment**: a reusable motion unit (an enter transition, an easing curve, a loop).
|
|
95
|
+
Emitted when motion is named and reused rather than ad hoc.
|
|
96
|
+
- **a11y-pattern**: an accessibility pattern (a focus-trap, a skip-link, an aria-live region).
|
|
97
|
+
Emitted when a component participates in a named accessibility contract.
|
|
98
|
+
- **screen**: a full page or view in a product flow. Emitted once per distinct screen.
|
|
99
|
+
- **layer**: a composition layer in the atomic taxonomy. Carries a `subtype` of `Atomic`,
|
|
100
|
+
`Molecular`, `Organism`, or `Template`. Emitted to record where an entity sits in the hierarchy.
|
|
101
|
+
- **pattern**: a recurring solution that spans components (a master-detail layout, a wizard).
|
|
102
|
+
Emitted when a shape repeats across the system and earns a name.
|
|
103
|
+
- **anti-pattern**: a recurring shape the system wants to retire (a one-off color, a duplicated
|
|
104
|
+
control). Emitted so the graph can point at debt and the edges can mark conflicts.
|
|
105
|
+
|
|
106
|
+
## Edge types
|
|
107
|
+
|
|
108
|
+
Twelve edge types cover how entities relate. Each entry states the relationship and when it
|
|
109
|
+
applies.
|
|
110
|
+
|
|
111
|
+
- **uses-token**: a component or variant consumes a token. Source is the consumer, target is the
|
|
112
|
+
token.
|
|
113
|
+
- **composes**: a larger entity is built from a smaller one (a card composes a button). Source is
|
|
114
|
+
the whole, target is the part.
|
|
115
|
+
- **extends**: a variant or component specializes another (a `primary` button extends the base
|
|
116
|
+
button). Source is the specialization.
|
|
117
|
+
- **transitions-to**: a screen or state moves to another in a flow. Used for navigation graphs
|
|
118
|
+
and state machines.
|
|
119
|
+
- **depends-on**: a generic dependency where the more specific edge types do not fit. Source
|
|
120
|
+
needs target to function.
|
|
121
|
+
- **mirrors**: two entities are intentional reflections of each other (a light-mode and dark-mode
|
|
122
|
+
pair). Usually bidirectional.
|
|
123
|
+
- **conflicts-with**: two entities collide or duplicate intent (an anti-pattern conflicts with a
|
|
124
|
+
sanctioned pattern). Used to surface debt.
|
|
125
|
+
- **referenced-by**: an inbound citation: the target points at the source from outside the graph
|
|
126
|
+
core (a doc or a ticket references this entity). Often backward.
|
|
127
|
+
- **tested-by**: an entity is covered by a test. Source is the entity, target is the test node.
|
|
128
|
+
- **documented-by**: an entity is described by a doc. Source is the entity, target is the doc.
|
|
129
|
+
- **consumes-context**: an entity reads shared context provided elsewhere (a component reads a
|
|
130
|
+
theme provider). Source is the reader.
|
|
131
|
+
- **provides-context**: an entity supplies shared context to others (a theme provider supplies a
|
|
132
|
+
palette). Source is the provider; the matching reader uses `consumes-context`.
|
|
133
|
+
|
|
134
|
+
## The two-phase mapper pattern
|
|
135
|
+
|
|
136
|
+
A mapper builds the graph in two passes so the cheap, deterministic work is separate from the
|
|
137
|
+
costly, judgment-bearing work.
|
|
138
|
+
|
|
139
|
+
1. **Deterministic extract.** A pure pass walks the source (a Figma file, a component tree, a docs
|
|
140
|
+
set) and emits node and edge skeletons: ids, types, names, tags, complexity, and every edge it
|
|
141
|
+
can prove. Summaries are left empty. This pass is reproducible and dependency-free, so a graph
|
|
142
|
+
can be rebuilt the same way every run.
|
|
143
|
+
2. **LLM summary.** A second pass fills each node `summary` with a one-line description an agent
|
|
144
|
+
can read. Only the `summary` field changes; ids, types, and edges stay fixed from the extract
|
|
145
|
+
pass. This keeps the expensive pass narrow and lets the validator flag any node the summary
|
|
146
|
+
pass missed as a stub.
|
|
147
|
+
|
|
148
|
+
Splitting the work this way means the structure of the graph is deterministic and auditable, while
|
|
149
|
+
the prose layer can be regenerated on its own without disturbing the topology. A mapper writes a
|
|
150
|
+
Fragment; the assembler merges fragments into the canonical Graph.
|
|
151
|
+
|
|
152
|
+
## Cross-references
|
|
153
|
+
|
|
154
|
+
- Controlled `tags[]` vocabulary the validator enforces: see `./design-context-tag-vocab.md`.
|
|
155
|
+
- The JSON Schema (Draft-07) for the Graph: see `./schemas/design-context.schema.json`.
|
|
156
|
+
- The validator (structural, referential, completeness, tag checks):
|
|
157
|
+
`scripts/validate-design-context.cjs`.
|
|
158
|
+
- The query library (filters, BFS path, consumers, orphans, cycles, coverage):
|
|
159
|
+
`scripts/lib/design-context-query.cjs`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-context-tag-vocab
|
|
3
|
+
type: reference
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
phase: 52
|
|
6
|
+
tags: [design-context, knowledge-graph, tags, vocabulary, controlled-vocab]
|
|
7
|
+
last_updated: 2026-06-03
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# DesignContext Tag Vocabulary
|
|
11
|
+
|
|
12
|
+
This is the controlled vocabulary the DesignContext validator checks node `tags[]` against. A tag
|
|
13
|
+
outside this list is a soft warning, not a hard error, so a mapper can ship a new tag and the
|
|
14
|
+
graph still loads; the warning prompts a reviewer to either fix the tag or add it here.
|
|
15
|
+
|
|
16
|
+
## Source of truth
|
|
17
|
+
|
|
18
|
+
The canonical list lives in code, in the `TAG_VOCAB` set inside
|
|
19
|
+
`scripts/validate-design-context.cjs`. That set is what the validator enforces. This document
|
|
20
|
+
mirrors it for human readers and groups the tags by concern. When the set changes, update this
|
|
21
|
+
file by hand to match. If the two ever disagree, the code wins.
|
|
22
|
+
|
|
23
|
+
## Why a controlled vocabulary
|
|
24
|
+
|
|
25
|
+
Free-form tags drift: one node says `darkmode`, another `dark-mode`, a third `dark`. A query for
|
|
26
|
+
`dark-mode` then misses two of three. A controlled list keeps tags queryable and keeps the
|
|
27
|
+
`nodes(graph, { tag })` filter reliable. The list is deliberately broad rather than minimal, so
|
|
28
|
+
most real tags land inside it; the soft-warning behavior absorbs the rest without blocking a build.
|
|
29
|
+
|
|
30
|
+
## Tags by concern
|
|
31
|
+
|
|
32
|
+
The groupings below are for reading only. The validator treats the vocabulary as one flat set, so
|
|
33
|
+
a tag is valid regardless of which group it sits in.
|
|
34
|
+
|
|
35
|
+
- **Color**: `color`, `palette`, `theme`, `dark-mode`, `light-mode`, `contrast`, `gradient`,
|
|
36
|
+
`surface`, `brand`.
|
|
37
|
+
- **Spacing and sizing**: `spacing`, `sizing`, `density`, `gap`, `inset`, `stack`.
|
|
38
|
+
- **Typography**: `typography`, `font`, `type-scale`, `heading`, `body-text`, `label`, `numeric`.
|
|
39
|
+
- **Radius, shape, elevation**: `radius`, `shape`, `border`, `shadow`, `elevation`, `depth`.
|
|
40
|
+
- **Motion**: `motion`, `transition`, `animation`, `easing`, `duration`, `enter`, `exit`, `loop`,
|
|
41
|
+
`gesture`.
|
|
42
|
+
- **Accessibility**: `a11y`, `aria`, `focus`, `keyboard`, `screen-reader`, `reduced-motion`,
|
|
43
|
+
`contrast-safe`, `touch-target`.
|
|
44
|
+
- **Layout and structure**: `layout`, `grid`, `flex`, `responsive`, `breakpoint`, `container`,
|
|
45
|
+
`overflow`, `position`, `z-index`.
|
|
46
|
+
- **Interaction state**: `state`, `hover`, `active`, `disabled`, `loading`, `error`, `success`,
|
|
47
|
+
`selected`, `pressed`, `dragging`.
|
|
48
|
+
- **Component taxonomy**: `atom`, `molecule`, `organism`, `template`, `primitive`, `composite`,
|
|
49
|
+
`layout-primitive`.
|
|
50
|
+
- **Forms and inputs**: `form`, `input`, `control`, `validation`, `field`.
|
|
51
|
+
- **Navigation and structure**: `navigation`, `overlay`, `modal`, `menu`, `tabs`, `data-display`,
|
|
52
|
+
`feedback`, `media`.
|
|
53
|
+
- **Semantic role**: `interactive`, `static`, `decorative`, `destructive`, `utility`.
|
|
54
|
+
- **Quality flags**: `deprecated`, `experimental`, `stable`, `anti-pattern`, `review-needed`.
|
|
55
|
+
|
|
56
|
+
## Machine-readable list
|
|
57
|
+
|
|
58
|
+
The fenced block below is the same vocabulary as a flat list for any tool that wants to read the
|
|
59
|
+
doc instead of the code. Tokens are separated by commas or newlines (split on `/[\s,]+/`), so the
|
|
60
|
+
grouping into lines is cosmetic. It must stay equal to the `TAG_VOCAB` set in
|
|
61
|
+
`scripts/validate-design-context.cjs`.
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
color, palette, theme, dark-mode, light-mode, contrast, gradient, surface, brand,
|
|
65
|
+
spacing, sizing, density, gap, inset, stack,
|
|
66
|
+
typography, font, type-scale, heading, body-text, label, numeric,
|
|
67
|
+
radius, shape, border, shadow, elevation, depth,
|
|
68
|
+
motion, transition, animation, easing, duration, enter, exit, loop, gesture,
|
|
69
|
+
a11y, aria, focus, keyboard, screen-reader, reduced-motion, contrast-safe, touch-target,
|
|
70
|
+
layout, grid, flex, responsive, breakpoint, container, overflow, position, z-index,
|
|
71
|
+
state, hover, active, disabled, loading, error, success, selected, pressed, dragging,
|
|
72
|
+
atom, molecule, organism, template, primitive, composite, layout-primitive,
|
|
73
|
+
form, input, control, validation, field,
|
|
74
|
+
navigation, overlay, modal, menu, tabs, data-display, feedback, media,
|
|
75
|
+
interactive, static, decorative, destructive, utility,
|
|
76
|
+
deprecated, experimental, stable, anti-pattern, review-needed
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Cross-references
|
|
80
|
+
|
|
81
|
+
- The node and edge type catalogue and the four shapes: see `./design-context-schema.md`.
|
|
82
|
+
- The validator that enforces this vocabulary: `scripts/validate-design-context.cjs`.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: instinct-format
|
|
3
|
+
type: meta-rules
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
phase: 51
|
|
6
|
+
tags: [instinct, learning, confidence, beta-prior, promotion-gate, ttl-decay]
|
|
7
|
+
last_updated: 2026-06-03
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Instinct Unit Format
|
|
11
|
+
|
|
12
|
+
An instinct is an atomic, confidence-weighted lesson the pipeline learns across design
|
|
13
|
+
cycles. Where an ADR records a deliberate, hard-to-reverse decision (see `./adr-format.md`)
|
|
14
|
+
and `CONTEXT.md` records agreed vocabulary (see `./context-md-format.md`), an instinct
|
|
15
|
+
records a softer signal: a recurring "when X, prefer Y" reflex that earns trust by being
|
|
16
|
+
seen again and again. Each unit is one trigger sentence plus a short body, stored as a YAML
|
|
17
|
+
document and validated by `./schemas/instinct.schema.json`. The store
|
|
18
|
+
(`scripts/lib/instinct-store.cjs`) persists, queries, promotes, and decays them.
|
|
19
|
+
|
|
20
|
+
Instincts are always advisory, never directive. A fresh instinct carries low confidence and
|
|
21
|
+
is offered as a suggestion; only repeated cross-project observation raises that confidence,
|
|
22
|
+
and even then it is capped well below certainty.
|
|
23
|
+
|
|
24
|
+
## Unit shape
|
|
25
|
+
|
|
26
|
+
A unit is a YAML frontmatter block followed by a 1 to 3 paragraph body. The body explains
|
|
27
|
+
the reflex in plain language: what situation fires it, what to reach for, and why.
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
---
|
|
31
|
+
id: prefer-token-over-hex # kebab-case stable identifier
|
|
32
|
+
trigger: When a raw color literal appears in a component, reach for a design token first.
|
|
33
|
+
confidence: 0.5 # float in [0.3, 0.9]
|
|
34
|
+
domain: build # intake|explore|decide|build|verify|operate|utility
|
|
35
|
+
scope: project # project|global
|
|
36
|
+
project_id: abcd1234 # sha8 of the normalized git origin (project scope)
|
|
37
|
+
source: reflection # reflection|extract-learnings|user
|
|
38
|
+
cycles_seen: 1 # distinct cycles that surfaced this instinct
|
|
39
|
+
project_ids: [abcd1234] # distinct origins that surfaced it (promotion gate)
|
|
40
|
+
first_seen: 2026-06-01 # ISO date the unit was recorded
|
|
41
|
+
last_seen: 2026-06-01 # ISO date it was last surfaced (resets decay)
|
|
42
|
+
---
|
|
43
|
+
Hardcoded hex values drift away from the system palette over time. Routing the value
|
|
44
|
+
through a token keeps one source of truth and lets a theme change land everywhere at once.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Field reference
|
|
48
|
+
|
|
49
|
+
- **id** (required). Kebab-case, lowercase letters and digits with single hyphens, 3 to 80
|
|
50
|
+
characters. The id is stable: touching or promoting a unit never changes it.
|
|
51
|
+
- **trigger** (required). One sentence naming the situation that fires the instinct. Keep it
|
|
52
|
+
concrete enough that a reader knows when it applies.
|
|
53
|
+
- **confidence** (required). A float between 0.3 and 0.9. The floor of 0.3 says a brand-new
|
|
54
|
+
instinct is a hint, not a rule. The ceiling of 0.9 says no instinct is ever certain.
|
|
55
|
+
- **domain** (required). One of `intake`, `explore`, `decide`, `build`, `verify`, `operate`,
|
|
56
|
+
`utility`. These map to the Phase 50 lifecycle stages so an instinct surfaces at the right
|
|
57
|
+
moment in a cycle.
|
|
58
|
+
- **scope** (required). `project` for a lesson learned in one repository, `global` for one
|
|
59
|
+
promoted after it clears the cross-project gate below.
|
|
60
|
+
- **project_id** (required for project scope). The 8-character hex sha of the normalized git
|
|
61
|
+
origin, produced by `deriveProjectId`. A global unit may omit it, since a promoted instinct
|
|
62
|
+
is no longer tied to a single origin.
|
|
63
|
+
- **source** (required). Which producer minted the unit: a `reflection` pass, the
|
|
64
|
+
`extract-learnings` step, or a direct `user` assertion.
|
|
65
|
+
- **cycles_seen** (optional, defaults to 1). How many distinct cycles have surfaced the unit.
|
|
66
|
+
- **project_ids** (optional). The set of distinct origins that surfaced it. Its size feeds the
|
|
67
|
+
promotion gate.
|
|
68
|
+
- **first_seen** / **last_seen** (optional, stamped on write). ISO dates for recording and last
|
|
69
|
+
surfacing. `last_seen` resets the decay window.
|
|
70
|
+
|
|
71
|
+
## The Beta(2,8) prior
|
|
72
|
+
|
|
73
|
+
When an instinct is promoted to the global store it is seeded with a Beta(2,8) posterior, the
|
|
74
|
+
same conservative prior the Phase 38 `design_arms` store uses. Its posterior mean is 0.2, so a
|
|
75
|
+
promoted pattern still has to earn standing from real outcomes rather than starting trusted.
|
|
76
|
+
The store exposes this as `INSTINCT_PRIOR = {alpha: 2, beta: 8}`. Successful outcomes add to
|
|
77
|
+
`alpha`, disappointing ones add to `beta`, and the mean moves accordingly.
|
|
78
|
+
|
|
79
|
+
## Promotion gate (K=2 cycles across M=2 projects)
|
|
80
|
+
|
|
81
|
+
A project instinct may move to the global store only when both halves of the gate hold:
|
|
82
|
+
|
|
83
|
+
1. **K = 2 cycles.** `cycles_seen` is at least 2, so the lesson repeated rather than firing once.
|
|
84
|
+
2. **M = 2 projects.** The unit appears in at least 2 distinct `project_ids`, so the lesson
|
|
85
|
+
generalized beyond the repository that first taught it.
|
|
86
|
+
|
|
87
|
+
`promote(id)` throws a clear error when either half is unmet, so a one-off observation in a
|
|
88
|
+
single project can never leak into global advice. On a passing gate the unit is re-scoped to
|
|
89
|
+
`global`, the single-origin `project_id` is dropped, the Beta(2,8) prior is applied, and the
|
|
90
|
+
unit is removed from the project store. `touch(id)` is how a unit accumulates toward the gate:
|
|
91
|
+
each call bumps `last_seen` and `cycles_seen` and adds the current origin to `project_ids`.
|
|
92
|
+
|
|
93
|
+
## TTL decay
|
|
94
|
+
|
|
95
|
+
Instincts that stop showing up should fade rather than linger as stale advice. `decay()` walks
|
|
96
|
+
a scope and, for any unit not surfaced within the decay window of 6 cycles, multiplies its
|
|
97
|
+
confidence by 0.9. Once a unit's confidence falls below 0.2 it is archived: the store writes it
|
|
98
|
+
to `<root>/instincts/archive/<id>.json` with an `archived_at` stamp and removes it from the live
|
|
99
|
+
set. Archiving is reversible by hand, so the audit trail survives. Calling `touch(id)` resets the
|
|
100
|
+
window, so an instinct that keeps proving useful never decays.
|
|
101
|
+
|
|
102
|
+
## Storage and recall
|
|
103
|
+
|
|
104
|
+
Project units live at `<root>/instincts/instincts.json`, resolved through the worktree-aware
|
|
105
|
+
root so writes land in the main checkout rather than a throwaway worktree. Global units live at
|
|
106
|
+
`~/.claude/gdd/global-instincts.json`. Both files are written atomically (a temp file plus a
|
|
107
|
+
rename), and JSON is always the source of truth.
|
|
108
|
+
|
|
109
|
+
`query(keyword)` ranks units by how well the keyword matches their trigger, body, and domain,
|
|
110
|
+
returning the best few. When `better-sqlite3` is present with its FTS5 extension the query runs
|
|
111
|
+
over a small full-text index for speed; otherwise an in-memory scan answers the same query with
|
|
112
|
+
the same shape. `better-sqlite3` stays a runtime probe and is never a hard dependency, so recall
|
|
113
|
+
degrades gracefully wherever the native module is absent. `backendName()` reports which path is
|
|
114
|
+
active.
|
|
115
|
+
|
|
116
|
+
## Cross-references
|
|
117
|
+
|
|
118
|
+
- Hard-to-reverse decisions belong in an ADR, not an instinct. See `./adr-format.md`.
|
|
119
|
+
- Agreed domain vocabulary belongs in `CONTEXT.md`. See `./context-md-format.md`.
|
|
120
|
+
- The frontmatter schema is `./schemas/instinct.schema.json`, validated under Ajv.
|
package/reference/registry.json
CHANGED
|
@@ -1149,6 +1149,27 @@
|
|
|
1149
1149
|
"type": "meta-rules",
|
|
1150
1150
|
"phase": 50,
|
|
1151
1151
|
"description": "Phase 50 auto-generated skill composition graph (mermaid): skills grouped by lifecycle stage with composes_with and next_skills edges; regenerated by scripts/generate-skill-graph.cjs and drift-gated in CI."
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
"name": "instinct-format",
|
|
1155
|
+
"path": "reference/instinct-format.md",
|
|
1156
|
+
"type": "meta-rules",
|
|
1157
|
+
"phase": 51,
|
|
1158
|
+
"description": "Phase 51 instinct unit format: YAML frontmatter (id, trigger, confidence 0.3-0.9, domain, scope, project_id, source, cycles_seen, first/last_seen) + body; promotion gate K=2/M=2; Beta(2,8) prior; TTL decay. Stored via scripts/lib/instinct-store.cjs."
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
"name": "design-context-schema",
|
|
1162
|
+
"path": "reference/design-context-schema.md",
|
|
1163
|
+
"type": "schema",
|
|
1164
|
+
"phase": 52,
|
|
1165
|
+
"description": "Phase 52 (KEYSTONE) typed DesignContext graph: 10 node types + 12 edge types, Node/Edge/Fragment/Graph shapes, two-phase mapper pattern. Schema at reference/schemas/design-context.schema.json."
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
"name": "design-context-tag-vocab",
|
|
1169
|
+
"path": "reference/design-context-tag-vocab.md",
|
|
1170
|
+
"type": "meta-rules",
|
|
1171
|
+
"phase": 52,
|
|
1172
|
+
"description": "Phase 52 controlled tag vocabulary for DesignContext nodes; validate-design-context.cjs soft-warns on unknown tags."
|
|
1152
1173
|
}
|
|
1153
1174
|
]
|
|
1154
1175
|
}
|