@pilotspace/add 1.16.0 → 1.17.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.
@@ -24,6 +24,8 @@ __all__ = [
24
24
  "SETUP_FILES",
25
25
  "PERSONA_FRONTMATTER_KEYS",
26
26
  "PERSONA_REQUIRED_SECTIONS",
27
+ "PERSONA_HINT",
28
+ "PERSONA_FIT_HINT_TEMPLATE",
27
29
  "GUIDELINE_FILES",
28
30
  "RULES_FILE_REL",
29
31
  "WORKFLOW_HEADINGS",
@@ -99,6 +101,24 @@ SETUP_FILES = ("PROJECT.md", "CONVENTIONS.md", "GLOSSARY.md", "MODEL_REGISTRY.md
99
101
  PERSONA_FRONTMATTER_KEYS = ("name", "vibe")
100
102
  PERSONA_REQUIRED_SECTIONS = ("## Identity", "## Critical Rules", "## Default Requirement", "## Success Metrics")
101
103
 
104
+ # persona-seed-nudge v2: ONE hint, single-sourced — `new-milestone`/`check`/`status` all print
105
+ # THIS constant (not their own copy) so the wording can never drift across the three surfaces.
106
+ # Project-scoped (not "this milestone's domain") per the confirmed v2 amendment: the AI should
107
+ # catch up ALL of a project's missing personas, not draft a single milestone-fit one.
108
+ PERSONA_HINT = ("no project-fit persona seeded yet under .add/personas/ — spawn the add-persona "
109
+ "agent (or read docs/18-personas.md) to seed the project's persona(s) from "
110
+ "PROJECT.md's domain")
111
+
112
+ # persona-fit-nudge: the OPPOSITE-branch, mutually-exclusive sibling of PERSONA_HINT — fires only
113
+ # when ≥1 real persona ALREADY exists, so a brand-new milestone doesn't silently assume one of
114
+ # them fits its domain. Existence-only (names the persona slugs already seeded); the AI still
115
+ # owns the actual fit judgment via add-persona — the engine never scores content similarity.
116
+ # {slugs} is filled at call time from `.add/personas/*.md` (excluding `_template`).
117
+ PERSONA_FIT_HINT_TEMPLATE = (
118
+ "existing persona(s) seeded — {slugs} — confirm one fits this milestone's domain, or spawn "
119
+ "the add-persona agent (or read docs/18-personas.md) to draft a better-fit one"
120
+ )
121
+
102
122
  # Scaffolded into .add/.gitignore at init so the engine's transient LOCAL artifacts
103
123
  # never reach git. Bare-filename patterns match at any depth under .add/ (tasks/,
104
124
  # milestones/, archive/). These are working state, not records: scope-snapshot.json
@@ -199,6 +219,7 @@ fast: true
199
219
  ## 0 · GROUND
200
220
  Touches (files · symbols):
201
221
  Anchors the contract cites:
222
+ Ground SHA:
202
223
 
203
224
  ## 1 · SPECIFY
204
225
  Feature:
@@ -199,3 +199,30 @@ def _md5_file(p: Path) -> str | None:
199
199
  return hashlib.md5(p.read_bytes()).hexdigest()
200
200
  except OSError:
201
201
  return None
202
+
203
+
204
+ def _personas_unseeded(root: Path) -> bool:
205
+ """True when `.add/personas/` has no REAL (non-template) authored persona: the
206
+ directory is absent, empty, or holds only the seeded `_template.md` scaffold
207
+ (persona-seed-nudge). Fail-soft: an unreadable directory counts as unseeded
208
+ rather than raising — this feeds a `note:`/INFO hint, never a gate."""
209
+ d = root / "personas"
210
+ if not d.is_dir():
211
+ return True
212
+ try:
213
+ return not any(p.stem != "_template" for p in d.glob("*.md"))
214
+ except OSError:
215
+ return True
216
+
217
+
218
+ def _real_persona_slugs(root: Path) -> list[str]:
219
+ """Sorted slugs of REAL (non-template) personas under `.add/personas/` — the existence-only
220
+ listing persona-fit-nudge names in its hint. Fail-soft: an unreadable/absent dir is empty,
221
+ mirroring `_personas_unseeded`'s own fail-soft convention."""
222
+ d = root / "personas"
223
+ if not d.is_dir():
224
+ return []
225
+ try:
226
+ return sorted(p.stem for p in d.glob("*.md") if p.stem != "_template")
227
+ except OSError:
228
+ return []
@@ -18,6 +18,7 @@ Touches (files · symbols): <path:symbol — what it is / how it is keyed>
18
18
  Context (working folder): <docs · config · data the task touches — task-delta only>
19
19
  Honors (patterns / conventions): <PROJECT.md / CONVENTIONS.md anchors — task-delta>
20
20
  Anchors the contract cites: <the symbols §3 will name>
21
+ Ground SHA: <`git rev-parse --short HEAD` at ground time — any line ref reads "as of" it>
21
22
 
22
23
  ---
23
24
 
@@ -56,7 +57,7 @@ Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
56
57
  ## 5 · BUILD — AI writes code
57
58
 
58
59
  Scope (may touch): `./src/` <every file the build may write — declared before the §3 freeze>
59
- Strategy & known-problem fixes: <ordered build steps · the trap each known problem must dodge>
60
+ Strategy & known-problem fixes: <ordered build steps · the trap each known problem must dodge · let the active persona's domain stance (or "generic") shape the approach, not just patterns>
60
61
  Strategy actually used: <fill at verify — what you ACTUALLY did, or "as planned"; harvested into §7 Decisions>
61
62
  Code lives in: `./src/` · Constraints: change no test, no contract; allow-list packages only.
62
63
 
@@ -2,15 +2,11 @@
2
2
 
3
3
  slug: {{slug}} · created: {{date}} · stage: {{stage}}
4
4
  milestone: {{milestone}}
5
- 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, or run `add.py autonomy set`. Multi-component repo (monorepo/multi-repo)? add a `component: <name>` line (declared in `.add/components.toml`) to ADD that component's root to your §5 Scope; omit for single-component projects (byte-identical default). -->
5
+ autonomy: {{autonomy}} <!-- level: manual < conservative < auto — lower for a high-risk task (`add.py autonomy set`). Multi-component repo? add a `component: <name>` line (.add/components.toml) to join that root to §5 Scope. -->
6
6
  phase: ground <!-- ground -> specify -> scenarios -> contract -> tests -> build -> verify -> observe -> done -->
7
- <!-- high-risk/method-defining scope? declare `risk: high` on the slug line above and lower the
8
- autonomy level to `manual` or `conservative` — the engine refuses an unguarded completion
9
- (`unguarded_high_risk_auto`, run.md guard). A comment is never a declaration. -->
7
+ <!-- high-risk/method-defining? declare `risk: high` on the slug line + a lowered autonomy — the engine refuses an unguarded completion (`unguarded_high_risk_auto`). A comment is never a declaration. -->
10
8
 
11
- > One file = one task. Fill sections top-to-bottom; the `add` skill drives each phase.
12
- > When a phase is unclear, read its book chapter in `.add/docs/` (linked per section).
13
- > The phase marker above is the single source of truth — keep it in sync via `add.py phase`.
9
+ > One file = one task fill top-to-bottom; the phase marker above is the single source of truth (`add.py phase`); unclear phase its book chapter.
14
10
 
15
11
  ---
16
12
 
@@ -49,7 +45,7 @@ Assumptions — lowest-confidence first:
49
45
  - [ ] <next assumption, ranked> — confirm or deny; never carry an open one forward
50
46
  </assumptions>
51
47
 
52
- <!-- 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). -->
48
+ <!-- EXIT: every rule + rejection stated; assumptions ranked lowest-confidence first, top 1–2 ⚠-flagged with why + cost (or an honest "none material" naming the biggest risk). -->
53
49
 
54
50
  ---
55
51
 
@@ -83,14 +79,7 @@ Schema: <tables/fields touched, and access pattern>
83
79
  Glossary deltas: <new domain term(s) this task introduces, `Term: definition` — or "none">
84
80
  Status: DRAFT
85
81
  Reported: <yes — the freeze report (banner/ARC/SHAPE) rendered before this froze | no>
86
- <!-- The freeze IS the one approval — lead it with the bundle's lowest-confidence flag: the 1–2
87
- points most likely wrong across the whole bundle, tagged [spec|scenario|contract|test], each
88
- with why + cost (the §1 ⚠ assumptions feed it; a flag may point at a scenario or the contract
89
- too — see run.md). Approved -> Status: FROZEN @ vN — approved by <name>. Changing a frozen
90
- contract = change request back to SPECIFY.
91
- EXIT: frozen + every spec rejection has a contracted response + names match GLOSSARY (new
92
- terms declared as a Glossary delta) + the bundle's lowest-confidence flag was surfaced at
93
- the freeze (or an honest "none material"). -->
82
+ <!-- The freeze IS the one approval — lead it with the bundle's lowest-confidence flag (§1 ⚠ feeds it; a flag may point at any part — run.md). Approved -> Status: FROZEN @ vN — approved by <name>; changing a frozen contract = change request back to SPECIFY. EXIT: frozen · every §1 rejection has a contracted response · names match GLOSSARY (new terms = Glossary delta) · flag surfaced. -->
94
83
 
95
84
  ---
96
85
 
@@ -103,10 +92,7 @@ Plan (one test per scenario, asserting behavior not internals):
103
92
  </test_plan>
104
93
 
105
94
  Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
106
- <!-- declare paths as backticked tokens on this line: `./…` = this task dir ·
107
- a token with "/" = project root · a bare name = sibling of the previous
108
- token's dir · a directory counts its *.py files (non-recursive); reports
109
- mark declared counts with † · anything resolving outside the project root counts 0 -->
95
+ <!-- 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 -->
110
96
 
111
97
  <!-- EXIT: one test per scenario; suite red for the RIGHT reason; target recorded. -->
112
98
 
@@ -115,9 +101,9 @@ Tests live in: `./tests/` · MUST run red (missing implementation) before Build.
115
101
  ## 5 · BUILD — AI writes code ▸ docs/07-step-5-build.md
116
102
 
117
103
  Scope (may touch): `./src/` <fill before the §3 freeze — every file the build may write>
118
- Strategy (ordered batches): <1. … 2. … — the planned build order; guidance, not enforced; preferred architecture/pattern strategies; advise solution/method to resolve issues/implement features>
104
+ Strategy (ordered batches): <1. … 2. … — the planned build order; guidance, not enforced; preferred architecture/pattern strategies; advise solution/method to resolve issues/implement features; let the named Persona's domain stance (below) shape the approach, not just architecture patterns>
119
105
 
120
- Persona (optional): <name the persona file under `.add/personas/` this build embodies as a domain stance atop SOUL.md — advisory, never lowers a gate; absent = generic>
106
+ Persona (required): <name the persona file under `.add/personas/` this build embodies as a domain stance atop SOUL.md — advisory, never lowers a gate; name "generic" if no project persona fits yet>
121
107
  Spawn isolation (default): <prefer isolation: "worktree" for any subagent build/verify spawn, not only explicit parallel mode; shared-tree needs a stated reason — see worktree-isolated-spawn-default>
122
108
  Known-problem fixes: <trap → planned fix — the failure modes this build must dodge; guidance, not enforced>
123
109
  Strategy actually used: <fill at VERIFY — the strategy you ACTUALLY used (or "as planned"); harvested into the §7 Decisions (ADR) block as the [AI] build decision>
@@ -125,14 +111,7 @@ Safety rule (feature-specific): <e.g. debit+credit in one atomic transaction>
125
111
  Code lives in: `./src/`
126
112
  Constraints: do NOT change any test or the contract; allow-list packages only; ask if unclear.
127
113
 
128
- <!-- Scope tokens, backticked, FIRST declaring line: `./…` = this task dir · a token
129
- with "/" = project root · a bare name = sibling of the previous token's dir ·
130
- outside-root resolutions are dropped fail-closed · a DIRECTORY token covers its
131
- whole subtree (containment — diverges from §4's non-recursive counting) ·
132
- absent line = UNDECLARED (pre-existing tasks grandfathered, never retro-red) ·
133
- engine enforcement (touched ⊆ declared) is live: a completing verify gate refuses an
134
- out-of-scope build (scope_violation → self-heal) and add.py check surfaces it.
135
- EXIT: all green; coverage held; no test/contract touched; no unlisted dependency. -->
114
+ <!-- Scope tokens, backticked, FIRST declaring line: `./…` = this task dir · a token with "/" = project root · a bare name = sibling of the previous token's dir · a DIRECTORY token covers its whole subtree (diverges from §4's non-recursive counting) · outside-root resolutions drop fail-closed · absent line = UNDECLARED (grandfathered, never retro-red) · enforcement live: a completing verify gate refuses an out-of-scope build (scope_violation → self-heal); check surfaces it. EXIT: all green; coverage held; no test/contract touched; no unlisted dependency. -->
136
115
 
137
116
  ---
138
117
 
@@ -148,9 +127,7 @@ Constraints: do NOT change any test or the contract; allow-list packages only; a
148
127
  - [ ] a person reviewed and approved the change
149
128
 
150
129
  ### Build expectations — what "correct" looks like (fill BEFORE build; confirm each at the gate)
151
- > Pre-declare the OBSERVABLE outcomes a correct build must produce derived from §2 SCENARIOS
152
- > + §3 CONTRACT — so this gate checks the build is RIGHT, not merely that tests are green. Each
153
- > row is evidence you can SEE, not a restatement of a test name.
130
+ > OBSERVABLE outcomes a correct build must produce, derived from the §2 scenarios + §3 contract — evidence you can SEE, not test names.
154
131
  - [ ] <observable outcome a correct build must produce> — confirmed by <how / where>
155
132
  - [ ] <another observable outcome> — confirmed by <evidence seen>
156
133
 
@@ -160,26 +137,17 @@ Constraints: do NOT change any test or the contract; allow-list packages only; a
160
137
  - [ ] SEMANTIC (prose / non-code) — read in full, not skimmed: <what read · what confirmed>
161
138
 
162
139
  ### Live-verify evidence — confirm the §0 GROUND anchors still resolve (fill at the gate)
163
- > §0's Ground SHA anchors the symbols cited at ground time to that commit code moves during
164
- > build. Before the gate, re-resolve every symbol §3 CONTRACT cites against the CURRENT tree
165
- > (not the Ground SHA) so a stale anchor is caught here, not by a future reader chasing a moved
166
- > line.
140
+ > Re-resolve every symbol §3 cites against the CURRENT tree (code moved since Ground SHA)catch a stale anchor here, not later.
167
141
  - [ ] every symbol §3 CONTRACT cites still resolves in the current tree — confirmed by <how / where>
168
142
  - [ ] any anchor that moved/renamed since Ground SHA is named here, not left silent
169
143
 
170
144
  ### Refute-read verdict — the earned-green check (record it; required for an auto-PASS)
171
- > Under autonomy: auto the AI auto-resolves Verify, so the earned-green refute-read MUST be
172
- > recorded here (the engine never spawns it — you do; NOT-EARNED -> `add.py heal`). The engine
173
- > MEASURES it is filled (`audit: refute_unrecorded`); it never auto-blocks — a human spot-audit
174
- > is the backstop. A human-gated (conservative/manual) task may leave it for the human's judgment.
145
+ > Under auto, record the earned-green refute-read (the engine never spawns it — you do; NOT-EARNED -> `add.py heal`). Audit-measured (`refute_unrecorded`), never blocked; a human spot-audit is the backstop.
175
146
  Verdict: <EARNED | NOT-EARNED>
176
147
  By: <self | agent-id> · adversarially checked: <what was probed>
177
148
 
178
149
  ### Advisor 3-lens verdict — sequential (security → concurrency → architecture)
179
- > Under autonomy: auto run the 3-lens checklist and record the verdict here. Lenses run in
180
- > order; a Security HARD-STOP ends the checklist (leave remaining lenses blank). Binding for
181
- > sensitivity: mechanical (advisor-gate-relax reads it); advisory for all other sensitivities.
182
- > The engine MEASURES this block is filled (audit: advisor_verdict_unrecorded); it never blocks.
150
+ > Lenses run in order; a Security HARD-STOP ends the checklist (leave the rest blank). Binding for sensitivity: mechanical (advisor-gate-relax); advisory otherwise. Audit-measured (`advisor_verdict_unrecorded`), never blocked.
183
151
  Advisor: <agent-id | self>
184
152
  1. Security: <CLEAR | HARD-STOP: finding>
185
153
  2. Concurrency: <CLEAR | RESIDUE: finding>
@@ -194,7 +162,7 @@ Outcome: <PASS | RISK-ACCEPTED | HARD-STOP>
194
162
  If RISK-ACCEPTED -> owner: <name> · ticket: <link> · expires: <date> (never for a security gap)
195
163
  Reviewed by: <name> · date: <date>
196
164
 
197
- <!-- A security finding is ALWAYS HARD-STOP. Record exactly one outcome — no silent pass. The Advisor 3-lens verdict and the Refute-read verdict are both measured by `add.py audit` (`advisor_verdict_unrecorded` · `refute_unrecorded`) neither is engine-blocked; a human spot-audit is the backstop for any finding the AI did not surface or record. -->
165
+ <!-- Security is ALWAYS HARD-STOP; record exactly one outcome — no silent pass. The Advisor 3-lens and Refute-read verdicts are audit-measured (`advisor_verdict_unrecorded` · `refute_unrecorded`), never engine-blocked; a human spot-audit backstops anything unrecorded. -->
198
166
 
199
167
  ---
200
168
 
@@ -206,11 +174,8 @@ Watch (reuse scenarios as monitors): <error rate / per-rejection rate / latency>
206
174
  <harvested at done from §1/§3/§5/§6 — do not hand-edit; one actor-tagged line per decision, refilled only while this placeholder stands>
207
175
 
208
176
  ### Spec delta
209
- Forward changes for the next loop — each re-enters at Specify as the next task. One line
210
- each, tagged `[SPEC · open|seeded|dropped]`, with evidence (e.g. `[SPEC · open] rate-limit
211
- the retry path (evidence: prod herd spikes)`). See the `add` skill's `deltas.md`.
177
+ One line per forward change, tagged `[SPEC · open|seeded|dropped]` + evidence — each re-enters at Specify (`deltas.md`).
212
178
 
213
179
  ### Competency deltas
214
- What did this loop teach the foundation? One line each, tagged by competency
215
- (`DDD · SDD · UDD · TDD · ADD`), status `open`, with evidence. See the `add` skill's `deltas.md`.
180
+ One lesson per line: `[DDD|SDD|UDD|TDD|ADD · open] the learning (evidence: …)` see `deltas.md`.
216
181
  <!-- e.g. - [DDD · open] the model missed multi-tenancy (evidence: scenario_x failed) -->
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: <persona name — e.g. Frontend Engineer, UX Researcher>
3
3
  vibe: <one-line essence — what this persona keeps true>
4
+ flow: <RECOMMENDED — which of the three apply-surfaces this persona is loaded at (see
5
+ docs/18-personas.md "Apply — three surfaces"): design (the UDD requirements lens) | build
6
+ (the domain-identity overlay on SOUL.md) | advisor (subagent/streams delegation, incl. the
7
+ verify refute-read) — comma-separate if more than one, e.g. `build, advisor`>
4
8
  source: <OPTIONAL — the teacher file(s) this was distilled from, e.g. `.add/personas-teacher/engineering/engineering-software-architect.md` (provenance; omit if hand-authored)>
5
9
  ---
6
10
  <!-- A PERSONA is a project-fit requirements persona, distilled from the vendored teacher
@@ -9,8 +13,11 @@ source: <OPTIONAL — the teacher file(s) this was distilled from, e.g. `.add/pe
9
13
  the teacher; the engine only seeds + validates this schema (presence-based). This
10
14
  `_template.md` is the schema reference — copy it to `<slug>.md` and fill it.
11
15
 
12
- REQUIRED: `name` + `vibe` frontmatter and the Identity / Critical Rules /
16
+ REQUIRED (engine-checked): `name` + `vibe` frontmatter and the Identity / Critical Rules /
13
17
  Default Requirement / Success Metrics sections.
18
+ RECOMMENDED (not engine-checked, but expected on every persona for consistency): `flow:`
19
+ frontmatter and the `## Abilities` section — a persona with no stated flow or abilities is
20
+ hard for the design/build/advisor surfaces to actually pick up and use.
14
21
  OPTIONAL (recommended for a faithful distillation): `source:` frontmatter, and the
15
22
  `## Anti-patterns` + `## Playbook` sections. The engine never requires the optional
16
23
  parts; absence is conformant.
@@ -23,12 +30,27 @@ source: <OPTIONAL — the teacher file(s) this was distilled from, e.g. `.add/pe
23
30
  3. TAG provenance honestly. In `## Playbook`, mark each item teacher-derived vs
24
31
  ADD-native. Never credit home-grown project scaffolding to the teacher.
25
32
  4. METRICS are rules, not snapshots. Prefer an invariant ("suite matches the last
26
- green run") over a volatile literal ("2491/0") that rots as the project grows. -->
33
+ green run") over a volatile literal ("2491/0") that rots as the project grows.
34
+ 5. NAME the flow. State which apply-surface(s) actually load this persona — a persona
35
+ that fits none of design/build/advisor is dead weight nobody will ever pick up.
36
+ 6. ABILITIES are checkable skills, not aspirations. Anchor each to a real file, tool, or
37
+ command this project already has — the same anchoring discipline as Critical Rules
38
+ and Success Metrics, applied to "what this persona can concretely do." -->
27
39
 
28
40
  ## Identity
29
41
  <who this persona is — role, domain depth, and the EARNED perspective it brings (what it has
30
42
  seen succeed/fail that shapes its judgement). One short paragraph.>
31
43
 
44
+ ## Abilities
45
+ <concrete, checkable things this persona can actually DO — a capability list, distinct from
46
+ Critical Rules (always-enforced constraints) and Playbook (optional step-by-step scaffolding).
47
+ State each as something the agent can perform right now, anchored to a real file/tool/command
48
+ where possible — not an aspiration.>
49
+ - <ORIENT first (convention): lead with the 1–3 commands the agent RUNS on load before acting —
50
+ e.g. `add.py status` · the domain's suite · the diff to judge — acting beats re-deriving>
51
+ - <a concrete capability — e.g. "can diff two response fixtures byte-for-byte to prove passthrough">
52
+ - <another concrete capability>
53
+
32
54
  ## Critical Rules
33
55
  <non-negotiables this persona ALWAYS enforces. Lead with 1–2 carried from the teacher (its
34
56
  signature stance), then add project-specific ones.>