@pilotspace/add 1.8.0 → 1.10.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 (38) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/bin/cli.js +137 -2
  3. package/docs/16-releasing.md +2 -2
  4. package/docs/17-components.md +125 -0
  5. package/docs/appendix-c-glossary.md +8 -0
  6. package/package.json +2 -2
  7. package/skill/add/SKILL.md +90 -116
  8. package/skill/add/adopt.md +13 -34
  9. package/skill/add/advisor.md +9 -10
  10. package/skill/add/compact-foundation.md +21 -34
  11. package/skill/add/components.md +54 -0
  12. package/skill/add/confidence.md +9 -25
  13. package/skill/add/deltas.md +12 -28
  14. package/skill/add/design.md +26 -32
  15. package/skill/add/fold.md +26 -51
  16. package/skill/add/graduate.md +18 -54
  17. package/skill/add/intake.md +30 -34
  18. package/skill/add/loop.md +30 -34
  19. package/skill/add/phases/0-ground.md +6 -14
  20. package/skill/add/phases/0-setup.md +30 -81
  21. package/skill/add/phases/1-specify.md +7 -22
  22. package/skill/add/phases/2-scenarios.md +1 -3
  23. package/skill/add/phases/3-contract.md +8 -22
  24. package/skill/add/phases/4-tests.md +3 -6
  25. package/skill/add/phases/5-build.md +8 -22
  26. package/skill/add/phases/6-verify.md +14 -49
  27. package/skill/add/phases/7-observe.md +1 -3
  28. package/skill/add/phases/fast-lane.md +31 -0
  29. package/skill/add/release.md +36 -90
  30. package/skill/add/report-template.md +41 -126
  31. package/skill/add/run.md +89 -159
  32. package/skill/add/scope.md +40 -89
  33. package/skill/add/setup-review.md +10 -27
  34. package/skill/add/soul.md +17 -36
  35. package/skill/add/streams.md +90 -149
  36. package/tooling/add.py +804 -14
  37. package/tooling/templates/TASK.fast.md.tmpl +72 -0
  38. package/tooling/templates/TASK.md.tmpl +1 -1
@@ -1,16 +1,12 @@
1
1
  # Lessons learned — how each loop sharpens the foundation
2
2
 
3
- A **lesson learned** is a single learning a task produces, tagged by which of ADD's five
4
- competencies it improves. You write deltas in a task's **OBSERVE** phase; later, the
5
- `foundation-update-loop` gathers the confirmed ones and consolidates them into a versioned `PROJECT.md`.
6
- This is how `DDD · SDD · UDD · TDD · ADD` stop being write-once and start converging.
3
+ A **lesson learned** is a single learning a task produces, tagged by which of ADD's five competencies it improves. Write deltas in a task's **OBSERVE** phase; later, `fold.md` gathers confirmed ones and consolidates them into a versioned `PROJECT.md`. This is how `DDD · SDD · UDD · TDD · ADD` stop being write-once and start converging.
7
4
 
8
- You (the AI) **emit** deltas as `open`. Only the **human** moves a delta to `folded` or `rejected`
9
- (consolidating into the foundation is judgment — see the verify/observe decision point). You never self-approve a consolidation.
5
+ You **emit** deltas as `open`. Only the **human** moves a delta to `folded` or `rejected`. You never self-approve a consolidation.
10
6
 
11
7
  ## The grammar (frozen)
12
8
 
13
- Each delta begins on its own **tag line**; the learning may wrap onto continuation lines:
9
+ Each delta begins on its own **tag line**; the learning may wrap:
14
10
 
15
11
  ```
16
12
  - [<COMPETENCY> · <status>] <learning> (evidence: <pointer>)
@@ -18,14 +14,10 @@ Each delta begins on its own **tag line**; the learning may wrap onto continuati
18
14
 
19
15
  - `<COMPETENCY>` — exactly one of the five (below).
20
16
  - `<status>` — `open` | `folded` | `rejected`. A **newly emitted delta is `open`**.
21
- - `<learning>` — the insight ("the domain model missed multi-tenancy"). It may run past one line;
22
- the `- [COMPETENCY · status]` tag line must come **first**, and the `(evidence: …)` clause must
23
- **close** the delta (on its last line).
24
- - `(evidence: …)` — **required**, non-empty: a failing scenario, a production signal, a review
25
- note. No evidence → it is an opinion, not a delta.
17
+ - `<learning>` — the insight. The `- [COMPETENCY · status]` tag line comes **first**; `(evidence: …)` **closes** the delta.
18
+ - `(evidence: …)` **required**, non-empty: a failing scenario, a production signal, a review note. No evidence it is an opinion, not a delta.
26
19
 
27
- A long learning may wrap — the lint (`add.py check`) joins continuation lines, so this is **one**
28
- delta, not two:
20
+ A long learning may wrap — `add.py check` joins continuation lines, so this is **one** delta:
29
21
 
30
22
  ```
31
23
  - [SDD · open] the export endpoint must reject a tenant-scoped token used cross-tenant,
@@ -42,23 +34,19 @@ delta, not two:
42
34
  | `TDD` | Test | how we prove correctness — a missing scenario, a flaky or hollow test |
43
35
  | `ADD` | AI/build | how the AI builds — a harness, prompt, or convention that helped or hurt |
44
36
 
45
- If a learning seems to touch two, ask "which competency, once updated, would have PREVENTED this?"
46
- That is its home. Split genuinely separate learnings into separate deltas; never tag one twice.
37
+ If a learning touches two, ask "which competency, once updated, would have PREVENTED this?" — that is its home. Split separate learnings into separate deltas; never tag one twice.
47
38
 
48
39
  ## Status lifecycle
49
40
 
50
41
  ```
51
42
  emit (OBSERVE) human review (foundation-update-loop)
52
- open ───────────▶ folded (the learning is merged into PROJECT.md; version bumps)
53
- └──────────▶ rejected (considered and deliberately NOT consolidated — the trail is kept)
43
+ open ───────────▶ folded (learning merged into PROJECT.md; version bumps)
44
+ └──────────▶ rejected (considered and deliberately NOT consolidated — trail kept)
54
45
  ```
55
46
 
56
- An `open` delta is a pending signal. `folded` and `rejected` are both human decisions; a `rejected`
57
- delta is left in place (not deleted) so "we saw this and chose not to act" stays auditable.
47
+ A `rejected` delta is left in place (not deleted) so "we saw this and chose not to act" stays auditable.
58
48
 
59
- ## Reject codes (well-formedness — you are the first check, the human is the backstop)
60
-
61
- There is no engine validator yet, so before you record a delta, self-check it:
49
+ ## Reject codes
62
50
 
63
51
  <reject_codes>
64
52
  - `unknown_competency` — the tag is missing or not one of `DDD · SDD · UDD · TDD · ADD`. Fix the tag.
@@ -68,14 +56,10 @@ There is no engine validator yet, so before you record a delta, self-check it:
68
56
 
69
57
  ## Worked example
70
58
 
71
- A task that built a tenancy feature finished its OBSERVE phase with:
72
-
73
59
  ```
74
60
  - [DDD · open] the account model conflated org and workspace (evidence: scenario_cross_tenant_read failed)
75
61
  - [TDD · open] no scenario covered a deleted tenant's dangling sessions (evidence: review note, PR thread)
76
62
  - [ADD · open] the scaffold's allow-list missed the tenancy lib, slowing build (evidence: build log retry)
77
63
  ```
78
64
 
79
- Three learnings, three competencies, each with a pointer. At the next foundation update the human
80
- consolidated the DDD and TDD deltas into `PROJECT.md` (→ `folded`) and rejected the ADD one as a one-off
81
- (→ `rejected`). The foundation got sharper; nothing was silently lost.
65
+ At the next foundation update the human consolidated the DDD and TDD deltas (→ `folded`) and rejected the ADD one (→ `rejected`). The foundation got sharper; nothing was silently lost.
@@ -2,14 +2,13 @@
2
2
 
3
3
  When a **UI feature** reaches specify, design it before you build it. This loop takes the
4
4
  feature from the **domain** to a screen the human has **seen and confirmed** — a real captured
5
- image — *before* any implementation, so the build matches the expected layout instead of
6
- discovering it. It is loaded on demand (like `advisor.md` / `confidence.md`); the engine never
7
- runs it for you.
5
+ image — *before* any implementation. It is loaded on demand (like `advisor.md` / `confidence.md`);
6
+ the engine never runs it for you.
8
7
 
9
8
  Design before code is the UDD half of the method. The token + component foundation a UI project
10
9
  draws from already exists — `tokens.json` (the compact-DTCG dialect, `udd-tokens.md`),
11
- `catalog.json` + `prototypes/<name>.json` (`udd-catalog.md`). This loop is how you *fill* that
12
- foundation for a feature and earn the human's sign-off on the look before build.
10
+ `catalog.json` + `prototypes/<name>.json` (`udd-catalog.md`). This loop fills that foundation
11
+ for a feature and earns the human's sign-off on the look before build.
13
12
 
14
13
  ## The loop — four beats
15
14
 
@@ -20,52 +19,47 @@ review-domain → research-components → wireframe → render-capture-con
20
19
  Run the beats in order. Each feeds the next; the last ends at a human design-confirm.
21
20
 
22
21
  ### 1 · review-domain
23
- Start from the **domain**, not from a blank canvas. Read the domain model — entities, flows, the
22
+ Start from the **domain**, not a blank canvas. Read the domain model — entities, flows, the
24
23
  ubiquitous language in `PROJECT.md` / `GLOSSARY.md` — and derive **which screens** the feature
25
24
  needs and the **regions** each screen holds. Map each domain entity to a *presentational*
26
25
  component (it shows state; it does not own a domain decision). The screen list + per-screen
27
- regions are this beat's output, and they are grounded in the domain you just read.
26
+ regions are this beat's output, grounded in the domain you just read.
28
27
 
29
28
  ### 2 · research-components (reuse before you invent)
30
- Check `catalog.json` **first** and **reuse** the components already there — that is what keeps
31
- screens consistent. Research a reference UI (a websearch over pattern galleries / component
32
- libraries for this kind of screen) only to fill a **genuine gap**, and propose a **new** catalog
29
+ Check `catalog.json` **first** and **reuse** the components already there — that keeps screens
30
+ consistent. Research a reference UI only to fill a **genuine gap**, and propose a **new** catalog
33
31
  component for that gap with a **cited** reference. Reuse before invention; a new component is the
34
- exception, named and justified, not the default.
32
+ exception, not the default.
35
33
 
36
34
  ### 3 · wireframe
37
35
  Draw a **low-fi**, **structural** layout for each screen — regions and component slots, no
38
- styling, no color. This is the cheapest possible artifact that shows the *expected layout*, so
39
- the human can correct the structure before a pixel is styled. Confirm the wireframe, then move on.
36
+ styling, no color. This is the cheapest artifact that shows the *expected layout*, so the human
37
+ can correct structure before a pixel is styled. Confirm the wireframe, then move on.
40
38
 
41
39
  ### 4 · render-capture-confirm
42
40
  Render the screen as a **self-contained HTML mock** — the project's component library via CDN,
43
41
  bound to `tokens.json` (resolved to CSS variables), composed from the **reusable per-component
44
42
  kit** (one token-bound partial per catalog component), populated with realistic **mock** data.
45
- **Capture** a real image of it (a headless screenshot) and present that image to the human for
43
+ **Capture** a real image (a headless screenshot) and present that image to the human for
46
44
  **design-confirm** — show-before-ask, **before build**. On confirm, record the layout back to
47
45
  `prototypes/<name>.json` + `catalog.json`, save the captured image to
48
- `.add/design/captures/<name>.<ext>`, and **attach or mention it in the feature's `TASK.md`**
49
- (alongside the §6 evidence) — so the screen the human approved is traceable from the task that
50
- builds it. The HTML mock is the *visible evidence*; the json-render tree is the
51
- *machine-checkable* record of the same screen.
46
+ `.add/design/captures/<name>.<ext>`, and **attach or mention it in the feature's `TASK.md`**
47
+ so the screen the human approved is traceable from the task that builds it. The HTML mock is the
48
+ *visible evidence*; the json-render tree is the *machine-checkable* record.
52
49
 
53
50
  ## Tool-agnostic capture
54
51
 
55
- How you render and capture is **your** choice, not the engine's: a headless browser
56
- (Playwright / Puppeteer), an `html2image`-style renderer, a browser-automation skill, a design
57
- tool, or a hosted screenshot service — whatever the agent has. The recommended default is the
58
- self-contained HTML mock above, captured headless, because it needs no app build yet still wears
59
- the project's real tokens and component vocabulary. For a project that renders json-render, the
60
- recommended default is **`@json-render/image`** (Satori → PNG/SVG, no browser) a deterministic
61
- `Spec` image. The captured image is a **design-confirm evidence** artifact the human approves;
62
- it is never an engine output, and the engine never renders. This keeps the loop tool-agnostic and
63
- the method renderer-free.
64
-
65
- Captures live at **`.add/design/captures/<name>.<ext>`** (one per prototype) and are
66
- attached/mentioned in the feature's `TASK.md`. `add.py check` raises a never-red
67
- `missing_capture` WARN for any prototype under `.add/design/prototypes/` that still lacks a
68
- capture — a nudge to render + confirm it, never a blocker.
52
+ How you render and capture is **your** choice: a headless browser (Playwright / Puppeteer), an
53
+ `html2image`-style renderer, a browser-automation skill, a design tool, or a hosted screenshot
54
+ service. The recommended default is the self-contained HTML mock above, captured headless,
55
+ because it needs no app build yet still wears the project's real tokens and component vocabulary.
56
+ For a project that renders json-render, the recommended default is **`@json-render/image`**
57
+ (Satori → PNG/SVG, no browser). The captured image is **design-confirm evidence** the human
58
+ approves; the engine never renders. This keeps the loop tool-agnostic and the method renderer-free.
59
+
60
+ Captures live at **`.add/design/captures/<name>.<ext>`** and are attached/mentioned in the
61
+ feature's `TASK.md`. `add.py check` raises a never-red `missing_capture` WARN for any prototype
62
+ under `.add/design/prototypes/` that still lacks a capture — a nudge to render + confirm it.
69
63
 
70
64
  The loop **binds** the existing UDD contracts **read-only**: `tokens.json`, `catalog.json`, and
71
65
  `prototypes/<name>.json` are read and composed, never reshaped — the `prototypes/<name>.json`
package/skill/add/fold.md CHANGED
@@ -1,86 +1,61 @@
1
1
  # Consolidating lessons — how the foundation self-improves
2
2
 
3
- This **closes the loop**. `deltas.md` lets a task EMIT lessons (`open` lessons learned in its
4
- OBSERVE phase); the retrospective consolidation gathers the confirmed ones and writes them into a
5
- **versioned foundation**, so `DDD · SDD · UDD · TDD · ADD` sharpen across milestones instead of drifting.
3
+ This **closes the loop**. `deltas.md` lets a task EMIT lessons (`open` lessons learned in OBSERVE); retrospective consolidation gathers the confirmed ones and writes them into a **versioned foundation**, so `DDD · SDD · UDD · TDD · ADD` sharpen across milestones.
6
4
 
7
- Since v3 this is mechanized by a command`add.py fold`but the command stays **judgment-free**:
8
- it only ever TRANSCRIBES each lesson's OWN captured text into its routed home and bumps the version.
9
- It NEVER composes or merges prose, and it never **self-approves** a consolidation — running the
10
- command records the human's confirmation, it does not replace their judgment. Deciding WHICH lessons
11
- to keep, and later polishing the raw transcribed bullets into lean one-screen prose, remain the
12
- human's work — the latter via the **compaction door** (`compact-foundation.md`).
5
+ `add.py fold` is **judgment-free**: it only TRANSCRIBES each lesson's own captured text into its routed home and bumps the version. It NEVER composes or merges prose, and it **never self-approves** a consolidationrunning the command records the human's confirmation. Deciding WHICH lessons to keep, and polishing raw transcribed bullets into lean one-screen prose, remain the human's work the latter via the **compaction door** (`compact-foundation.md`).
13
6
 
14
7
  ## When to consolidate
15
8
 
16
- At **milestone close** (the natural "version bump to the foundation"), or **on demand** when open
17
- lessons have piled up. One run of `add.py fold` is ONE consolidation session: it bumps
18
- `foundation-version` exactly once and stamps every lesson it resolves with that one new version.
9
+ At **milestone close** (the natural version bump), or **on demand** when open lessons pile up. One run of `add.py fold` = ONE consolidation session: bumps `foundation-version` exactly once and stamps every resolved lesson with that version.
19
10
 
20
11
  ## The ritual
21
12
 
22
- 1. **Gather** — `add.py deltas` reads every task's OBSERVE block for lessons still `open` (by the machine heading).
23
- 2. **Confirm** — decide which to keep; a lesson you do NOT want is marked `rejected` and left in place
24
- (see below). Running `add.py fold` over the rest IS your confirmation — the command needs no separate flag.
13
+ 1. **Gather** — `add.py deltas` reads every task's OBSERVE block for lessons still `open`.
14
+ 2. **Confirm** — decide which to keep; a lesson you do NOT want is marked `rejected` and left in place. Running `add.py fold` over the rest IS your confirmation.
25
15
  3. **Write** — `add.py fold [--task <slug>] [--comp <TAG>]` performs the mechanical write atomically:
26
16
  - flips each selected `open` lesson to `folded` and stamps it `[folded foundation-version N]`;
27
- - transcribes each lesson VERBATIM as one bullet at the TOP (newest-first) of its routed section (below);
17
+ - transcribes each lesson VERBATIM as one bullet at the TOP (newest-first) of its routed section;
28
18
  - prepends one row to §Key Decisions (date · what · why · outcome);
29
19
  - bumps `foundation-version` by one.
30
- Validate-all-then-write: if any precondition fails the command writes NOTHING (the tree stays byte-identical).
31
- 4. **Propose & polish** — the transcribed bullets are RAW; afterward you consolidate/merge them into
32
- lean prose (append-only, newest-first) via the compaction door. The engine never does this editorial step.
20
+ Validate-all-then-write: if any precondition fails the command writes NOTHING.
21
+ 4. **Propose & polish** — transcribed bullets are RAW; afterward consolidate/merge into lean prose (append-only, newest-first) via the compaction door.
33
22
 
34
- ## Consolidation routing (every competency has a home)
23
+ ## Consolidation routing
35
24
 
36
25
  | competency | consolidates into | how |
37
26
  |------------|-----------|-----|
38
- | `DDD` | `PROJECT.md` §Domain | a transcribed bullet at the top |
39
- | `SDD` | `PROJECT.md` §Spec | a transcribed bullet at the top |
40
- | `UDD` | `PROJECT.md` §Users | a transcribed bullet at the top |
41
- | `TDD` | `CONVENTIONS.md` §Method learnings | a transcribed bullet (no PROJECT.md section — it is the engine) |
42
- | `ADD` | `CONVENTIONS.md` §Method learnings | a transcribed bullet (likewise the engine) |
27
+ | `DDD` | `PROJECT.md` §Domain | transcribed bullet at the top (newest-first) |
28
+ | `SDD` | `PROJECT.md` §Spec | transcribed bullet at the top |
29
+ | `UDD` | `PROJECT.md` §Users | transcribed bullet at the top |
30
+ | `TDD` | `CONVENTIONS.md` §Method learnings | transcribed bullet |
31
+ | `ADD` | `CONVENTIONS.md` §Method learnings | transcribed bullet |
43
32
 
44
- **Every** consolidation — whatever the competency — ALSO prepends one row at the TOP of `PROJECT.md`
45
- **§Key Decisions** (newest-first) (date · decision · why · outcome): the universal, auditable trail of
46
- what the foundation learned.
33
+ Every consolidation ALSO prepends one row at the TOP of `PROJECT.md` §Key Decisions: date · decision · why · outcome.
47
34
 
48
35
  ## Status transitions & version
49
36
 
50
- - on **confirm**: the lesson moves `open` → `folded` (its text transcribed at the top of the routed target, newest-first).
51
- - on **decline**: the lesson moves `open` → `rejected` and is **left in place**never deleted
52
- so "we considered this and chose not to act" stays auditable.
53
- - a consolidation is **append-only (newest-first)**: it PREPENDS new bullets/rows at the top and never
54
- silently rewrites existing foundation text — EXCEPT via the recorded **compaction door** (`compact-foundation.md`):
55
- eligible (shipped + zero open residues) stable entries collapse upward into a rolled-up settled line at the tail.
56
- - each `add.py fold` run **bumps** the `foundation-version:` marker in `PROJECT.md` by one (monotonic int).
37
+ - **confirm**: `open` → `folded` (text transcribed at top of routed target, newest-first).
38
+ - **decline**: `open` → `rejected`, left in place — "we considered and chose not to act" stays auditable.
39
+ - Consolidation is **append-only (newest-first)**: PREPENDS new bullets/rows, never silently rewrites — EXCEPT via the recorded compaction door.
40
+ - Each `add.py fold` run **bumps** `foundation-version:` in `PROJECT.md` by one (monotonic int).
57
41
 
58
- ## Reject codes (the command is fail-closed; validate-all-then-write)
42
+ ## Reject codes
59
43
 
60
44
  <reject_codes>
61
- - `no_open_deltas` — nothing is `open` in the selected scope. The session is a no-op; the version is NOT bumped.
62
- - `missing_route_section` — a lesson routes to a foundation section that does not exist (e.g. a `UDD`
63
- lesson with no `## Users` section). Fail-closed: add the section header, then re-run. Nothing is written.
45
+ - `no_open_deltas` — nothing is `open` in the selected scope. Version is NOT bumped.
46
+ - `missing_route_section` — a lesson routes to a foundation section that does not exist. Add the section header, then re-run. Nothing is written.
64
47
  - `no_foundation_version` — `PROJECT.md` carries no parseable `foundation-version:` marker to bump.
65
48
  </reject_codes>
66
49
 
67
- The convention-era codes `unconfirmed_fold` and `unroutable_delta` are **retired** (bridged here, not
68
- silently dropped): invoking `add.py fold` IS the confirmation, so `unconfirmed_fold` no longer applies;
69
- and routing is total over the five competencies, so a missing destination surfaces as the more precise
70
- `missing_route_section` rather than `unroutable_delta`.
50
+ Convention-era codes `unconfirmed_fold` and `unroutable_delta` are **retired**: invoking `add.py fold` IS the confirmation; a missing destination surfaces as `missing_route_section`.
71
51
 
72
- ## Worked example (from this repo's own history)
52
+ ## Worked example
73
53
 
74
- The `competency-deltas` task closed its OBSERVE with two lessons — the homeless ones, `TDD`/`ADD`,
75
- which have no PROJECT.md section:
54
+ The `competency-deltas` task closed its OBSERVE with two lessons:
76
55
 
77
56
  ```
78
57
  - [ADD · open] dogfood .add/tooling template can silently diverge from canonical (evidence: md5 mismatch this build)
79
58
  - [TDD · open] structural tests guard canonical artifacts but not their dogfood twins (evidence: scope-loop note + this build)
80
59
  ```
81
60
 
82
- At the next consolidation the human keeps both and runs `add.py fold`. Routing transcribes each into
83
- `CONVENTIONS.md` §Method learnings, prepends a §Key Decisions row, flips them to `folded` with a
84
- `[folded foundation-version N]` stamp, and bumps `foundation-version` 1 → 2 — all in one atomic write.
85
- The two competencies the foundation never tracked before now have a home — which is exactly why v5
86
- routes `TDD`/`ADD` to `CONVENTIONS.md`.
61
+ The human keeps both and runs `add.py fold`. Routing transcribes each into `CONVENTIONS.md` §Method learnings, prepends a §Key Decisions row, flips them to `folded` with `[folded foundation-version N]`, and bumps `foundation-version` 1 → 2 — all in one atomic write.
@@ -1,75 +1,39 @@
1
1
  # Stage graduation — propose the move to production as a roadmap, never a flip
2
2
 
3
- A project does not become "production" because someone typed a new label. It graduates when
4
- the MVP is genuinely covered AND a human-confirmed roadmap of production work exists. This guide
5
- is the **4th scope level** — after setup (`phases/0-setup.md`), intake (`intake.md` / `scope.md`),
6
- and the milestone loop (`loop.md`). It turns the bare `add.py stage` flip into the **final step** of
7
- an analytics-driven, interview-led orchestration.
3
+ A project graduates when the MVP is genuinely covered AND a human-confirmed roadmap of production work exists. This is the **4th scope level** — after setup, intake/scope, and the milestone loop. It turns the bare `add.py stage` flip into the **final step** of an analytics-driven, interview-led orchestration.
8
4
 
9
- You (the AI) **gather and propose**; the **human confirms and judges**; the engine only counts
10
- tallies and enforces the floor. The engine never decides that the project is "ready" — that is
11
- judgment, and it belongs to the interview.
5
+ You **gather and propose**; the **human confirms and judges**; the engine only counts tallies and enforces the floor. The engine never decides "ready" — that is judgment, and it belongs to the interview.
12
6
 
13
- ## The cue (what starts this)
7
+ ## The cue
14
8
 
15
- When every milestone is `done` AND the human's stage-goal-criteria in `PROJECT.md` are all `[x]`,
16
- `add.py status` prints:
9
+ When every milestone is `done` AND the human's stage-goal-criteria in `PROJECT.md` are all `[x]`, `add.py status` prints:
17
10
 
18
11
  ```
19
12
  → MVP covered → propose graduation
20
13
  ```
21
14
 
22
- That line is the trigger. Before both tallies complete, status is silent and nothing here applies
23
- (a project with no stage-goal-criteria block behaves exactly as today — grandfathered, zero change).
15
+ Before both tallies complete, status is silent. A project with no stage-goal-criteria block is grandfathered — zero change.
24
16
 
25
17
  ## The flow
26
18
 
27
- 1. **Gather the analytics** — run `add.py graduation-report` (add `--json` to branch on it). It
28
- clusters the whole MVP loop's evidence into five labeled record-sets: open deltas by competency ·
29
- open RISK-ACCEPTED waivers by expiry · RETRO records · verify residue · observe-loop coverage gaps.
30
- It **gathers, never judges** — there is no readiness verdict to read; the records are what you
31
- reason from.
32
- 2. **Co-specify interview** — synthesize *"what production means HERE"* WITH the human, using the
33
- gathered records as the agenda (the residue to harden, the coverage gaps to monitor, the open
34
- deltas to consolidate). This synthesis is the judgment the engine refuses to make. Interview to real confidence —
35
- do not guess what "production-ready" means for this project.
36
- 3. **Draft the roadmap** — for each production outcome the interview surfaces, draft a production
37
- milestone with the EXISTING command and goal-gate criteria:
38
- `add.py new-milestone <slug> --stage production --goal "…"`, then write its exit criteria. The
39
- roadmap is **≥1** milestone — the hardening work itself (SLOs, rollback tests, incident runbooks)
40
- is what these milestones *contain*; this guide proposes them, it does not do them.
41
- 4. **Human confirms** — present the roadmap via `report-template.md`, opening with the ARC
42
- (goal · done · plan): the stage-graduation goal, the MVP coverage that earns the move, and the
43
- plan the production milestones lay out. Render the roadmap as a guided choice — the recommended path
44
- + its described alternatives (per `report-template.md`). The human accepts, edits, or declines each drafted
45
- milestone. No milestone is created without this; nothing advances on a draft the human has not confirmed.
46
- 5. **Flip — the final step** — only now run `add.py stage production`. Because ≥1 production milestone
47
- now exists, the guard passes and the transition is recorded. This is the orchestration's last act.
19
+ 1. **Gather** — run `add.py graduation-report` (`--json` to branch on it). Clusters the MVP loop's evidence: open deltas by competency · open RISK-ACCEPTED waivers · RETRO records · verify residue · observe-loop coverage gaps. Gathers, never judges.
20
+ 2. **Co-specify interview** — synthesize *"what production means HERE"* WITH the human, using the gathered records as the agenda. Interview to real confidence — do not guess what "production-ready" means for this project.
21
+ 3. **Draft the roadmap** for each production outcome the interview surfaces, draft a production milestone: `add.py new-milestone <slug> --stage production --goal "…"`, then write its exit criteria. The roadmap is **≥1** milestone containing the hardening work (SLOs, rollback tests, incident runbooks).
22
+ 4. **Human confirms** — present the roadmap via `report-template.md`, opening with the ARC (goal · done · plan). Render as a guided choice (per `report-template.md`). Nothing advances on an unconfirmed draft.
23
+ 5. **Flip — the final step** — only now run `add.py stage production`. Because ≥1 production milestone now exists, the guard passes and the transition is recorded.
48
24
 
49
- ## The floor (what the engine enforces)
25
+ ## The floor
50
26
 
51
- `add.py stage production` is **guarded**: it refuses with `stage_no_roadmap` (non-zero exit, state
52
- byte-unchanged) when zero milestones have `stage: production`. The check is a **tally** — "does a
53
- production-roadmap record exist?" — never a readiness judgment (gather-not-judge at stage level;
54
- it mirrors the milestone goal-gate's `milestone_goal_unmet`). `--force` overrides it, preserving human
55
- authority for grandfathered/edge cases; use it deliberately, not as the normal path.
27
+ `add.py stage production` is **guarded**: refuses with `stage_no_roadmap` when zero milestones have `stage: production`. The check is a tally, never a readiness judgment. `--force` overrides for grandfathered/edge cases.
56
28
 
57
- Scope: the guard is on the `→production` **transition** only. Flips to prototype/poc/mvp are the
58
- existing bare flip, unchanged. `add.py init --stage production` is an explicit at-creation declaration
59
- (the same authority as `--force`), not a transition — it is out of scope of the guard by design.
29
+ Guard is on the `→production` transition only. Flips to prototype/poc/mvp are unchanged. `add.py init --stage production` is an explicit at-creation declaration — out of scope of the guard.
60
30
 
61
- ## Invariants (never break these)
31
+ ## Invariants
62
32
 
63
- - **The flip is the final step**, never called outside this confirmed-roadmap path. A bare flip with
64
- no roadmap is the symptom this scope level removes.
65
- - **The engine never auto-flips.** Every step here is human-confirmed; the engine gathers, counts, and
66
- enforces the floor — it does not advance the stage on its own.
67
- - **The flow is continuous, not cue-reentrant.** The moment you draft the first production milestone,
68
- `status` stops printing the cue (the "every milestone done" tally breaks). That is expected — do NOT
69
- re-await the cue after drafting; carry the flow straight through to confirm and flip.
33
+ - **The flip is the final step**, never called outside this confirmed-roadmap path.
34
+ - **No auto-flip.** Every step is human-confirmed; the engine gathers, counts, and enforces the floor — it does not advance the stage on its own.
35
+ - **The flow is continuous, not cue-reentrant.** Once you draft the first production milestone, `status` stops printing the cue. Do NOT re-await the cue; carry straight through to confirm and flip.
70
36
 
71
37
  ## Depth and reuse
72
38
 
73
- The same orchestration serves prototype→poc and poc→mvp; **mvp→production** is the rigorous proof
74
- case (every step at full depth + the observe loop). At lower stages, run it light — the shape is the
75
- same, the depth is less.
39
+ The same orchestration serves prototype→poc and poc→mvp; **mvp→production** is the rigorous proof case. At lower stages, run it light — same shape, less depth.
@@ -1,18 +1,16 @@
1
1
  # Intake — size a request into versioned scope
2
2
 
3
- Before a task exists, ADD turns a raw request into correctly-sized, versioned scope.
4
- This is the **intake level**: the per-task flow is phases 0–7; intake is the step
5
- *before* a task — request → milestone or task. You (the AI) **propose**; the human
6
- **confirms**. Never create scope without a confirmed proposal.
3
+ Before a task exists, ADD turns a raw request into correctly-sized, versioned scope — the
4
+ **intake level** (the per-task flow is phases 0–7; intake is the step *before* a task). You
5
+ (the AI) **propose**; the human **confirms**. Never create scope without a confirmed proposal.
7
6
 
8
7
  ## Interview before you size
9
8
 
10
- When the request arrives as a question, or its intent is not yet sharp enough to
11
- place in one bucket: explore it WITH the user before classifying. Reflect the
12
- intent you heard, name what seems in and out of scope, and offer 2–3 sized options
13
- with your own recommendation. Only then emit `{ bucket, rationale, command }`.
14
- `ask_human` stays the floor: when interviewing cannot sharpen the request,
15
- reject — never guess a bucket.
9
+ When the request arrives as a question, or its intent is not sharp enough to place in one bucket:
10
+ explore it WITH the user before classifying. Reflect the intent you heard, name what seems in and
11
+ out of scope, and offer 2–3 sized options with your own recommendation. Only then emit
12
+ `{ bucket, rationale, command }`. `ask_human` stays the floor: when interviewing cannot sharpen the
13
+ request, reject never guess a bucket.
16
14
 
17
15
  ## The four buckets
18
16
 
@@ -25,40 +23,38 @@ Classify every request into exactly ONE bucket:
25
23
  | `task` | fits within the ACTIVE milestone's stated scope | `add.py new-task <slug>` |
26
24
  | `change-request` | modifies ALREADY-FROZEN scope (a frozen contract or a shipped promise) | `add.py phase specify\|contract <affected>` |
27
25
 
28
- **Tie-break order: the frozen-scope test runs FIRST, before the size test.**
29
- First ask "does this change already-frozen scope?" → if yes, it is a `change-request`
30
- (never re-size frozen work as new scope). Only if no, apply the size test: a new theme
31
- `new-major`; a slice of a live theme → `sub-milestone`; fits the active milestone
32
- → `task`.
26
+ **Tie-break order: the frozen-scope test runs FIRST, before the size test.** Ask "does this change
27
+ already-frozen scope?" → if yes it is a `change-request` (never re-size frozen work as new scope).
28
+ Only if no, apply the size test: a new theme → `new-major`; a slice of a live theme → `sub-milestone`;
29
+ fits the active milestone → `task`.
33
30
 
34
- **One-task gap rule.** If the request is ONE task but does NOT fit the active milestone's
35
- stated scope, do NOT force it into `sub-milestone` (which requires "too big for one task").
36
- Instead: create a new micro-milestone to house it (`new-milestone` + `new-task`). The
37
- micro-milestone gives the task ledger attribution and clear exit criteria without inflating scope.
31
+ **One-task gap rule.** If the request is ONE task but does NOT fit the active milestone's stated
32
+ scope, do not force it into `sub-milestone` (which requires "too big for one task"): create a new
33
+ micro-milestone to house it (`new-milestone` + `new-task`) — that gives the task ledger attribution
34
+ and clear exit criteria without inflating scope.
38
35
 
39
36
  ## What you emit (the proposal)
40
37
 
41
- Present the proposal to the human via `report-template.md` — open with the ARC (goal · done ·
42
- plan): the goal this request serves, what is already covered, and the plan the chosen bucket sets up.
43
- Render the proposal as a guided choice — the recommended bucket + its described alternatives (per `report-template.md`).
38
+ Present the proposal via `report-template.md` — open with the ARC (goal · done · plan): the goal this
39
+ request serves, what is already covered, and the plan the chosen bucket sets up. Render it as a guided
40
+ choice — the recommended bucket + its described alternatives (per `report-template.md`). For every
41
+ request, emit ONE of:
44
42
 
45
- For every request, emit ONE of:
46
-
47
- - **a classification** `{ bucket, rationale, command }` — where `rationale` names WHY
48
- (the theme, the slice, the fit, or the frozen scope touched) and `command` is the exact
49
- `add.py …` from the table. The human confirms or overrides before you run it.
50
- - **a rejection** — `{ reject, rationale }` — and you create nothing, emitting one of the closed set:
43
+ - **a classification** — `{ bucket, rationale, command }` — `rationale` names WHY (the theme, the
44
+ slice, the fit, or the frozen scope touched) and `command` is the exact `add.py …` from the table.
45
+ The human confirms or overrides before you run it.
46
+ - **a rejection** `{ reject, rationale }` create nothing, from this closed set:
51
47
 
52
48
  <reject_codes>
53
49
  - `ask_human` — too ambiguous/underspecified to size. Ask the human; never guess a bucket.
54
- - `frozen_scope` — it changes frozen scope; route it as a `change-request` back to
55
- SPECIFY/CONTRACT of the affected task — never spawn a parallel milestone that forks the truth.
56
- - `split_required` — it spans more than one bucket; propose the SMALLEST set of correctly-sized
57
- items, each with its own rationale; never force it into one milestone.
50
+ - `frozen_scope` — it changes frozen scope; route it as a `change-request` back to SPECIFY/CONTRACT of
51
+ the affected task — never spawn a parallel milestone that forks the truth.
52
+ - `split_required` — it spans more than one bucket; propose the SMALLEST set of correctly-sized items,
53
+ each with its own rationale; never force it into one milestone.
58
54
  </reject_codes>
59
55
 
60
- When confirmed, record the `rationale` in the artifact you create or affect — the new
61
- MILESTONE.md goal/body, the new TASK.md, or a note in the affected TASK.md — never in state.json.
56
+ When confirmed, record the `rationale` in the artifact you create or affect — the new MILESTONE.md
57
+ goal/body, the new TASK.md, or a note in the affected TASK.md — never in state.json.
62
58
 
63
59
  ## Worked examples (from this project's own history)
64
60
 
package/skill/add/loop.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # The dynamic loop — open deltas and extras become the next tasks
2
2
 
3
3
  A milestone is not done when its tasks are done — it is done when its **GOAL** is met.
4
- This guide is the loop that drives a milestone toward that goal: turn what each task
5
- leaves behind (open lessons, and work discovered but out of scope) into the next tasks,
6
- and keep going until the exit criteria are all met.
4
+ This guide drives a milestone toward that goal: turn what each task leaves behind (open lessons,
5
+ and work discovered but out of scope) into the next tasks, and keep going until the exit criteria
6
+ are all met.
7
7
 
8
- You (the AI) **gather and propose**; the **human confirms**; the existing `add.py new-task`
8
+ You **gather and propose**; the **human confirms**; the existing `add.py new-task`
9
9
  creates each one. The engine never decides what the next task is — that is judgment.
10
10
 
11
11
  ## The goal-gate (what holds the loop open)
@@ -13,19 +13,19 @@ creates each one. The engine never decides what the next task is — that is jud
13
13
  `add.py milestone-done <slug>` REFUSES to close a milestone while its exit criteria are not
14
14
  all met — it stops with `milestone_goal_unmet` and the milestone stays active. The exit-criteria
15
15
  checkboxes in `MILESTONE.md` ARE the human's goal-met affirmation: the engine reads the
16
- `- [x]`/`- [ ]` tally, it never judges whether the goal is met (the same trust model as reading a
17
- recorded `PASS`). Checking the last box is the deliberate act that releases the gate.
16
+ `- [x]`/`- [ ]` tally, it never judges whether the goal is met. Checking the last box is the
17
+ deliberate act that releases the gate.
18
18
 
19
19
  The gate fires only when criteria exist. A milestone with no exit-criteria checkboxes closes as
20
20
  before — write criteria into `MILESTONE.md` if you want the goal-gate to hold the milestone open.
21
21
 
22
22
  `milestone-done` is the only way a milestone reaches `done`; `archive-milestone` and `compact`
23
- both refuse a milestone that is not done. So the one gate is enough — there is no quiet way around it.
23
+ both refuse a milestone that is not done. The one gate is enough — there is no quiet way around it.
24
24
 
25
25
  ## The loop
26
26
 
27
27
  When every task is done but the goal is not, `add.py status` shows
28
- `goal not met (m/n exit criteria)` where it would otherwise prompt to archive. That is the cue:
28
+ `goal not met (m/n exit criteria)`. That is the cue:
29
29
 
30
30
  1. **Gather** the carried inventory:
31
31
  - open lessons — `add.py deltas` (the §7 OBSERVE deltas still `open`);
@@ -38,36 +38,32 @@ When every task is done but the goal is not, `add.py status` shows
38
38
  the normal flow (specify → … → verify).
39
39
  5. **Repeat** until the work the goal needs is done.
40
40
  6. **Close** — when the goal is genuinely met, run the **ship review** before you close:
41
- - **Fill the ship review first** — write the milestone's `## Close — ship review` section (the
42
- scaffold ships in `MILESTONE.md`): **Ship by domain** — what changed per bounded context
43
- (`tooling` · `skill` · `book`, or "untouched"); **Cross-task evidence** — one row per task
44
- (`gate` · `tests` · `residue`); and the **Goal met?** map — each exit criterion tied to the
45
- evidence that satisfies it. This is the whole-milestone, cross-task evidence the human READS;
46
- it is evidence, **not a new gate**.
41
+ - **Fill the ship review first** — write the milestone's `## Close — ship review` section:
42
+ **Ship by domain** — what changed per bounded context (`tooling` · `skill` · `book`, or
43
+ "untouched"); **Cross-task evidence** — one row per task (`gate` · `tests` · `residue`);
44
+ and the **Goal met?** map — each exit criterion tied to the evidence that satisfies it.
45
+ This is the whole-milestone cross-task evidence the human READS; it is evidence, not a gate.
47
46
  - **Check the boxes** — read that evidence, then check the exit-criteria boxes in `MILESTONE.md`
48
- (the single affirmation — the same gate as ever), and `add.py milestone-done <slug>` succeeds
49
- (then consolidate the open deltas and archive — the `milestone-done → fold → compact → archive`
50
- lifecycle, per `fold.md` · `compact-foundation.md`).
51
- - **Define the release steps** — write the milestone's `## Release steps` (merge is one small step
52
- among them; PR, asset export, tag/publish are others tool-agnostic hints the human runs).
53
- These **feed** the release scope — read `release.md` for the cut; loop.md never re-specifies it.
54
- Present the close via `report-template.md` — open with the ARC (goal · done · plan): the milestone
55
- goal, the exit-criteria met (with the ship-review evidence) that prove it, and the plan beyond the
56
- close. Render the close as a guided choice — the recommended next move + its described alternatives
57
- (per `report-template.md`).
47
+ (the single affirmation), and `add.py milestone-done <slug>` succeeds (then consolidate open
48
+ deltas and archive — the `milestone-done → fold → compact → archive` lifecycle, per
49
+ `fold.md` · `compact-foundation.md`).
50
+ - **Define the release steps** — write the milestone's `## Release steps` (merge is one small
51
+ step among them; PR, asset export, tag/publish are others). These **feed** the release scope —
52
+ read `release.md` for the cut; loop.md never re-specifies it.
53
+ Present the close via `report-template.md` — open with the ARC (goal · done · plan) and
54
+ render as a guided choice (recommended next move + described alternatives).
58
55
 
59
56
  ## Reopen is the verb; this loop is the trigger
60
57
 
61
- When a deepened verify (the no-skim wiring / dead-code / semantic check) finds a criterion unmet
62
- on a task already marked done, `add.py reopen <task> --to <phase> --reason "..."` returns it to the
63
- flow with a recorded reason and a reset gate. `reopen` is the recorded action; deciding WHEN to
64
- fire it — because a goal criterion is unmet — is this loop's job.
58
+ When a deepened verify finds a criterion unmet on a task already marked done,
59
+ `add.py reopen <task> --to <phase> --reason "..."` returns it to the flow with a recorded
60
+ reason and a reset gate. Deciding WHEN to fire it because a goal criterion is unmet — is this
61
+ loop's job.
65
62
 
66
63
  ## The reactivation residual (deferred)
67
64
 
68
- A reopen fired inside the loop happens while the milestone is still **active** — the goal-gate held
69
- it open, so it never reached done, and no reactivation is needed. The one residual — reopening a
70
- task inside a milestone that was already closed — is surfaced by `add.py check` (a done milestone
71
- with a live task reads as incoherent). Re-activating a closed milestone is **deferred**: resolve it
72
- by hand for now (the loop's own design keeps in-flight milestones open), until a later task makes
73
- milestone reactivation first-class.
65
+ A reopen fired inside the loop happens while the milestone is still **active** — the goal-gate
66
+ held it open, so it never reached done. The one residual — reopening a task inside a milestone
67
+ that was already closed — is surfaced by `add.py check` (a done milestone with a live task reads
68
+ as incoherent). Re-activating a closed milestone is **deferred**: resolve it by hand for now,
69
+ until a later task makes milestone reactivation first-class.