@pilotspace/add 2.0.0 → 2.1.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.
@@ -0,0 +1,69 @@
1
+ # The ADD persona contract
2
+
3
+ What the ADD engine reads and validates. Miss the required parts and the persona fails
4
+ `add.py check`; miss the recommended frontmatter and no apply-surface loads it. This is the
5
+ hard schema — `references/patterns.md` is the judgment that fills it well.
6
+
7
+ ## File
8
+
9
+ - Path: `.add/personas/<slug>.md` — `<slug>` is kebab-case (e.g. `payments-api-engineer`).
10
+ - **Never overwrite** an existing persona file; author a new slug or fold into the named one.
11
+ - **Never** name a persona `_`-prefixed — the engine treats `_`-prefixed files as scaffolds and
12
+ skips them (they are excluded from the roster, emptiness checks, and quality WARNs).
13
+
14
+ ## Frontmatter
15
+
16
+ ```yaml
17
+ ---
18
+ name: <persona name — e.g. Payments API Engineer> # REQUIRED
19
+ vibe: <one-line essence — what this persona keeps true> # REQUIRED
20
+ flow: <design | build | advisor | verify> # RECOMMENDED — comma-separate if >1
21
+ task-kinds: <from the closed taxonomy, comma-separated> # RECOMMENDED
22
+ use-when: <pushy should-select line — enumerate triggers> # RECOMMENDED
23
+ not-when: <the near-miss that belongs to a named sibling> # RECOMMENDED
24
+ folded: <consolidation history, newest first> # OPTIONAL
25
+ source: <teacher file(s) distilled from> # OPTIONAL
26
+ ---
27
+ ```
28
+
29
+ - **`name` · `vibe`** — REQUIRED. Absence fails the schema check.
30
+ - **`flow`** — the apply-surfaces this lens loads at. The ONLY valid values are
31
+ `design` · `build` · `advisor` · `verify` (single-sourced as `constants.PERSONA_FLOW_VALUES`).
32
+ Any other value is a typo that no surface loads — `add.py check` emits a `persona_quality` WARN
33
+ naming it. Surfaces: **design** = the UDD requirements lens · **build** = the domain-identity
34
+ overlay on SOUL.md · **advisor** = the subagent/streams delegation lens · **verify** = the
35
+ evidence-judging lens (earned-green refute-read + gate record).
36
+ - **`task-kinds`** — the persona's SCOREBOARD KEY, from the closed taxonomy:
37
+ `feature · refactor · test · docs · ui · security · data · infra · release · integration`.
38
+ Route-outcome traces join a task's `kind:` header to this claim, so performance is measurable
39
+ per kind. A value outside the taxonomy scores as nothing.
40
+ - **`use-when` / `not-when`** — the selection boundary. Selectors under-trigger on essence lines,
41
+ so `use-when` ENUMERATES the concrete contexts that should pick THIS persona; `not-when` names
42
+ the sibling that owns the near-miss (e.g. `CI permissions → security-gatekeeper`).
43
+
44
+ ## Sections
45
+
46
+ **REQUIRED (engine-checked, presence-based):**
47
+
48
+ - `## Identity`
49
+ - `## Critical Rules`
50
+ - `## Default Requirement`
51
+ - `## Success Metrics`
52
+
53
+ **RECOMMENDED (a surface can't fully use the lens without them):**
54
+
55
+ - `## Abilities`
56
+
57
+ **OPTIONAL (absence is conformant):**
58
+
59
+ - `## Anti-patterns`
60
+ - `## Playbook`
61
+
62
+ ## Quality WARNs `add.py check` surfaces (non-blocking, measure-not-block)
63
+
64
+ - **flow typo** — a `flow:` value outside the four is named in the finding (loaded by no surface).
65
+ - **bare placeholder** — a `<…>` token left outside backtick spans and HTML comments (a half-filled
66
+ copy). Backticked (`` `<slug>` ``) and commented (`<!-- <x> -->`) angle brackets are content, not
67
+ placeholders. Sweep every real `<…>` before you finish.
68
+
69
+ These are WARNs, never failures — but a roster-ready persona clears all of them.
@@ -0,0 +1,122 @@
1
+ # The judgment layer — distilled from strong subagent design
2
+
3
+ Eleven patterns that separate an expert lens from an undifferentiated keyword list. Each is drawn
4
+ from an apple-to-apple read of strong agent files (senior-rust/-java engineers, python-expert,
5
+ module-doc-generator, component-tracer, ux-design-architect, and peers) plus a diagnosis of the
6
+ vendored teacher corpus, and cast for an ADD persona. Contract (which section) →
7
+ `references/contract.md`; this file is *how to fill it well*.
8
+
9
+ ## Contents
10
+ 1. Earned-perspective Identity
11
+ 2. Bold-lead Critical Rules
12
+ 3. The qualification gate
13
+ 4. Read-before-you-assert
14
+ 5. Failure-mode-aware Success Metrics
15
+ 6. ORIENT-first Abilities
16
+ 7. Design-for-failure (conditional)
17
+ 8. Guilty-until-proven Anti-patterns
18
+ 9. Numbers you'd defend
19
+ 10. Per-flow stance
20
+ 11. Deliberate exclusions — what NOT to put in a persona
21
+
22
+ ---
23
+
24
+ ## 1. Earned-perspective Identity
25
+ Every strong agent opens not with a title but with *what it has seen*. State the domain depth AND
26
+ the scar that shapes its judgement.
27
+ - ✗ "You are a senior payments engineer with expertise in APIs."
28
+ - ✓ "…has shipped reconciliation systems where a single un-idempotent retry double-charged a
29
+ customer, so it treats every write as replayable until proven otherwise."
30
+ The scar is what makes the later Anti-patterns feel inevitable rather than arbitrary.
31
+
32
+ ## 2. Bold-lead Critical Rules
33
+ Lead each rule with a **bold clause**, then the why. Scannable beats prose. Keep it to what the
34
+ persona would actually *refuse to wave through* — not a wish list.
35
+ - ✗ "Always make sure to handle errors properly and think about idempotency."
36
+ - ✓ "**Every write is idempotent** — a retried request must not double-apply; key it or reject it."
37
+ When a rule is subtle, one ✗/✓ contrast pair teaches more than a paragraph — "follow best
38
+ practices" is useless; "wrap MobX components in `observer()` (see `RoleSkillCard.tsx:15`)" acts.
39
+
40
+ ## 3. The qualification gate
41
+ The single sharpest transferable stance. Before elaborating, name the simplest baseline that meets
42
+ the contract; if it wins, take it and STOP. Cleverness is a tax the project pays forever.
43
+ - ✓ Critical Rule: "**Simplest baseline first** — if a plain table + unique index meets the
44
+ contract, ship that; an event-sourced ledger earns its keep or it's a tax."
45
+ Its sibling move covers contradictory requirements ("minimal but feature-rich"): name the tension
46
+ and propose the balance with its cost — never silently satisfy one half. And perspective proves
47
+ itself by naming the losing option: *why this, not that — and why that loses*.
48
+
49
+ ## 4. Read-before-you-assert
50
+ The reporting agents (module-doc, component-tracer) make this a hard rule: never cite a file,
51
+ symbol, or line you have not opened. In an ADD persona it is an Anti-pattern:
52
+ - ✓ "a claim resting on a file/symbol not opened → open it or cut the claim."
53
+ It binds outputs too: every path or example the persona's own deliverable cites must exist at the
54
+ named place — a placeholder that survives into the deliverable is the same defect. This mirrors
55
+ the add-worker floor ("never invent a file you have not opened") as a domain instinct.
56
+
57
+ ## 5. Failure-mode-aware Success Metrics
58
+ A metric is only expertise if it names the way of being wrong it catches. State each as an
59
+ INVARIANT (true as the project grows), paired with its failure mode.
60
+ - ✗ "High test coverage; good performance."
61
+ - ✓ "**No double-post under retry** — a replayed request leaves the ledger byte-identical (catches
62
+ the un-idempotent write); **p95 < 150 ms at 100 rps** (catches the N+1 that only shows under load)."
63
+ Each bar must be checkable IN-SESSION by the agent holding the lens — a behaviour it can observe
64
+ or a test it can run. An invented outcome statistic ("engagement +40%") sounds measured and never
65
+ was; it is the signature rot of weak persona corpora.
66
+
67
+ ## 6. ORIENT-first Abilities
68
+ Lead the ability list with the 1–3 commands the lens RUNS on load before acting — `add.py status`,
69
+ the domain's suite, the diff to judge. Acting on ground truth beats re-deriving it. State every
70
+ other ability as something doable *now*, anchored to a real file/tool/command — not an aspiration.
71
+ - ✓ "can diff two response fixtures byte-for-byte to prove passthrough" (checkable)
72
+ - ✗ "understands API design deeply" (unfalsifiable)
73
+
74
+ ## 7. Design-for-failure (conditional)
75
+ Any persona that owns I/O, network, or infra carries a design-for-failure ability: it can name the
76
+ **timeout · retry · circuit-breaker · rollback** for every external call. An unbounded await or a
77
+ silent half-write is a defect, never "expected". Omit this for pure design/docs lenses — forcing it
78
+ on a lens that touches no I/O is noise. Match the pattern to the persona's real surface.
79
+
80
+ ## 8. Guilty-until-proven Anti-patterns
81
+ Distinct from Critical Rules (always-do): these are the smells the lens treats as *guilty until
82
+ proven innocent*, each with its default reaction. The sharpest are the instincts the Identity's
83
+ scars produced — and the strongest attach the COST to the smell:
84
+ - ✓ "'0 issues found' on a first pass → look harder."
85
+ - ✓ "an abstraction with no second caller → cut it."
86
+ - ✓ "PIL in production preprocessing → 3× slower than cv2; reach for cv2 first."
87
+ A smell with its price is an argument; a bare smell is a style opinion.
88
+
89
+ ## 9. Numbers you'd defend
90
+ The cheapest bytes-per-judgment in strong agents: a named budget beats an adjective. "Optimize
91
+ performance" buys nothing; "p95 < 200 ms at the declared load", "44×44 px touch targets", "batch
92
+ wait ≤ 10 ms" anchor the lens to a bar it can hold a build to. Two conditions, or the number is
93
+ cosplay: the expert could defend WHY that number (name what breaks past it), and the lens can
94
+ check it in-session (see 5). Fake precision reads as measured and teaches the agent to invent —
95
+ worse than no number at all.
96
+
97
+ ## 10. Per-flow stance
98
+ The strongest agents split behaviour by mode and bind a bookend to each — *reviewing opens with
99
+ the defect sweep; reimplementing opens with the qualification gate; explaining closes with failure
100
+ modes*. A persona claiming more than one `flow:` does the same in one or two lines: what it LEADS
101
+ with at build, what it REFUSES at verify. A verify stance carries the default verdict — NEEDS-WORK
102
+ until the evidence cites the actual run — plus its automatic-fail triggers. A lens whose rules
103
+ read identically at every flow hasn't decided what each surface is for.
104
+
105
+ ## 11. Deliberate exclusions — what NOT to put in a persona
106
+ A persona is a layer in a stack; keep the other layers' work OUT of it.
107
+ - **No tone/voice** — that is SOUL.md's. A persona that prescribes phrasing is duplicating it.
108
+ - **No self-score / confidence rubric** — the agent (add-worker) owns the six-dimension score.
109
+ - **No output skeleton** — the deliverable's shape is the agent's Return contract, not the lens's.
110
+ - **No stakes/CoT priming** ("take a deep breath", "$500 tip") — motivation is the agent's; the
111
+ persona supplies judgment, not pep talk.
112
+ - **No keyword taxonomy** — a page of noun-phrase bullets ("saga pattern, composite indexes, …")
113
+ buys no behaviour; the model knows the words. Every bullet carries an opinion or it goes.
114
+ - **No tutorial code dumps** — framework boilerplate rots fast; a snippet earns its place only
115
+ when it encodes a rule the prose can't.
116
+ - **No invented metrics or fabricated telemetry** — "+40% engagement", "47 pipelines deployed":
117
+ numbers that sound measured train the lens to hallucinate finished-ness.
118
+ - **No adverb-padded checklists** — "documented thoroughly", "monitored comprehensively" are
119
+ unverifiable filler wearing a checklist's clothes.
120
+ - **No other project's paths** — a persona anchors to THIS project's real files and commands;
121
+ an inherited path from a seed source is contamination, not context.
122
+ Every line you cut from these categories makes the judgment that remains sharper.
@@ -0,0 +1,79 @@
1
+ # Seeding a persona from an existing source
2
+
3
+ Authoring from a blank page is the slowest path and the one most likely to miss the judgment
4
+ layer. When a near-fit source exists, **seed from it, then distil** — never copy it wholesale. Two
5
+ sources are always worth checking first, in this order:
6
+
7
+ 1. **The teacher library** — `.add/personas-teacher/**/*.md`, grouped by division folder
8
+ (`design/`, `sales/`, `paid-media/`, …). A vetted, domain-organised corpus. Prefer this.
9
+ 2. **A sample subagent** — a `~/.claude/agents/*.md` file (e.g. `senior-rust-engineer.md`) when the
10
+ teacher library has no near-fit for a technical domain.
11
+
12
+ A source is a **head-start on structure**, not a finished lens. The mechanical parts map straight
13
+ across; the judgment parts you must *distil* (compress a verbose body into a few sharp clauses);
14
+ and a few parts the source will **never** carry — you add those yourself. That last column is the
15
+ whole point: a seed that skips it is just a reformatted résumé.
16
+
17
+ ## Teacher file → ADD persona
18
+
19
+ A teacher file: frontmatter `name · vibe · description · color · emoji`, division from its folder,
20
+ and a verbose motivational body (`## 🧠 Identity & Memory`, `## 🎯 Core Mission`, …).
21
+
22
+ | ADD schema | Seed from the teacher | Action |
23
+ |---|---|---|
24
+ | `name:` | frontmatter `name` | carry across |
25
+ | `vibe:` | frontmatter `vibe` | carry across (tighten to one line) |
26
+ | `flow:` | the division folder — `design/` → `design`; a review/audit persona → `verify`/`advisor` | **infer, then confirm** against the closed values |
27
+ | `task-kinds:` | the domain (a `design/` lens → `ui`; a data persona → `data`) | **you pick** from the closed taxonomy |
28
+ | `use-when:` / `not-when:` | frontmatter `description` seeds `use-when`; the sibling it's most confused with seeds `not-when` | distil + **add `not-when`** (the source has none) |
29
+ | `## Identity` | the body's identity/experience paragraph ("You've seen developers struggle with…") | **distil to earned perspective** — scars, not the résumé |
30
+ | `## Critical Rules` | any `**Default requirement**:` / non-negotiable lines in the body | distil to bold-lead rules; **add** the two default stances (surface-tradeoffs · qualification-gate) |
31
+ | `## Default Requirement` | the one `Default requirement:` line if present | carry or write |
32
+ | `## Success Metrics` | *(the teacher rarely has measurable metrics)* | **ADD — this is the judgment.** MEASURABLE invariants, each sharpened by the failure it guards |
33
+ | `## Abilities` | the `Core Mission` bullets | distil to concrete, anchored, checkable actions; lead with the ORIENT commands |
34
+ | `## Anti-patterns` | the instincts implied by the "you've seen X fail" lines | name them guilty-until-proven; **always add** read-before-you-assert |
35
+
36
+ ## Subagent md (`~/.claude/agents/*.md`) → ADD persona
37
+
38
+ A subagent file: frontmatter `name · description · model · color`, the `description` embeds a
39
+ `Use when: …` clause and `<example>` blocks, and the body leads with `## Core Principles
40
+ (Non-Negotiable)`.
41
+
42
+ | ADD schema | Seed from the subagent | Action |
43
+ |---|---|---|
44
+ | `name:` / `vibe:` | frontmatter `name`; **vibe has no source** | carry name; **write a one-line vibe** |
45
+ | `flow:` / `task-kinds:` | the `<example>` Contexts name the work (review → `verify`; build → `build`) | infer flow + kinds, confirm against the closed sets |
46
+ | `use-when:` / `not-when:` | the `Use when: …` clause in `description`; each `<commentary>` names a core capability | carry `use-when`; **add `not-when`** |
47
+ | `## Identity` | the opening `You are a **…**` paragraph | distil to earned perspective |
48
+ | `## Critical Rules` | the `## Core Principles (Non-Negotiable)` list | distil to bold-lead rules (keep 1–2 signatures); **add** the two default stances |
49
+ | `## Abilities` | the capabilities in the `<commentary>` tags + the body's numbered principles | make each doable *now*, anchored to a file/tool/command; an I/O lens **adds** design-for-failure |
50
+ | `## Success Metrics` | *(subagents state principles, not measurable bars)* | **ADD — measurable, failure-aware invariants** |
51
+ | `## Anti-patterns` | the "only when genuinely required" / "avoid" hedges | name them guilty-until-proven; **always add** read-before-you-assert |
52
+
53
+ ## What to mine, what to refuse
54
+
55
+ Both source families carry gold the mapping tables can't express — and a signature rot that must
56
+ not survive the seed:
57
+
58
+ - **Teacher gold** — the one-line `Default requirement:` floor · the "You've seen…" scar sentence
59
+ (feeds Identity) · behaviour-paired metric rows ("100% warm transfers — never a cold handoff")
60
+ · a named methodology with its verbatim moves and why-they-work (feeds a Playbook) · the rare
61
+ "when NOT to use" lines (feed `not-when:` as *symptom → sibling*) · a reviewer's default-verdict
62
+ stance with automatic-fail triggers (feeds a verify-flow stance).
63
+ - **Teacher rot — refuse** — deliverable code dumps (CSS/config skeletons), emoji-header chrome,
64
+ invented outcome statistics ("+40% engagement" no one measured), motivational closers and
65
+ "instructions reference" footers that point at nothing.
66
+ - **Subagent gold** — review-mode checklists (feed the verify side of a per-flow stance) ·
67
+ pitfalls with the cost attached ("PIL in prod preprocessing → 3× slower than cv2" — feeds
68
+ Anti-patterns) · budgets the author would defend ("p95 < 200 ms", "44×44 px" — feed
69
+ Rules/Metrics) · mode bookends ("reviewing opens with the defect sweep").
70
+ - **Subagent rot — refuse** — keyword-taxonomy pages (nouns buy no behaviour), tip bribes and
71
+ self-score rubrics, tutorial code blocks, and another project's hard-coded paths — a persona
72
+ anchors to THIS project's real files only.
73
+
74
+ ## After the seed
75
+
76
+ Record provenance honestly: add a `source:` frontmatter line naming the seed — the teacher slug,
77
+ or `agents/<file>` — (the contract lists `source` as an optional field). Then run the **Workflow** in `SKILL.md` over the
78
+ seeded draft — every section still faces its judgment bar — and `add.py check` until green. A seed
79
+ that never had the Success-Metrics and Anti-patterns columns filled is not done.
@@ -1,8 +1,9 @@
1
1
  # Build — AI writes the code (the beat you drive to green)
2
2
 
3
- Objective: implement the feature so EVERY failing test passes without changing any
4
- test or the contract. This is the only phase the AI leads; §1–§4 removed all
5
- ambiguity. Write code into `.add/tasks/<slug>/src/`.
3
+ Objective: implement the feature so EVERY failing test — or, for a non-coding task, every §4
4
+ acceptance check — passes, without changing any test or the contract. This is the only phase the
5
+ AI leads; §1–§4 removed all ambiguity. Write code into `.add/tasks/<slug>/src/` (a non-coding
6
+ task writes its artifact to the path its §5 Scope declares).
6
7
 
7
8
  ## Work in small batches
8
9
 
@@ -15,7 +16,7 @@ each batch small enough to review in full.
15
16
 
16
17
  - **Scope (may touch)** — the allowlist the build may write (backticked tokens); a file outside it is a **STOP → change request** back to Specify.
17
18
  - **Strategy (ordered batches)** — the planned build order; guidance, not enforced.
18
- - **Strategy facets** — Approach (domain strategy) · Data strategy · Pattern · Optimization stance: the domain HOW, anchored upstream (§1 Framings · §3 Schema · CONVENTIONS.md Honors), drafted at tests->build in the Persona's domain vocabulary; ⚠-mark the facet you trust least (risk: high → consult the add agent, advise mode). Advisory, never a gate.
19
+ - **Strategy facets** — Approach (domain strategy) · Data strategy · Pattern · Optimization stance: the domain HOW, anchored upstream (§1 Framings · §3 Schema · CONVENTIONS.md Honors), drafted at tests->build in the Persona's domain vocabulary; ⚠-mark the facet you trust least (risk: high → spawn `add-advisor`, advise-midflight mode). Advisory, never a gate.
19
20
 
20
21
  Enforced: a completing verify gate refuses an out-of-scope build (`scope_violation` → self-heal).
21
22
 
@@ -39,7 +40,7 @@ contract.** A genuine need to change either is a change request back to Specify.
39
40
  ## Exit gate
40
41
 
41
42
  <exit_gate>
42
- - [ ] All tests pass.
43
+ - [ ] All tests (or §4 acceptance checks) pass.
43
44
  - [ ] Coverage did not decrease.
44
45
  - [ ] No test and no contract modified by the AI.
45
46
  - [ ] No dependency outside the allow-list.
@@ -81,7 +81,7 @@ stamp its §3 `Status: FROZEN @ v1`, build is open.
81
81
  <exit_gate>
82
82
  - [ ] `.add/state.json` exists; setup seeded unlocked (`--await-lock`) then locked.
83
83
  - [ ] Seed lines filled; untouched sections carry the living marker (brownfield: evidence-grounded from code).
84
- - [ ] First task created; §1–§4 drafted — the red suite runs RED before build opens; `.add/SETUP-REVIEW.md` written lowest-confidence-first.
84
+ - [ ] First task created; §1–§4 drafted — the red suite (or §4 acceptance checks) runs RED before build opens; `.add/SETUP-REVIEW.md` written lowest-confidence-first.
85
85
  - [ ] Human confirmed the baseline approval and `add.py lock --by` ran with their name.
86
86
  </exit_gate>
87
87
 
@@ -236,6 +236,13 @@ scenario asserting **behavior, not internals** · contract-conformance tests (sh
236
236
  responses) · side-effect assertions on rejection paths (`assert balance unchanged`) · a recorded
237
237
  coverage target.
238
238
 
239
+ **Non-coding task?** For `kind: docs · release · infra` (or a wholly non-coding project) the
240
+ check need not be a script: §4 is a failing-first **acceptance check** — verifiable pass/fail
241
+ evidence (renders · every internal link resolves · `§X covers A/B/C` · a command exits 0), red
242
+ before the artifact exists and green after. Declare `Tests live in: evidence`. Red→green still
243
+ binds; only the must-be-executable-code requirement is lifted (the human may declare acceptance
244
+ mode on any task). Coding kinds keep the executable red suite above.
245
+
239
246
  ## Declaring where tests live
240
247
 
241
248
  §4's `Tests live in:` line is machine-read — declare paths as backticked tokens on that line: with
@@ -254,7 +261,7 @@ moves. Ground §3 on each parent edge's frozen §3 — the PLAN.md itself, never
254
261
  built code.
255
262
 
256
263
  <exit_gate>
257
- - [ ] One test per scenario, red for the right reason, asserting observable behavior; coverage target recorded.
264
+ - [ ] One test (or acceptance check, for a non-coding kind) per scenario, red for the right reason, asserting observable behavior; coverage target recorded.
258
265
  </exit_gate>
259
266
 
260
267
  > **Persona / Advisor / Confidence** — load the domain-fit `.add/personas/<slug>.md` (its Critical
@@ -11,7 +11,7 @@ sufficient. Fill **§6** in PLAN.md including the GATE RECORD.
11
11
 
12
12
  ## Part one — confirm the evidence
13
13
 
14
- - [ ] All tests pass.
14
+ - [ ] All tests pass — or, for a non-coding task, every §4 acceptance check is green (the evidence it names is real).
15
15
  - [ ] Coverage did not decrease.
16
16
  - [ ] No test or contract was altered during build.
17
17
  - [ ] The §3 Target (measurable) is hit — including any declared outcome tests can't show, confirmed by real evidence.
@@ -50,7 +50,7 @@ tasks use the compact form — banner → SUMMARY → EVIDENCE → APPROVE; `sec
50
50
  |---------|------|
51
51
  | `PASS` | all checks met |
52
52
  | `RISK-ACCEPTED` | a **non-security** gap, with signed owner + ticket + expiry |
53
- | `HARD-STOP` | any failing test or any security finding |
53
+ | `HARD-STOP` | any failing test or acceptance check, or any security finding |
54
54
 
55
55
  ## Exit gate / Next
56
56
 
@@ -95,10 +95,11 @@ Spawn a *single* subagent for one well-scoped piece of your plan (many-task pipe
95
95
  stream-orchestrator persona); the engine never spawns — your call per step. Spawn when the piece
96
96
  is separable and worth the round-trip: a broad sweep, an independent adversarial review (the
97
97
  refute-read — fresh context, never author-graded), a batch, a context-offload; not for narrow
98
- cheap work — in doubt, do it in-context. **Prefer the named roster**: the ONE `add` agent the
99
- spawn names the mode (direction · build · verify · persona · advise) over an ad-hoc spawn; it
100
- carries the worker contract and loads the beat guide + best-fit persona itself. Tier: **mid**
101
- ordinary, **top** complex/cross-cutting (the roster contract in `agents/add.md` maps tiers to
98
+ cheap work — in doubt, do it in-context. **Prefer the named roster**: `add-worker` for an execution
99
+ piece (the spawn names the mode: direction · build · verify · persona) and `add-advisor` for an
100
+ advisory one (propose-plan · refute · advise-midflight) over an ad-hoc spawn; each carries its
101
+ roster contract and loads the beat guide + best-fit persona itself. Tier: **mid** ordinary, **top**
102
+ complex/cross-cutting (the roster contract in `agents/*.md` maps tiers to
102
103
  models); a stronger model never buys back a gate. **Refute-read persona** — a **Code-Reviewer**;
103
104
  findings carry severity: 🔴 blocker · 🟡 concern · 💭 note. A persona is advisory: it never
104
105
  lowers a gate (a security finding still HARD-STOPs).
package/skill/add/run.md CHANGED
@@ -89,6 +89,11 @@ Two findings enter the loop:
89
89
 
90
90
  Either way: ≤3 honest redos, then escalate. A gamed green never ships.
91
91
 
92
+ **Every return trip is a visible round** (round-visible-runs): any verify→build return —
93
+ a plain `add.py phase build [--note "finding"]` or a heal — increments the task's
94
+ uncapped, observational `rounds` record; `status` names `round N` and the route trace
95
+ carries it. Heal stays the cheat-classed, capped subset; rounds are the honest whole.
96
+
92
97
  ## Emitting deltas — feeding the foundation back
93
98
 
94
99
  Every gap the completeness-critic finds becomes an **`open` lesson learned** in the task's OBSERVE
package/tooling/add.py CHANGED
@@ -593,6 +593,11 @@ def cmd_init(args: argparse.Namespace) -> None:
593
593
  continue
594
594
  _atomic_write(dest, rendered)
595
595
 
596
+ # persona-skill: personas are AUTHORED via the persona-author skill (not seeded from a
597
+ # template) — but the location must exist so the first authored persona has a home and the
598
+ # unseeded nudge has a directory to check. Create it empty; the skill fills it.
599
+ (root / "personas").mkdir(parents=True, exist_ok=True)
600
+
596
601
  # specs-5dd (ADD 2.0 M3): the five living 5-DD specs — same survivor idiom as
597
602
  # SETUP_FILES (never clobber, never write blank), ONE template rendered five ways.
598
603
  for dd in SPEC_DDS:
@@ -779,6 +784,8 @@ def cmd_new_task(args: argparse.Namespace) -> None:
779
784
  # was HARD-STOP escalated) could launder the cap (HEAL_CAP) to zero by re-creating itself —
780
785
  # a zero-human cap bypass (the same invariant _heal_or_escalate guards: "never auto-resets").
781
786
  prior_heal = state["tasks"].get(slug, {}).get("heal") if args.force else None
787
+ # round-visible-runs: the round record is monotonic the same way — survives a --force re-create.
788
+ prior_rounds = state["tasks"].get(slug, {}).get("rounds") if args.force else None
782
789
  state["tasks"][slug] = {
783
790
  "title": title,
784
791
  "phase": "direction",
@@ -796,6 +803,8 @@ def cmd_new_task(args: argparse.Namespace) -> None:
796
803
  state["tasks"][slug]["relates_to"] = relates_to
797
804
  if prior_heal is not None:
798
805
  state["tasks"][slug]["heal"] = prior_heal # monotonic — survives the --force re-create
806
+ if prior_rounds is not None:
807
+ state["tasks"][slug]["rounds"] = prior_rounds # same monotonic contract as heal
799
808
  if from_delta:
800
809
  state["tasks"][slug]["from_delta"] = from_delta # lineage: seeded from <prior>
801
810
  if sensitivity:
@@ -1686,8 +1695,21 @@ def cmd_phase(args: argparse.Namespace) -> None:
1686
1695
  # scope snapshot), so verify's _tamper_guard is armed and a freeze-gated DRAFT §3 is refused.
1687
1696
  # validate-then-write: a refusal raises BEFORE the phase is set, so nothing moves. The heal
1688
1697
  # loop sets phase=build directly (never via cmd_phase) and so stays exempt.
1698
+ # round-visible-runs: --note annotates a verify->build ROUND — refuse it anywhere else,
1699
+ # BEFORE any write (validate-then-write; a refused entry leaves state byte-unchanged).
1700
+ # The refusal keys on the FLAG being passed (whitespace included) and the note is stored
1701
+ # VERBATIM (§1 Boundary); the contract's refusal exit code is 2.
1702
+ note = getattr(args, "note", None)
1703
+ if note is not None and args.phase != "build":
1704
+ _die("phase_note_build_only: --note annotates the verify->build round this return "
1705
+ "records — it is only valid with target build", code=2)
1706
+ prior = state["tasks"][slug].get("phase")
1689
1707
  if args.phase == "build":
1690
1708
  _build_entry(root, state, slug, skip_freeze=getattr(args, "skip_freeze", False))
1709
+ # round-visible-runs: a verify->build return trip IS a round — record it in the SAME
1710
+ # save as the phase write (atomic; no round without the return, no return without it).
1711
+ if args.phase == "build" and prior == "verify":
1712
+ _record_round(state["tasks"][slug], source="phase", note=note)
1691
1713
  state["tasks"][slug]["phase"] = args.phase
1692
1714
  state["tasks"][slug]["updated"] = _now()
1693
1715
  save_state(root, state) # F12: durable state FIRST (source of truth) — may _die
@@ -2293,6 +2315,7 @@ def _append_route_trace(root: Path, state: dict, slug: str, outcome: str) -> Non
2293
2315
  "kind": kind, "lane": lane, "routed_by": by,
2294
2316
  "persona": m.group(1) if m else None, "outcome": outcome,
2295
2317
  "heals": (t.get("heal") or {}).get("attempts", 0),
2318
+ "rounds": (t.get("rounds") or {}).get("count", 0), # visible verify->build return trips
2296
2319
  "recross": bool(t.get("recross")), "age_hours": age,
2297
2320
  "target_hit": t.get("target_hit"), # the §3 Target judgment (plan-core)
2298
2321
  "actor": (t.get("gate_actor") or {}).get("name"),
@@ -2925,7 +2948,10 @@ def cmd_status(args: argparse.Namespace) -> None:
2925
2948
  _now_active = _active_task(state)
2926
2949
  if _now_active and _now_active in (state.get("tasks") or {}):
2927
2950
  _now_ph = (state["tasks"][_now_active] or {}).get("phase", "?")
2928
- print(f"now : '{_now_active}' · phase={_now_ph} · {_next_footer(root, state)}")
2951
+ # round-visible-runs: a bounced task names its return-trip count; silent at 0.
2952
+ _now_r = ((state["tasks"][_now_active] or {}).get("rounds") or {}).get("count", 0)
2953
+ _now_rr = f" · round {_now_r}" if _now_r else ""
2954
+ print(f"now : '{_now_active}' · phase={_now_ph}{_now_rr} · {_next_footer(root, state)}")
2929
2955
  print(f" PLAN.md: .add/tasks/{_now_active}/PLAN.md · re-orient: add.py status --brief")
2930
2956
  print(f"project : {state.get('project', '(unknown)')}")
2931
2957
  # project autonomy default (task init-auto-default): the posture new tasks INHERIT,
@@ -3957,7 +3983,7 @@ def cmd_check(args: argparse.Namespace) -> None:
3957
3983
  infos.append((f"persona '{slug}'", "schema-conformant"))
3958
3984
  # persona-schema-hardening: quality findings the presence check can't see
3959
3985
  # (typo'd flow: value · bare <…> placeholder) — WARN-only (measure-not-block),
3960
- # REAL personas only: the seeded `_template.md` is all placeholders by design.
3986
+ # REAL personas only: any `_`-prefixed scaffold is placeholders by design.
3961
3987
  if not slug.startswith("_"):
3962
3988
  try:
3963
3989
  text = pf.read_text(encoding="utf-8")
@@ -4914,6 +4940,18 @@ def _scope_guard(root: Path, state: dict, slug: str) -> None:
4914
4940
  f"declared §5 Scope — {shown} ({len(out)} total)"))
4915
4941
 
4916
4942
 
4943
+ def _record_round(task: dict, *, source: str, note: str | None = None) -> None:
4944
+ """Record ONE verify->build return trip — a visible 'round' (round-visible-runs).
4945
+
4946
+ Uncapped and OBSERVATIONAL: rounds never gate, never cap, never move a phase — they
4947
+ make a dynamic verify->fix workflow legible in status and the route traces. Distinct
4948
+ from the heal counter (the CHEAT-classed, capped subset); a heal return records BOTH.
4949
+ Caller owns the save — the increment rides the same atomic write as the phase move."""
4950
+ r = task.setdefault("rounds", {"count": 0, "history": []})
4951
+ r["count"] = r.get("count", 0) + 1
4952
+ r.setdefault("history", []).append({"at": _now(), "source": source, "note": note})
4953
+
4954
+
4917
4955
  def _heal_or_escalate(root: Path, state: dict, slug: str, *, reason: str, source: str) -> None:
4918
4956
  """The bounded self-heal router (verify-integrity, heal-then-escalate). Called ONLY when
4919
4957
  a cheat is CONFIRMED at this point — mechanical (tripwire divergence, source "tamper") or
@@ -4941,6 +4979,7 @@ def _heal_or_escalate(root: Path, state: dict, slug: str, *, reason: str, source
4941
4979
  "spec (change-request -> re-freeze) or abandon. A gamed green is never auto-passed.")
4942
4980
  heal["attempts"] = heal.get("attempts", 0) + 1
4943
4981
  heal.setdefault("history", []).append(entry)
4982
+ _record_round(t, source=source) # a heal return is ALSO a visible round (uncapped view)
4944
4983
  t["phase"] = "build" # DIRECT — never via advance (no re-snapshot)
4945
4984
  t["updated"] = _now()
4946
4985
  _sync_task_marker(root, slug, "build")
@@ -6682,6 +6721,8 @@ def build_parser() -> argparse.ArgumentParser:
6682
6721
  # cmd_phase) so pre-collapse scripts keep working; the stored value is always canonical.
6683
6722
  pp.add_argument("phase", choices=PHASES + tuple(LEGACY_PHASES))
6684
6723
  pp.add_argument("slug", nargs="?", default=None)
6724
+ pp.add_argument("--note", default=None,
6725
+ help="annotate the verify->build round this return records (build target only)")
6685
6726
  pp.add_argument("--skip-freeze", action="store_true",
6686
6727
  help="cross direction->build on a DRAFT §3, recording an auditable freeze_skipped "
6687
6728
  "marker (the universal freeze gate's only bypass; never auto-freezes §3)")
@@ -115,18 +115,18 @@ PHASE_GROUPS = {
115
115
  "VERIFY": ("verify",),
116
116
  }
117
117
  # phase-bundles: the roster agent PREFERRED for each phase (per-PHASE, not per-bundle —
118
- # roster-distill (ADD 2.0 M1): ONE execution shell the `add` agent serves every
119
- # phase; the spawn prompt names the mode (direction·build·verify·advise·persona) and
120
- # the agent loads that beat's guide + the fitting persona (personas carry the
121
- # expertise, the agent carries the discipline). A phase missing here is a bug (PHASE_GROUPS'
122
- # own union covers every key); `_phase_bundle` is the fail-closed resolver for an
123
- # unmapped/corrupted phase token, not this map directly.
118
+ # advisor-split: `add-worker` is the execution shell for every phase; the spawn prompt names
119
+ # the mode (direction·build·verify·persona) and the agent loads that beat's guide + the fitting
120
+ # persona (personas carry the expertise, the agent carries the discipline). `add-advisor` is
121
+ # spawned on demand to propose/pressure-test/decide it is not a per-phase default, so it is
122
+ # absent here. A phase missing here is a bug (PHASE_GROUPS' own union covers every key);
123
+ # `_phase_bundle` is the fail-closed resolver for an unmapped/corrupted phase token, not this map.
124
124
  PHASE_AGENT = {
125
- "direction": "add",
126
- "build": "add",
127
- "verify": "add",
125
+ "direction": "add-worker",
126
+ "build": "add-worker",
127
+ "verify": "add-worker",
128
128
  }
129
- SETUP_FILES = ("PROJECT.md", "CONVENTIONS.md", "GLOSSARY.md", "MODEL_REGISTRY.md", "dependencies.allowlist", "DESIGN.md", "SOUL.md", "personas/_template.md")
129
+ SETUP_FILES = ("PROJECT.md", "CONVENTIONS.md", "GLOSSARY.md", "MODEL_REGISTRY.md", "dependencies.allowlist", "DESIGN.md", "SOUL.md")
130
130
 
131
131
  # persona-setup: a PERSONA living doc (`.add/personas/<slug>.md`) is a frozen-schema file
132
132
  # distilled from the vendored teacher library to its critical-rules + default-requirement +
@@ -168,18 +168,19 @@ SPEC_DDS = {
168
168
  # THIS constant (not their own copy) so the wording can never drift across the three surfaces.
169
169
  # Project-scoped (not "this milestone's domain") per the confirmed v2 amendment: the AI should
170
170
  # catch up ALL of a project's missing personas, not draft a single milestone-fit one.
171
- PERSONA_HINT = ("no project-fit persona seeded yet under .add/personas/ — spawn the add agent "
172
- "in persona mode (or read docs/18-personas.md) to seed the project's persona(s) "
173
- "from PROJECT.md's domain + the seed templates (tooling/templates/personas/)")
171
+ PERSONA_HINT = ("no project-fit persona seeded yet under .add/personas/ — use the persona-author "
172
+ "skill (or read docs/18-personas.md) to author the project's persona(s) "
173
+ "from PROJECT.md's domain")
174
174
 
175
175
  # persona-fit-nudge: the OPPOSITE-branch, mutually-exclusive sibling of PERSONA_HINT — fires only
176
176
  # when ≥1 real persona ALREADY exists, so a brand-new milestone doesn't silently assume one of
177
177
  # them fits its domain. Existence-only (names the persona slugs already seeded); the AI still
178
- # owns the actual fit judgment (the add agent's persona mode) the engine never scores content
179
- # similarity. {slugs} is filled at call time from `.add/personas/*.md` (excluding `_template`).
178
+ # owns the actual fit judgment (add-worker's persona mode, guided by the persona-author skill)
179
+ # the engine never scores content similarity. {slugs} is filled at call time from
180
+ # `.add/personas/*.md` (excluding any `_`-prefixed scaffold).
180
181
  PERSONA_FIT_HINT_TEMPLATE = (
181
- "existing persona(s) seeded — {slugs} — confirm one fits this milestone's domain, or spawn "
182
- "the add agent in persona mode (or read docs/18-personas.md) to draft a better-fit one"
182
+ "existing persona(s) seeded — {slugs} — confirm one fits this milestone's domain, or use the "
183
+ "persona-author skill (or read docs/18-personas.md) to author a better-fit one"
183
184
  )
184
185
 
185
186
  # Scaffolded into .add/.gitignore at init so the engine's transient LOCAL artifacts
@@ -46,10 +46,11 @@ def _guideline_block() -> str:
46
46
  "PROJECT.md `invariants:` (run/entry contracts) bind EVERY task: the artifact must hold under\n"
47
47
  "the BARE declared runtime — a dependency that breaks it is a defect, never \"expected\".\n"
48
48
  "\n"
49
- "Roster (from `add-method/agents/add.md`): ONE `add` agent the spawn names the mode\n"
50
- "(direction · build · verify · advise · persona); the agent loads that beat's phase guide\n"
51
- "plus the best-fit `.add/personas/` persona (personas carry the expertise, the agent\n"
52
- "carries the discipline; the floor above binds every mode).\n"
49
+ "Roster (from `add-method/agents/*.md`): TWO agents — `add-worker` runs each EXECUTION beat\n"
50
+ "(the spawn names it: direction · build · verify · persona), and `add-advisor` is the second\n"
51
+ "mind it spawns to propose a plan, pressure-test a draft, or decide a delegable ambiguity so\n"
52
+ "the beat never stalls. Each loads that beat's phase guide plus the best-fit `.add/personas/`\n"
53
+ "persona (personas carry the expertise, the agent carries the discipline; the floor binds both).\n"
53
54
  "\n"
54
55
  "On Claude Code the `add` skill drives this loop; other agents follow the three steps. Book: https://pilotspace.github.io/ADD/. This block is generated by `add.py sync-guidelines` — edit outside the markers.\n"
55
56
  f"{_GUIDE_END}"
@@ -270,9 +270,9 @@ def _md5_file(p: Path) -> str | None:
270
270
 
271
271
  def _personas_unseeded(root: Path) -> bool:
272
272
  """True when `.add/personas/` has no REAL (non-template) authored persona: the
273
- directory is absent, empty, or holds only the seeded `_template.md` scaffold
274
- (persona-seed-nudge). Fail-soft: an unreadable directory counts as unseeded
275
- rather than raising — this feeds a `note:`/INFO hint, never a gate."""
273
+ directory is absent, empty, or holds only a `_template.md` scaffold (personas are
274
+ authored via the persona-author skill, not seeded). Fail-soft: an unreadable directory
275
+ counts as unseeded rather than raising — this feeds a `note:`/INFO hint, never a gate."""
276
276
  d = root / "personas"
277
277
  if not d.is_dir():
278
278
  return True
@@ -71,7 +71,7 @@ Verified by: <agent-id> · at: <ISO-8601 UTC timestamp>
71
71
 
72
72
  ---
73
73
 
74
- ## 4 · TESTS — failing-first suite (red) ▸ docs/06-step-4-tests.md
74
+ ## 4 · TESTS — failing-first suite or acceptance checks (red) ▸ docs/06-step-4-tests.md
75
75
 
76
76
  <test_plan>
77
77
  - test_<name>: arrange / act / assert behavior not internals · covers: <M#, R:code>
@@ -79,6 +79,7 @@ Verified by: <agent-id> · at: <ISO-8601 UTC timestamp>
79
79
 
80
80
  Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
81
81
  <!-- declare paths as backticked tokens on this line: `./…` = this task dir · a token with "/" = the project root · a bare name = a sibling of the previous token's dir · a directory counts its *.py files (non-recursive) · declared counts marked † · outside the project root counts 0. The test_plan bullets' `covers:` tails are machine-read too: `add.py locate path::test_name` resolves a failing test to the frozen §3 clause it proves -->
82
+ <!-- NON-CODING task (kind: docs · release · infra, or a non-coding project)? §4 is a failing-first ACCEPTANCE CHECK, not a script — verifiable pass/fail evidence (mkdocs build succeeds · §X covers A/B/C · every internal link resolves), red before the artifact exists and green after. Set `Tests live in: evidence` (no `./tests/`). The red→green discipline holds; only the must-be-executable-code requirement is lifted. -->
82
83
 
83
84
  ---
84
85
 
@@ -86,14 +87,14 @@ Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
86
87
 
87
88
  Strategy actually used: <fill at VERIFY — what you ACTUALLY did (or "as planned"); harvested into §7 Decisions (ADR)>
88
89
  Code lives in: `./src/`
89
- Spawn (multi-agent): build/verify subagent spawns default `isolation: worktree`; cross-agent advisor — spawn the `add` agent in **advise mode** (an agent OTHER than the builder) for the freeze `--cross` and the §6 refute-read; `self` only when solo.
90
+ Spawn (multi-agent): build/verify subagent spawns default `isolation: worktree`; cross-agent advisor — spawn `add-advisor` (an agent OTHER than the builder) for the freeze `--cross` and the §6 refute-read; `self` only when solo.
90
91
  Constraints: do NOT change any test or the frozen §3 contract; stay inside §3 Scope (an out-of-scope build fails the gate: scope_violation); keep the §3 Regression floor green; allow-list packages only; ask if unclear.
91
92
 
92
93
  ---
93
94
 
94
95
  ## 6 · VERIFY — evidence + non-functional review ▸ docs/08-step-6-verify.md
95
96
 
96
- - [ ] all tests pass — including the §3 Regression floor (host suite)
97
+ - [ ] all tests (or §4 acceptance checks) pass — including the §3 Regression floor (host suite)
97
98
  - [ ] coverage did not decrease
98
99
  - [ ] no test or contract was altered during build
99
100
  - [ ] the green was EARNED, not gamed — no overfit to fixtures, vacuous asserts, or stubbed-away logic (a confirmed cheat is HARD-STOP)