@pilotspace/add 1.1.0 → 1.3.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/GETTING-STARTED.md +187 -139
  3. package/README.md +13 -7
  4. package/bin/cli.js +96 -5
  5. package/docs/01-principles.md +3 -3
  6. package/docs/02-the-flow.md +19 -12
  7. package/docs/03-step-1-specify.md +15 -13
  8. package/docs/04-step-2-scenarios.md +2 -2
  9. package/docs/05-step-3-contract.md +3 -3
  10. package/docs/06-step-4-tests.md +10 -2
  11. package/docs/07-step-5-build.md +3 -1
  12. package/docs/08-step-6-verify.md +25 -5
  13. package/docs/09-the-loop.md +12 -6
  14. package/docs/10-setup-and-stages.md +27 -13
  15. package/docs/11-governance.md +6 -2
  16. package/docs/12-roles.md +3 -3
  17. package/docs/13-adoption.md +1 -1
  18. package/docs/14-foundation.md +15 -15
  19. package/docs/15-foundations-and-lineage.md +106 -0
  20. package/docs/README.md +4 -0
  21. package/docs/appendix-a-templates.md +3 -3
  22. package/docs/appendix-b-prompts.md +40 -5
  23. package/docs/appendix-c-glossary.md +49 -12
  24. package/docs/appendix-d-worked-example.md +2 -2
  25. package/docs/appendix-e-checklists.md +16 -4
  26. package/docs/appendix-f-requirements-matrix.md +8 -8
  27. package/docs/appendix-g-references.md +106 -0
  28. package/package.json +1 -1
  29. package/skill/add/SKILL.md +41 -38
  30. package/skill/add/adopt.md +13 -11
  31. package/skill/add/deltas.md +8 -6
  32. package/skill/add/fold.md +19 -17
  33. package/skill/add/graduate.md +74 -0
  34. package/skill/add/intake.md +22 -7
  35. package/skill/add/loop.md +59 -0
  36. package/skill/add/phases/0-ground.md +66 -0
  37. package/skill/add/phases/0-setup.md +32 -25
  38. package/skill/add/phases/1-specify.md +28 -13
  39. package/skill/add/phases/2-scenarios.md +14 -4
  40. package/skill/add/phases/3-contract.md +27 -12
  41. package/skill/add/phases/4-tests.md +15 -5
  42. package/skill/add/phases/5-build.md +33 -4
  43. package/skill/add/phases/6-verify.md +40 -2
  44. package/skill/add/phases/7-observe.md +13 -5
  45. package/skill/add/report-template.md +65 -7
  46. package/skill/add/run.md +93 -39
  47. package/skill/add/scope.md +10 -6
  48. package/skill/add/setup-review.md +13 -10
  49. package/skill/add/streams.md +88 -23
  50. package/tooling/add.py +1817 -90
  51. package/tooling/templates/CONVENTIONS.md.tmpl +1 -1
  52. package/tooling/templates/DESIGN.md.tmpl +66 -0
  53. package/tooling/templates/GLOSSARY.md.tmpl +29 -0
  54. package/tooling/templates/MILESTONE.md.tmpl +1 -0
  55. package/tooling/templates/PROJECT.md.tmpl +6 -3
  56. package/tooling/templates/TASK.md.tmpl +55 -15
  57. package/tooling/templates/catalog.sample.json +38 -0
  58. package/tooling/templates/prototype.sample.json +48 -0
  59. package/tooling/templates/tokens.sample.json +55 -0
  60. package/tooling/templates/udd-catalog.md +122 -0
  61. package/tooling/templates/udd-tokens.md +79 -0
@@ -1,4 +1,4 @@
1
- # CONVENTIONS (survivor layer — set once, kept for the whole project)
1
+ # CONVENTIONS (living documentation — set once, kept for the whole project)
2
2
 
3
3
  Language/framework: <e.g. Python 3.12 / FastAPI>
4
4
  Folders: .add/tasks/<slug>/{TASK.md, tests/, src/}
@@ -0,0 +1,66 @@
1
+ # {{project}} — Design (DESIGN.md)
2
+
3
+ > The design source of truth for **{{project}}** ({{stage}}) — drafted {{date}}.
4
+ > The AI drafts every UI screen against this doc; the JSON foundation below is what
5
+ > the validators lint.
6
+ >
7
+ > **No UI in this project? This doc is optional — delete it.**
8
+
9
+ This doc is the **prose front-door** to the UDD foundation: it carries the design
10
+ *identity* and *intent* a human owns, then points at the named-set JSON
11
+ (`tokens.json` · `catalog.json` · `prototypes/`) the AI renders from.
12
+
13
+ ## Identity
14
+
15
+ Design identity is **human-owned** — set it here at specify; never let the AI invent
16
+ a brand. Fill each prompt with your real values, then delete the comment.
17
+
18
+ - **Brand color** — <!-- your one primary brand color, as your brand guide names it -->
19
+ - **Palette** — <!-- the supporting roles: accent · surface · text · success/warn/danger -->
20
+ - **Typeface** — <!-- the heading + body type families (and the weights you actually use) -->
21
+ - **Voice / tone** — <!-- 2–3 words, e.g. calm · precise · warm -->
22
+
23
+ Once set, these flow down into the **semantic** layer of `tokens.json` (the AI wires
24
+ the primitives beneath them); see Foundation below.
25
+
26
+ ## Principles
27
+
28
+ The design intent the AI drafts UI against — the persona, the one job, the feel.
29
+
30
+ - **Primary user & their job** — <!-- who this is for and the one thing they hire it to do -->
31
+ - **Design principles** — <!-- 3–5 lines, e.g. "one primary action per screen" · "never hide state" -->
32
+ - **Accessibility floor** — <!-- e.g. WCAG AA contrast · focus-visible · hit-target ≥ 44px -->
33
+
34
+ ## Screens
35
+
36
+ An index of the app's screens — one `design/prototypes/<name>.json` per screen (a flat
37
+ json-render tree the catalog validates). Add a row as each screen is designed.
38
+
39
+ | Screen | Prototype | Status |
40
+ |--------|-----------|--------|
41
+ | (worked example) | `design/prototypes/main.json` ← seed `prototype.sample.json` | sample |
42
+ | <!-- Home --> | <!-- design/prototypes/home.json --> | <!-- draft --> |
43
+
44
+ Start from `prototype.sample.json` (a clean Screen › Card › {Text, Text, Button})
45
+ and adapt it into `design/prototypes/<name>.json` per screen.
46
+
47
+ ## Foundation
48
+
49
+ The named-set JSON the validators lint — lives under `.add/design/`; start from the
50
+ shipped samples, then adapt:
51
+
52
+ - **Tokens** — `design/tokens.json` · the 3-layer design tokens (primitive · semantic ·
53
+ component). Dialect + divergences: `udd-tokens.md`. Start from `tokens.sample.json`.
54
+ - **Catalog** — `design/catalog.json` · the component catalog (typed props + token
55
+ bindings). Render adapter: `udd-catalog.md`. Start from `catalog.sample.json`.
56
+ - **Prototypes** — `design/prototypes/<name>.json` · the flat json-render screen trees
57
+ the catalog validates. Start from `prototype.sample.json`.
58
+
59
+ `add.py check` lints this named set in place — a layer/catalog/tree/cross-file violation
60
+ goes red with a named code; it is silent when there is no `design/` set.
61
+
62
+ ## Render
63
+
64
+ To turn the catalog + a prototype into a live, clickable UI, follow the render recipe
65
+ in **`udd-catalog.md`** (its `## Render recipe` section): `catalog.json` →
66
+ `defineCatalog(...)`, then `catalog.validate(spec)` on the flat tree as-is.
@@ -1,3 +1,32 @@
1
1
  # GLOSSARY (one name per concept — used everywhere: specs, contracts, code)
2
2
 
3
3
  <term>: <definition>
4
+
5
+ # ADD method vocabulary (domain-standard names; bridges to legacy terms)
6
+ GOAL: the one durable outcome a project (and each milestone) runs toward — the loop's orientation anchor, declared as the lowercase `goal:` line in PROJECT.md / MILESTONE.md and surfaced by status/guide every session; distinct from a task's §1 Must (a single required behavior, not the whole-project outcome).
7
+ deep verify: the deepened Verify evidence (v20) required beyond passing tests — for a task that produced code, that every new symbol is referenced (wiring) and no new dead/unused code exists; for prose/non-code, a recorded no-skim semantic read; which path applies is resolver-judged and the engine never classifies (a rubric, not add.py).
8
+ earned green: the build-integrity property — the green is EARNED when the implementation makes the UNCHANGED red suite pass by GENERAL behavior, not by overfitting src to the test fixtures, vacuous/tautological asserts, or stubbing real logic away; task 1's tamper tripwire protects it mechanically and the adversarial refute-read by judgment, and a confirmed earned-green failure is HARD-STOP-class (never auto-passed, never RISK-ACCEPTED).
9
+ adversarial refute-read: the verify-gate, whole-suite specialization of run.md's adversarial verify — an independent reviewer (a subagent under autonomy:auto is recommended; the engine never spawns one) prompted to argue "the green was NOT earned", scoring the earned-green cheats (overfit · vacuous · stub) the mechanical tripwire cannot see.
10
+ bounded self-heal: the verify-gate loop (heal-then-escalate) that gives a CONFIRMED cheat — mechanical tamper (the tripwire) or a reported earned-green failure (`add.py heal --reason`) — a chance to redo honestly before it stops: the engine returns the task to build, COUNTS the attempt, CAPS it at 3 (`HEAL_CAP`), and on the next confirmed cheat forces a HARD-STOP that escalates to the human. The counter is MONOTONIC — it never auto-resets (cmd_phase is unguarded, so a reset would be a zero-human cap bypass). The engine counts/caps/escalates; the AGENT does the honest re-build (never the engine). An honest build passes even at the cap (the cap bites a CONTINUED cheat, never a recovery); a gamed green is never auto-passed, never RISK-ACCEPTED-waived.
11
+ onboarding: the install -> first-milestone path (formerly "on-ramp").
12
+ primary flow: the solid forward path of the flow diagram — a phase starts only when its input exists (formerly "forward spine").
13
+ ground: the phase-0 preamble before specify — the AI gathers the real current codebase a task touches (files, symbols, signatures, conventions) into a §0 grounding map; AI-owned, adds no gate (the one approval stays at the contract freeze).
14
+ grounding map / anchors: the §0 GROUND artifact — the real files/symbols/conventions a task touches plus the anchors the frozen contract cites; task-specific delta only (defers to PROJECT.md / CONVENTIONS.md), surfaced by status/check (measure, never block).
15
+ cross-cutting concern: a concern running through every step rather than being one step — security, testing, observability, cost (formerly "spine / continuous concern").
16
+ working state: everything an agent loads each session — skill router, active phase, PROJECT/MILESTONE/TASK, state.json (formerly "state surface").
17
+ audit trail: the reference record read by people, never auto-loaded into agent context (formerly "story surface").
18
+ method rationale: the why behind every rule — the AIDD book, loaded on demand, never duplicated (formerly "trust layer").
19
+ failing-first suite: the test suite written before code, confirmed red for the right reason — a missing implementation (formerly "red safety net").
20
+ non-functional review: the deliberate post-evidence check of what tests rarely catch — concurrency, security, architecture (formerly "blind-spot checks").
21
+ change scope: the files a locked run may and may not touch (formerly "touch-boundary"; the <touch_boundary> XML prompt tag keeps its name).
22
+ automated quality gate: the evidence-based Verify resolver under autonomy auto — may auto-PASS on complete evidence; security always escalates (formerly "evidence auto-gate").
23
+ autonomy level: the explicit per-task Verify resolver setting on an ordered ladder — manual | conservative | auto (manual < conservative < auto): auto (the seeded default) auto-PASSes on complete evidence, conservative keeps a human at the gate, manual is the strict floor (the human owns the gate; nothing auto-resolves); declared in the TASK.md header, human-reviewed at the freeze (formerly "autonomy dial").
24
+ auto-ready goal: a milestone goal whose every exit criterion CITES a verifier `(verify: <test|command|metric>)` — so the engine can self-verify the result against the goal without human judgement. This is the prerequisite by which autonomy is EARNED by goal-clarity (the autonomy level then governs who resolves Verify, but a clarified goal is what makes a self-verifying run meaningful). `add.py check` WARNs (never red) the active milestone until its goal is auto-ready (total >= 1 AND every criterion cited) and `status` surfaces it (`goal-ready: auto-ready ✓` / cited-of-total); a zero-criteria goal is NOT auto-ready and is milestone-shaping's nudge, not this WARN's. The lint forces a citation slot per criterion (raising the floor) but cannot PROVE the citation is real — the human still owns whether it is honest (citation-theater is the accepted, irreducible floor; the freeze gate and autonomy behavior are unchanged by it).
25
+ living documentation: the durable project artifacts — conventions, glossary, frozen contracts — that outlive any particular code (formerly "survivor layer").
26
+ scope level: the granularity a decision lives at — intake level (request -> versioned scope), milestone level, setup/foundation level, task level (formerly "altitude").
27
+ baseline approval: the one human gate that freezes the AI-drafted foundation, first scope, and first contract together — runs as `add.py lock` (formerly "the lock-down").
28
+ lesson learned: a single learning a loop produces, tagged by the competency it improves — the `- [DDD · open]` grammar and deltas.md/`add.py deltas` machine names stay (formerly "competency delta").
29
+ lowest-confidence flag: the AI's ranked declaration of the 1–2 points most likely to be wrong in what a human is asked to approve — each with why + cost-if-wrong; the ⚠ glyph keeps its name as the machine marker (formerly "least-sure flag").
30
+ decision point: a stop for human judgment — the contract-freeze approval, an escalated verify gate, intake confirmation, milestone close; the machine names seam (--json owner enum, decide key) and seam-audit (CI job) keep their names (formerly "seam").
31
+ retrospective consolidation: gathering confirmed lessons learned at milestone close and writing them append-only into the versioned foundation — human-confirmed, never self-approved; the machine names fold.md, the folded status, and add.py deltas keep their names (formerly "the fold / fold ritual").
32
+ specification bundle: a task's spec, scenarios, contract, and failing tests drafted as one piece and approved by a person once at the contract freeze (formerly "the one-approval front").
@@ -1,6 +1,7 @@
1
1
  # MILESTONE: {{title}}
2
2
 
3
3
  goal: {{goal}}
4
+ rationale: <why this scope — the confirmed intake classification (bucket + reason)>
4
5
  stage: {{stage}} · status: active · created: {{date}}
5
6
 
6
7
  > SDD living doc for this milestone. Keep it THIN: breadth, shared decisions, and
@@ -1,4 +1,4 @@
1
- # PROJECT — survivor layer (cross-milestone context)
1
+ # PROJECT — living documentation (cross-milestone context)
2
2
 
3
3
  > The durable foundation that outlives every milestone and feeds context into each
4
4
  > TDD⇄ADD loop. Read this FIRST in any session. Keep it lean — one screen, not a
@@ -7,6 +7,9 @@
7
7
  > that is the re-entrant arrow from the engine down to the foundation.
8
8
 
9
9
  slug: {{project}} · stage: {{stage}} · updated: {{date}}
10
+ autonomy: auto <!-- project default — new tasks inherit this rung (manual < conservative < auto); lower a single task in its TASK.md header when it needs a human gate. -->
11
+ goal:
12
+ <!-- set at setup — the outcome this project runs toward; status/guide surface it every session -->
10
13
 
11
14
  ---
12
15
 
@@ -22,8 +25,8 @@ slug: {{project}} · stage: {{stage}} · updated: {{date}}
22
25
  <!-- The spec is a living document, not a frozen plan. This section POINTS to the
23
26
  active milestone + frozen contracts; it does not duplicate them. -->
24
27
  - Active milestone → `.add/milestones/<slug>/MILESTONE.md` (see `add.py status`)
25
- - Frozen contracts (survivor): the contracts other work builds against.
26
- - Settled vs still open:
28
+ - Frozen contracts (living docs): the contracts other work builds against.
29
+ - Settled vs still open: <frozen contracts · open questions>
27
30
 
28
31
  ## Users (UDD) — UI/UX: design before code
29
32
  <!-- UI/UX-Driven Development: users use the interface, not the spec. Capture the
@@ -1,9 +1,10 @@
1
1
  # TASK: {{title}}
2
2
 
3
3
  slug: {{slug}} · created: {{date}} · stage: {{stage}}
4
- phase: specify <!-- specify -> scenarios -> contract -> tests -> build -> verify -> observe -> done -->
5
- <!-- high-risk/method-defining scope? declare `risk: high` on the slug line above and lower
6
- the dial with `autonomy: conservative` the engine refuses an unguarded completion
4
+ autonomy: {{autonomy}} <!-- inherited from the project default (PROJECT.md); explicit level: manual < conservative < auto (visible · overridable) — lower below if a high-risk task needs it. -->
5
+ phase: ground <!-- ground -> specify -> scenarios -> contract -> tests -> build -> verify -> observe -> done -->
6
+ <!-- high-risk/method-defining scope? declare `risk: high` on the slug line above and lower the
7
+ autonomy level to `manual` or `conservative` — the engine refuses an unguarded completion
7
8
  (`unguarded_high_risk_auto`, run.md guard). A comment is never a declaration. -->
8
9
 
9
10
  > One file = one task. Fill sections top-to-bottom; the `add` skill drives each phase.
@@ -12,26 +13,45 @@ phase: specify <!-- specify -> scenarios -> contract -> tests -> build -> veri
12
13
 
13
14
  ---
14
15
 
16
+ ## 0 · GROUND — the real codebase ▸ docs/02-the-flow.md
17
+
18
+ Touches (files · symbols · signatures): <path:symbol — what it is / how it is keyed>
19
+ Context (working folder): <docs · todos · config · data the task touches — task-delta only>
20
+ Honors (patterns / conventions): <PROJECT.md / CONVENTIONS.md anchors — task-delta only, never a re-scan>
21
+ Anchors the contract cites: <the symbols §3 will name>
22
+
23
+ ---
24
+
15
25
  ## 1 · SPECIFY — the rules ▸ docs/03-step-1-specify.md
16
26
 
17
27
  Feature: <name>
18
28
  Framings weighed: <chosen> (chosen) · <alternative> · <alternative>
19
29
  Must:
30
+ <must>
20
31
  - <required behavior>
32
+ </must>
21
33
  Reject:
34
+ <reject>
22
35
  - <bad input / situation> -> "<error_code>"
36
+ </reject>
23
37
  After:
38
+ <after>
24
39
  - <state that is true once it succeeds>
25
- Assumptions — least-sure first:
26
- <the one assumption most likely to be wrong> least sure because <why>; if wrong: <cost>
40
+ </after>
41
+ Assumptionslowest-confidence first:
42
+ <assumptions>
43
+ ⚠ <the one assumption most likely to be wrong> — lowest confidence because <why>; if wrong: <cost>
27
44
  - [ ] <next assumption, ranked> — confirm or deny; never carry an open one forward
45
+ </assumptions>
28
46
 
29
- <!-- EXIT: every rule stated, every rejection named; assumptions ranked least-sure first, the top one or two ⚠-flagged with why + cost (or, for trivial scope, an honest "none material" that still names the single biggest risk). -->
47
+ <!-- EXIT: every rule stated, every rejection named; assumptions ranked lowest-confidence first, the top one or two ⚠-flagged with why + cost (or, for trivial scope, an honest "none material" that still names the single biggest risk). -->
30
48
 
31
49
  ---
32
50
 
33
51
  ## 2 · SCENARIOS — pass/fail cases ▸ docs/04-step-2-scenarios.md
34
52
 
53
+ <scenarios>
54
+
35
55
  ```gherkin
36
56
  Scenario: <short name>
37
57
  Given <starting situation>
@@ -40,6 +60,8 @@ Scenario: <short name>
40
60
  And <what must remain unchanged> # required for every rejection
41
61
  ```
42
62
 
63
+ </scenarios>
64
+
43
65
  <!-- EXIT: one scenario per Must AND per Reject; each result is observable. -->
44
66
 
45
67
  ---
@@ -53,20 +75,24 @@ Scenario: <short name>
53
75
  Schema: <tables/fields touched, and access pattern>
54
76
  ```
55
77
 
56
- Status: DRAFT <!-- becomes: FROZEN @ v1 once approved. Changing a frozen contract = change request back to SPECIFY. -->
57
- <!-- The freeze IS the one approval. Lead it with the bundle's least-sure flag: the 1–2 points
58
- most likely wrong across the whole bundle, tagged [spec|scenario|contract|test], with why + cost.
59
- The §1 ⚠ assumptions are its first feeder; a flag may point at a scenario or the contract too. See run.md. -->
60
-
61
- <!-- EXIT: frozen + every spec rejection has a contracted response + names match GLOSSARY + the bundle's least-sure flag was surfaced at the freeze (or an honest "none material"). -->
78
+ Status: DRAFT
79
+ <!-- The freeze IS the one approval lead it with the bundle's lowest-confidence flag: the 1–2
80
+ points most likely wrong across the whole bundle, tagged [spec|scenario|contract|test], each
81
+ with why + cost (the §1 ⚠ assumptions feed it; a flag may point at a scenario or the contract
82
+ too — see run.md). Approved -> Status: FROZEN @ vN — approved by <name>. Changing a frozen
83
+ contract = change request back to SPECIFY.
84
+ EXIT: frozen + every spec rejection has a contracted response + names match GLOSSARY + the
85
+ bundle's lowest-confidence flag was surfaced at the freeze (or an honest "none material"). -->
62
86
 
63
87
  ---
64
88
 
65
- ## 4 · TESTS — red safety net ▸ docs/06-step-4-tests.md
89
+ ## 4 · TESTS — failing-first suite (red) ▸ docs/06-step-4-tests.md
66
90
 
67
91
  Coverage target: <e.g. 90%>
68
92
  Plan (one test per scenario, asserting behavior not internals):
93
+ <test_plan>
69
94
  - test_<scenario>: arrange <Given> / act <When> / assert <Then> + assert <unchanged>
95
+ </test_plan>
70
96
 
71
97
  Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
72
98
  <!-- declare paths as backticked tokens on this line: `./…` = this task dir ·
@@ -80,24 +106,38 @@ Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
80
106
 
81
107
  ## 5 · BUILD — AI writes code ▸ docs/07-step-5-build.md
82
108
 
109
+ Scope (may touch): `./src/` <fill before the §3 freeze — every file the build may write>
110
+ Strategy (ordered batches): <1. … 2. … — the planned build order; guidance, not enforced>
83
111
  Safety rule (feature-specific): <e.g. debit+credit in one atomic transaction>
84
112
  Code lives in: `./src/`
85
113
  Constraints: do NOT change any test or the contract; allow-list packages only; ask if unclear.
86
114
 
87
- <!-- EXIT: all green; coverage held; no test/contract touched; no unlisted dependency. -->
115
+ <!-- Scope tokens, backticked, FIRST declaring line: `./…` = this task dir · a token
116
+ with "/" = project root · a bare name = sibling of the previous token's dir ·
117
+ outside-root resolutions are dropped fail-closed · a DIRECTORY token covers its
118
+ whole subtree (containment — diverges from §4's non-recursive counting) ·
119
+ absent line = UNDECLARED (pre-existing tasks grandfathered, never retro-red) ·
120
+ engine enforcement (touched ⊆ declared) lands in scope-gate-enforce.
121
+ EXIT: all green; coverage held; no test/contract touched; no unlisted dependency. -->
88
122
 
89
123
  ---
90
124
 
91
- ## 6 · VERIFY — evidence + blind-spot checks ▸ docs/08-step-6-verify.md
125
+ ## 6 · VERIFY — evidence + non-functional review ▸ docs/08-step-6-verify.md
92
126
 
93
127
  - [ ] all tests pass
94
128
  - [ ] coverage did not decrease
95
129
  - [ ] no test or contract was altered during build
130
+ - [ ] the green was EARNED, not gamed — no overfit to fixtures, vacuous asserts, or stubbed-away logic (score with an adversarial refute-read — a subagent recommended under `autonomy: auto`; a confirmed cheat is HARD-STOP)
96
131
  - [ ] concurrency / timing of the risky operation is safe
97
132
  - [ ] no exposed secrets, injection openings, or unexpected dependencies
98
133
  - [ ] layering & dependencies follow CONVENTIONS.md
99
134
  - [ ] a person reviewed and approved the change
100
135
 
136
+ ### Deep checks — do not skim (fill the path that applies; the resolver judges which)
137
+ - [ ] WIRING (code) — every new symbol is referenced; record where / how confirmed
138
+ - [ ] DEAD-CODE (code) — no new unused or orphaned symbol introduced
139
+ - [ ] SEMANTIC (prose / non-code) — read in full, not skimmed: <what read · what confirmed>
140
+
101
141
  ### GATE RECORD
102
142
  Outcome: <PASS | RISK-ACCEPTED | HARD-STOP>
103
143
  If RISK-ACCEPTED -> owner: <name> · ticket: <link> · expires: <date> (never for a security gap)
@@ -0,0 +1,38 @@
1
+ {
2
+ "components": {
3
+ "Screen": {
4
+ "description": "The root viewport container.",
5
+ "hasChildren": true,
6
+ "props": {
7
+ "background": { "type": "token", "token": "color" },
8
+ "padding": { "type": "token", "token": "dimension" }
9
+ }
10
+ },
11
+ "Card": {
12
+ "description": "A surface that groups related content.",
13
+ "hasChildren": true,
14
+ "props": {
15
+ "background": { "type": "token", "token": "color" },
16
+ "gap": { "type": "token", "token": "dimension" },
17
+ "elevation": { "type": "enum", "values": ["none", "sm", "md"] }
18
+ }
19
+ },
20
+ "Text": {
21
+ "description": "A run of styled text.",
22
+ "props": {
23
+ "content": { "type": "string" },
24
+ "color": { "type": "token", "token": "color" },
25
+ "weight": { "type": "enum", "values": ["regular", "bold"] }
26
+ }
27
+ },
28
+ "Button": {
29
+ "description": "A primary call-to-action.",
30
+ "props": {
31
+ "label": { "type": "string" },
32
+ "variant": { "type": "enum", "values": ["primary", "secondary"] },
33
+ "background": { "type": "token", "token": "color" },
34
+ "disabled": { "type": "boolean" }
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "root": "screen",
3
+ "elements": {
4
+ "screen": {
5
+ "type": "Screen",
6
+ "props": {
7
+ "background": "{semantic.color.surface}",
8
+ "padding": "{semantic.space.inset-md}"
9
+ },
10
+ "children": ["welcome-card"]
11
+ },
12
+ "welcome-card": {
13
+ "type": "Card",
14
+ "props": {
15
+ "background": "{semantic.color.surface}",
16
+ "gap": "{semantic.space.inset-sm}",
17
+ "elevation": "sm"
18
+ },
19
+ "children": ["title", "subtitle", "cta"]
20
+ },
21
+ "title": {
22
+ "type": "Text",
23
+ "props": {
24
+ "content": "Welcome to ADD",
25
+ "color": "{semantic.color.text}",
26
+ "weight": "bold"
27
+ }
28
+ },
29
+ "subtitle": {
30
+ "type": "Text",
31
+ "props": {
32
+ "content": "Spec-and-tests-first, render-ready.",
33
+ "color": "{semantic.color.text}",
34
+ "weight": "regular"
35
+ }
36
+ },
37
+ "cta": {
38
+ "type": "Button",
39
+ "props": {
40
+ "label": "Get started",
41
+ "variant": "primary",
42
+ "background": "{semantic.color.accent}",
43
+ "disabled": false
44
+ },
45
+ "on": { "click": [{ "action": "navigate", "to": "onboarding" }] }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "primitive": {
3
+ "color": {
4
+ "$type": "color",
5
+ "blue-500": { "$value": "#3B82F6" },
6
+ "slate-900": { "$value": "#0F172A" },
7
+ "white": { "$value": "#FFFFFF" }
8
+ },
9
+ "space": {
10
+ "$type": "dimension",
11
+ "2": { "$value": "8px" },
12
+ "4": { "$value": "16px" }
13
+ },
14
+ "font": {
15
+ "family": {
16
+ "$type": "fontFamily",
17
+ "sans": { "$value": ["Inter", "system-ui", "sans-serif"] }
18
+ },
19
+ "weight": {
20
+ "$type": "fontWeight",
21
+ "regular": { "$value": 400 },
22
+ "bold": { "$value": 700 }
23
+ }
24
+ },
25
+ "motion": {
26
+ "$type": "duration",
27
+ "fast": { "$value": "150ms" }
28
+ }
29
+ },
30
+ "semantic": {
31
+ "color": {
32
+ "$type": "color",
33
+ "accent": { "$value": "{primitive.color.blue-500}" },
34
+ "text": { "$value": "{primitive.color.slate-900}" },
35
+ "surface": { "$value": "{primitive.color.white}" }
36
+ },
37
+ "space": {
38
+ "$type": "dimension",
39
+ "inset-sm": { "$value": "{primitive.space.2}" },
40
+ "inset-md": { "$value": "{primitive.space.4}" }
41
+ }
42
+ },
43
+ "component": {
44
+ "button": {
45
+ "bg": { "$type": "color", "$value": "{semantic.color.accent}" },
46
+ "label": { "$type": "color", "$value": "{semantic.color.surface}" },
47
+ "padding": { "$type": "dimension", "$value": "{semantic.space.inset-md}" }
48
+ },
49
+ "card": {
50
+ "bg": { "$type": "color", "$value": "{semantic.color.surface}" },
51
+ "text": { "$type": "color", "$value": "{semantic.color.text}" },
52
+ "gap": { "$type": "dimension", "$value": "{semantic.space.inset-sm}" }
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,122 @@
1
+ # UDD catalog + content trees — the render-ready half
2
+
3
+ The token foundation (`udd-tokens.md`) says what a UI is *made of*; this layer says
4
+ what it is *built from* and *laid out as*. A **catalog** declares the components and
5
+ their typed props; a **content tree** is a flat, json-render-shaped prototype that
6
+ binds those props to semantic tokens. `catalog.sample.json` + `prototype.sample.json`
7
+ are a worked pair that validates clean against each other.
8
+
9
+ ## The foundation is a NAMED SET (Fork A)
10
+
11
+ ```
12
+ .add/design/tokens.json the task-1 dialect (primitive · semantic · component)
13
+ .add/design/catalog.json the component catalog (this doc)
14
+ .add/design/prototypes/<name>.json one flat content tree per prototype screen/flow
15
+ ```
16
+
17
+ Each file lints independently — the token validator never sees the catalog, the
18
+ catalog/tree validator never sees the tokens. The cross-file check (a tree's
19
+ token-prop actually resolving to a semantic token of the right `$type`) is the
20
+ **composer's** job — `add.py check` wires it in the udd-check-lint task.
21
+
22
+ ## CATALOG — components and typed props
23
+
24
+ ```
25
+ { "components": { "<Name>": {
26
+ "description"?: str,
27
+ "hasChildren"?: bool (default false — only containers may hold children),
28
+ "props": { "<prop>": PropSpec } } } }
29
+ ```
30
+
31
+ A **PropSpec** is exactly one of:
32
+
33
+ | PropSpec | the tree value it accepts |
34
+ |----------|---------------------------|
35
+ | `{ "type": "string" }` | a JSON string |
36
+ | `{ "type": "number" }` | a JSON number (not a bool) |
37
+ | `{ "type": "boolean" }` | a JSON bool |
38
+ | `{ "type": "enum", "values": ["a","b"] }` | a string in `values` |
39
+ | `{ "type": "token", "token": "<$type>" }` | a `{semantic.…}` alias (see below) |
40
+
41
+ `<$type>` is a task-1 token type — `color · dimension · number · fontFamily ·
42
+ fontWeight · duration`.
43
+
44
+ ## CONTENT TREE — a flat json-render `Spec`
45
+
46
+ Pinned to **vercel-labs/json-render v0.19.0 / commit `4e4dc46`** (the milestone's
47
+ named top risk: young-project schema drift). The tree mirrors json-render's `Spec`
48
+ exactly, so it is render-ready as-is:
49
+
50
+ ```
51
+ { "root": "<id>",
52
+ "elements": { "<id>": {
53
+ "type": "<Name>", a catalog component name
54
+ "props": { … }, keys ⊆ the component's declared props
55
+ "children"?: ["<id>", …] only if the component hasChildren; ids ∈ elements
56
+ } } }
57
+ ```
58
+
59
+ - A `token` prop's value is the **alias form** `"{semantic.dotted.path}"` from the
60
+ token dialect — it MUST target the `semantic` layer. (Whether that semantic token
61
+ exists and matches the prop's `$type` is resolved by the composer, which has
62
+ `tokens.json`.)
63
+ - json-render's optional `state` / `on` / `visible` / `repeat` are **passed through**
64
+ — render-compatible (a clickable prototype) but NOT linted, keeping the validator
65
+ lean. Interactivity rules stay additive for a later task.
66
+
67
+ ## Validation — the named reds
68
+
69
+ `_catalog_tree_violations(catalog, tree)` returns `[]` for a valid pair, else one
70
+ `(code, path, detail)` per violation, in deterministic order. The eight codes:
71
+
72
+ | code | when |
73
+ |------|------|
74
+ | `tree_cites_uncataloged_component` | an element `type` is not in the catalog |
75
+ | `unknown_prop` | a props key not declared on the element's component |
76
+ | `prop_type_mismatch` | a value's form ≠ its PropSpec (incl. a token prop given a non-alias literal) |
77
+ | `non_semantic_prop_token` | a token-prop alias does not target the `semantic` layer |
78
+ | `dangling_child` | a child id absent from `elements` |
79
+ | `children_not_allowed` | children on a component whose `hasChildren` is false |
80
+ | `missing_root` | `root` absent, or names an id not in `elements` |
81
+ | `malformed_catalog` | a PropSpec with unknown `type`, or a token prop naming an unknown `$type` |
82
+
83
+ The validator lints **shape only** — pure, stdlib, tool-agnostic, no rendering. It is
84
+ SEPARATE from `_token_layer_violations`; udd-check-lint composes both inside
85
+ `add.py check`.
86
+
87
+ ## Render recipe — catalog.json → json-render
88
+
89
+ json-render authors a catalog in TypeScript (`defineCatalog`), not JSON, so our
90
+ `catalog.json` is adapted by a thin (~20-line) loader. The content tree needs no
91
+ adapter — it is a json-render `Spec` already:
92
+
93
+ ```ts
94
+ import { defineCatalog } from "json-render";
95
+ import { z } from "zod";
96
+ import catalogJson from "./catalog.json";
97
+ import spec from "./prototypes/welcome.json";
98
+
99
+ const PROP = { // PropSpec → a Zod field
100
+ string: () => z.string(),
101
+ number: () => z.number(),
102
+ boolean: () => z.boolean(),
103
+ enum: (p) => z.enum(p.values),
104
+ token: () => z.string(), // "{semantic.…}" — resolved to a CSS value at render
105
+ };
106
+
107
+ const components = Object.fromEntries(
108
+ Object.entries(catalogJson.components).map(([name, c]) => [name, {
109
+ hasChildren: c.hasChildren ?? false,
110
+ props: z.object(
111
+ Object.fromEntries(Object.entries(c.props).map(([k, p]) => [k, PROP[p.type](p)])),
112
+ ),
113
+ }]),
114
+ );
115
+
116
+ const catalog = defineCatalog(z, { components });
117
+ catalog.validate(spec); // the flat tree feeds json-render AS-IS
118
+ ```
119
+
120
+ Resolving a `{semantic.…}` alias to a concrete CSS value (via `tokens.json`) is the
121
+ renderer's binding step — out of scope for the foundation, which only guarantees the
122
+ SHAPE is render-ready. The renderer itself is never bundled with the method.
@@ -0,0 +1,79 @@
1
+ # UDD tokens — the compact-DTCG dialect
2
+
3
+ The token foundation a UI project drafts from. Three layers, a fail-closed
4
+ citation rule, and value forms compacted for the AI economy. Aligned with the
5
+ **Design Tokens Format Module 2025.10** (Final Community Group Report, 28 Oct 2025
6
+ — <https://www.designtokens.org/tr/2025.10/format/>); every divergence is NAMED
7
+ below. `tokens.sample.json` is a worked example that validates clean.
8
+
9
+ ## Identity values are human-owned — discuss at specify
10
+
11
+ The dialect checks a token's **shape**; its identity **value** — the brand
12
+ color, the palette seed, the typeface — is design *direction*, not an AI
13
+ default. During **specify**, surface identity tokens for discussion with the
14
+ user; never invent a brand value. Shape is verifiable; identity is a human
15
+ decision.
16
+
17
+ ## The three layers (a convention, not a DTCG feature)
18
+
19
+ A token's **layer is its top-level group name** — `primitive`, `semantic`, or
20
+ `component`. DTCG itself defines groups, `$type`, and aliases but no tier model;
21
+ the layering and the citation rule below are ours.
22
+
23
+ ```
24
+ primitive raw literal values (no alias — the source of truth)
25
+ semantic intent, cites primitives (e.g. color.accent → primitive.color.blue-500)
26
+ component parts, cite semantics (e.g. button.bg → semantic.color.accent)
27
+ ```
28
+
29
+ ## The citation rule (fail-closed)
30
+
31
+ - a `primitive` token's `$value` MUST be a literal — never an alias.
32
+ - a `semantic` token's `$value` MUST be a literal OR an alias to a `primitive` only.
33
+ - a `component` token's `$value` MUST be a literal OR an alias to a `semantic` only.
34
+
35
+ No layer-skipping, no sideways or upward citation. An **alias** is the DTCG curly
36
+ form `"{layer.dotted.path}"` used as a `$value`; chains are allowed (each hop is
37
+ checked); the chain must terminate in a literal.
38
+
39
+ ## Token shape (kept from DTCG)
40
+
41
+ - a **token** is an object with `$value` (required); optional `$type`,
42
+ `$description`.
43
+ - `$type` is optional on a token and **inherited** from the nearest parent group
44
+ that sets it.
45
+ - a **group** is any object without `$value`.
46
+
47
+ ## Value forms — supported `$type`s (NAMED divergences from DTCG 2025.10)
48
+
49
+ | `$type` | compact form (ours) | DTCG 2025.10 form |
50
+ |--------------|----------------------------------------|--------------------------|
51
+ | `color` | `"#RRGGBB"` / `"#RRGGBBAA"` | object `{colorSpace,…}` ⟵ DIVERGES |
52
+ | `dimension` | `"<n><unit>"`, unit ∈ px·rem·em·%·vh·vw | object `{value,unit}` ⟵ DIVERGES |
53
+ | `number` | JSON number | JSON number |
54
+ | `fontWeight` | `100`–`900` or a keyword string | number / keyword |
55
+ | `duration` | `"<n>ms"` / `"<n>s"` | object `{value,unit}` ⟵ DIVERGES |
56
+ | `fontFamily` | string or array of strings | string / array |
57
+
58
+ Composites (`shadow`, `typography`, `border`) and the rarer DTCG types
59
+ (`cubicBezier`, `strokeStyle`, `gradient`, `transition`) are **deferred** —
60
+ additive later without breaking this dialect.
61
+
62
+ ## Validation — the named reds
63
+
64
+ `_token_layer_violations(tokens)` returns `[]` for a valid file, else one
65
+ `(code, path, detail)` per violation. The six codes:
66
+
67
+ | code | when |
68
+ |------|------|
69
+ | `unknown_layer` | a top-level group is not primitive/semantic/component |
70
+ | `unknown_type` | a token's resolved `$type` is outside the supported set |
71
+ | `unresolved_alias` | `{a.b.c}` points at no token bearing a `$value` |
72
+ | `cross_layer_citation` | an alias skips or inverts a layer |
73
+ | `primitive_has_alias` | a primitive token's `$value` is an alias, not a literal |
74
+ | `malformed_value` | a literal `$value` does not match the form for its `$type` |
75
+
76
+ The validator lints **shape only** — stdlib, tool-agnostic, no rendering. The
77
+ `add.py check` wiring (named reds) and the catalog/prototype-tree rules arrive in
78
+ later udd-design-foundation tasks. To render: point a json-render-style renderer
79
+ at the component layer (see the milestone's render recipe).