@pilotspace/add 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.md +70 -3
- package/agents/add-advisor.md +31 -0
- package/agents/add-build.md +29 -0
- package/agents/add-design.md +32 -0
- package/agents/add-persona.md +31 -0
- package/agents/add-verify.md +34 -0
- package/bin/cli.js +581 -46
- package/docs/08-step-6-verify.md +7 -0
- package/package.json +2 -1
- package/skill/add/SKILL.md +3 -2
- package/skill/add/advisor.md +2 -0
- package/skill/add/loop.md +2 -2
- package/skill/add/phases/0-setup.md +3 -3
- package/skill/add/phases/3-contract.md +1 -1
- package/skill/add/phases/6-verify.md +3 -1
- package/skill/add/report-template.md +1 -0
- package/skill/add/run.md +1 -0
- package/skill/add/streams.md +7 -0
- package/tooling/add.py +86 -4
- package/tooling/templates/TASK.md.tmpl +3 -0
package/docs/08-step-6-verify.md
CHANGED
|
@@ -31,9 +31,15 @@ Before the build, the task pre-declares its **build expectations** — the obser
|
|
|
31
31
|
- [ ] Coverage did not decrease.
|
|
32
32
|
- [ ] No test or contract was altered during the build.
|
|
33
33
|
- [ ] Every pre-declared build expectation is confirmed by real evidence (not merely a green test).
|
|
34
|
+
- [ ] Every §1 Must/Reject rule still traces to a §2 scenario tag or a §4 `covers:` line.
|
|
35
|
+
- [ ] Every symbol §3's contract cites still resolves in the *current* tree, not just at Ground SHA.
|
|
34
36
|
|
|
35
37
|
If any of these is false, stop here and return to the build; there is nothing to verify yet.
|
|
36
38
|
|
|
39
|
+
A green suite proves the tests you wrote pass — it says nothing about a rule you never wired a scenario or test to at all. That is a different failure than the ones Part four covers: not a cheated pass, but a rule with no trace of ever being checked. `add.py check` scans every task project-wide for exactly this — a §1 Must or Reject with no §2 scenario tag and no §4 `covers:` line — but that sweep is easy to forget to run. `add.py audit`'s `rule_coverage_gap` notice surfaces the SAME check for the task actually at this gate, the moment it reaches Verify (a task that never adopted the tag convention at all is silently grandfathered, never retro-flagged). Neither blocks the gate; both make an already-real gap impossible to miss.
|
|
40
|
+
|
|
41
|
+
The second check guards against a quieter kind of drift. §0's Ground SHA anchors the symbols §3's contract cites to the commit they were read at — but code moves *during* the build that follows. Before this gate, re-resolve every one of those symbols against the tree as it stands now, not the Ground SHA snapshot, and record it in §6's **Live-verify evidence** block. A moved or renamed anchor caught here is a one-line note; the same drift left silent is a future reader chasing a line number that no longer means what it did at Ground.
|
|
42
|
+
|
|
37
43
|
## Part two — check what tests miss
|
|
38
44
|
|
|
39
45
|
Automated tests are excellent at behavior on defined inputs and poor at a few specific things. Check those by hand, every time:
|
|
@@ -77,6 +83,7 @@ A security finding is always a `HARD-STOP`; it is never waved through with a wai
|
|
|
77
83
|
|
|
78
84
|
- [ ] All tests pass (the evidence).
|
|
79
85
|
- [ ] Every pre-declared build expectation is confirmed by observable evidence.
|
|
86
|
+
- [ ] Every §1 rule traces to §2/§4; every §3-cited symbol still resolves in the current tree.
|
|
80
87
|
- [ ] Concurrency/timing of the risky operation is safe.
|
|
81
88
|
- [ ] No exposed secrets, injection openings, or unexpected dependencies.
|
|
82
89
|
- [ ] Layering and dependencies follow `CONVENTIONS.md`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pilotspace/add",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "ADD (AI-Driven Development). One skill. Eight steps. Five disciplines. Every feature ships through the loop — a minimal, state-tracked Claude Code skill that ships the AIDD book as its trust layer.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"add": "bin/cli.js"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"bin/",
|
|
20
20
|
"skill/",
|
|
21
|
+
"agents/",
|
|
21
22
|
"tooling/add.py",
|
|
22
23
|
"tooling/add_engine/",
|
|
23
24
|
"!tooling/add_engine/__pycache__",
|
package/skill/add/SKILL.md
CHANGED
|
@@ -120,8 +120,9 @@ once the human confirms, rewrites `SOUL.md` (the human is the only writer) — `
|
|
|
120
120
|
|
|
121
121
|
- **§3 CONTRACT FROZEN** → build→verify is a dynamic, auto-gated run (`autonomy: auto` default; lower to
|
|
122
122
|
`conservative`/`manual` for a human gate) — `run.md`. Pipeline ready tasks behind frozen
|
|
123
|
-
contracts — `streams.md`. Delegate one piece of your plan to a subagent
|
|
124
|
-
|
|
123
|
+
contracts — `streams.md`. Delegate one piece of your plan to a subagent — prefer the named roster
|
|
124
|
+
(`add-design`/`add-build`/`add-verify`/`add-persona`/`add-advisor`) over an ad-hoc spawn; when to
|
|
125
|
+
spawn, the prompt template, the tier — `advisor.md`. Self-score a draft (0–1 across six dimensions, refine if any < 0.9)
|
|
125
126
|
— `confidence.md`. Both advisory; the engine never spawns.
|
|
126
127
|
- **Small, low-risk task**, less ceremony → the **fast lane**: `new-task --fast` scaffolds the minimal
|
|
127
128
|
`TASK.fast.md`, bundle approved in one freeze — `phases/fast-lane.md`. Floor held (frozen contract ·
|
package/skill/add/advisor.md
CHANGED
|
@@ -11,6 +11,8 @@ Spawn when the piece is **separable and worth the round-trip**: a broad sweep; a
|
|
|
11
11
|
|
|
12
12
|
Do **not** spawn for narrow, cheap work — pay the round-trip only when the piece is big or independent enough. When in doubt, do it in-context.
|
|
13
13
|
|
|
14
|
+
**Prefer the named roster.** Check first whether the piece fits one of ADD's five phase specialists — `add-design` (setup–contract) · `add-build` (tests·build) · `add-verify` (verify·observe) · `add-persona` (fit-persona selection) · `add-advisor` (a consultative second opinion) — before an ad-hoc spawn; each already carries the worker contract below.
|
|
15
|
+
|
|
14
16
|
## The 3-lens sequential checklist at verify
|
|
15
17
|
|
|
16
18
|
At Verify, sweep security → concurrency → architecture in order. **Security HARD-STOP ends the checklist** (leave the rest blank). Each lens returns: **CLEAR** · **HARD-STOP** (security only) · **RESIDUE** (concurrency/architecture).
|
package/skill/add/loop.md
CHANGED
|
@@ -50,8 +50,8 @@ When every task is done but the goal is not, `add.py status` shows
|
|
|
50
50
|
- **Define the release steps** — write the milestone's `## Release steps` (merge is one small
|
|
51
51
|
step among them; PR, asset export, tag/publish are others). These **feed** the release scope —
|
|
52
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)
|
|
54
|
-
render as a guided choice
|
|
53
|
+
Present the close via `report-template.md` — open with the ARC (goal · done · plan),
|
|
54
|
+
render as a guided choice — **before `milestone-done`/`archive-milestone` run, not after.**
|
|
55
55
|
|
|
56
56
|
## Reopen is the verb; this loop is the trigger
|
|
57
57
|
|
|
@@ -47,12 +47,12 @@ Ask only the live ones. Rank: `⚠ <assumption> — lowest confidence because <w
|
|
|
47
47
|
|
|
48
48
|
Capture each surfaced decision as an **ADR** in `PROJECT.md` **Key Decisions** as it lands.
|
|
49
49
|
|
|
50
|
-
**Under `autonomy: auto`, auto-complete all four drives in one pass** — lowest-confidence-first. This deepens **drafting**, never the gate
|
|
50
|
+
**Under `autonomy: auto`, auto-complete all four drives in one pass** — lowest-confidence-first. This deepens **drafting**, never the gate — `lock` stays the one decision.
|
|
51
51
|
|
|
52
52
|
## 3 · Draft to the lock (both paths)
|
|
53
53
|
|
|
54
54
|
1. **Fill the living documentation**: `.add/PROJECT.md` (Domain · Spec · UI/UX · Key Decisions), `CONVENTIONS.md`, `GLOSSARY.md`, `MODEL_REGISTRY.md`, `dependencies.allowlist`, and — for a UI project — `DESIGN.md` (delete if no UI; `design.md`). Brownfield: from code. Greenfield: from interview, gaps flagged `guessed`.
|
|
55
|
-
- **Seed personas** (`.add/personas/`): `init` scaffolds `_template.md` (the schema). Author one per role from PROJECT.md + the vendored teacher library `.add/personas-teacher/` (read off-build; engine never fetches)
|
|
55
|
+
- **Seed personas** (`.add/personas/`): `init` scaffolds `_template.md` (the schema). **Author one per role** from PROJECT.md + the vendored teacher library `.add/personas-teacher/` (read off-build; engine never fetches) — citing the teacher in `source:` and carrying its top `## Playbook` are the two optional parts, not the authoring. Covered by the **baseline approval**; `add.py check` validates; never clobber.
|
|
56
56
|
2. **Propose, then size it.** Float a **kickoff suggestion** for the first milestone: a **goal** (one sentence), a **flow** (task order), **scenarios** (examples of what ships). Not the frozen `MILESTONE.md`. On their reaction, draft `MILESTONE.md` (read `scope.md`).
|
|
57
57
|
3. **Create the first task and draft its candidate specification bundle.** `new-task` is allowed pre-lock:
|
|
58
58
|
```bash
|
|
@@ -76,7 +76,7 @@ Floor: **one human approval per contract**.
|
|
|
76
76
|
|
|
77
77
|
## 4 · The one human gate — the baseline approval
|
|
78
78
|
|
|
79
|
-
Open the report with the ARC per `report-template.md`, render SHAPE then APPROVE as a guided choice, then present `SETUP-REVIEW.md` lowest-confidence-first. They confirm **once** — an explicit yes
|
|
79
|
+
Open the report with the ARC per `report-template.md`, render SHAPE then APPROVE as a guided choice, then present `SETUP-REVIEW.md` lowest-confidence-first. They confirm **once** — an explicit yes; ambient agreement is not a confirmation. **Never self-stamp a timeout — hold, or re-ask.** On that recorded confirmation, you run the lock:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
82
|
python3 .add/tooling/add.py lock --by "<name>"
|
|
@@ -14,7 +14,7 @@ Then mark `Status: FROZEN @ v1`. Generate a mock + contract tests so dependent
|
|
|
14
14
|
work can start before the real code exists.
|
|
15
15
|
</output_format>
|
|
16
16
|
|
|
17
|
-
**The freeze is the one approval.** Present the bundle **lowest-confidence first**: the 1–2 points most likely wrong (`⚠ [spec|scenario|contract|test] … — because …; if wrong: …`). Open with the ARC per `report-template.md`, rendering SHAPE then the freeze APPROVE as a guided choice (recommended pick +
|
|
17
|
+
**The freeze is the one approval.** Present the bundle **lowest-confidence first**: the 1–2 points most likely wrong (`⚠ [spec|scenario|contract|test] … — because …; if wrong: …`). Open with the ARC per `report-template.md`, rendering SHAPE then the freeze APPROVE as a guided choice (recommended pick + alternatives) — **render before `FROZEN`, then record `Reported: yes` in §3; never on a timeout.** See `run.md`. The approval also freezes §5 **Scope (may touch)** + Strategy.
|
|
18
18
|
|
|
19
19
|
## The freeze review checklist
|
|
20
20
|
|
|
@@ -19,6 +19,8 @@ Fill the §6 **Build expectations** block BEFORE Build: OBSERVABLE outcomes deri
|
|
|
19
19
|
- [ ] Coverage did not decrease.
|
|
20
20
|
- [ ] No test or contract was altered during build.
|
|
21
21
|
- [ ] Every §6 Build expectation is confirmed by real evidence (not just a green test).
|
|
22
|
+
- [ ] §1 rules trace to §2/§4 — an untraced rule is a coverage gap (`add.py audit`'s `rule_coverage_gap`; `check` has detail).
|
|
23
|
+
- [ ] every §3-cited symbol still resolves in the CURRENT tree, not just Ground SHA (§6 Live-verify evidence catches a stale/moved anchor here, not later).
|
|
22
24
|
|
|
23
25
|
If any is false, stop and return to Build.
|
|
24
26
|
|
|
@@ -42,7 +44,7 @@ A green suite proves tests pass — not that the build EARNED them. Three judgme
|
|
|
42
44
|
|
|
43
45
|
## Record exactly one outcome (no silent pass)
|
|
44
46
|
|
|
45
|
-
Present this gate via `report-template.md`'s ARC, render APPROVE
|
|
47
|
+
Present this gate via `report-template.md`'s ARC, render APPROVE, and reconcile FLAGS with `add.py report --decide`'s open-item count. **Human-led: render before `gate` and record `Reported: yes` in §6, never self-stamp.**
|
|
46
48
|
|
|
47
49
|
| Outcome | When |
|
|
48
50
|
|---------|------|
|
|
@@ -125,4 +125,5 @@ APPROVE <the question>
|
|
|
125
125
|
- **The question is a summary, never the artifact.** A compact SUMMARY · FLAGS block sits in chat immediately before the ask; the question text itself is two lines at most — intent + what "yes" means + flag count — pointing at the report above.
|
|
126
126
|
- **NEXT is not a second gate.** The single decision stays in APPROVE; NEXT is ranked recommendations only.
|
|
127
127
|
- **DECIDED never holds a gate-class call.** Security / residue / lowered-autonomy calls escalate in APPROVE.
|
|
128
|
+
- **Recorded, not just performed.** Rendering this template at a gate is recorded, not assumed — TASK.md's `Reported: yes` (§3/§6) is the mechanical trace; `add.py audit` surfaces an unrecorded one (`contract_report_unrecorded` / `verify_report_unrecorded`), a spot-audit the backstop.
|
|
128
129
|
</constraints>
|
package/skill/add/run.md
CHANGED
|
@@ -70,6 +70,7 @@ recorded pass is an explicit pass, not a skip).
|
|
|
70
70
|
- **The deep check (every gate, do not skim).** If the task produced code, record that every new symbol is referenced (wiring) and that no new dead/unused code was introduced. If it produced prose or non-code, record a semantic read. An unfilled deep check is a **shallow verify**, not an auto-PASS.
|
|
71
71
|
- **The recorded refute-read (under `auto`).** The earned-green refute-read (`6-verify.md`) is not just run — its **verdict is recorded** in §6 (`EARNED | NOT-EARNED`); `add.py audit` surfaces an unrecorded one as `refute_unrecorded` — one of three shape lints it lists (with `shallow_deep_check` + `risk_unset`) — and a human spot-audit is the backstop. NOT-EARNED routes to `add.py heal`, never an auto-PASS.
|
|
72
72
|
- **The recorded Advisor 3-lens verdict (under `auto`).** The Advisor 3-lens sweep (security → concurrency → architecture, `6-verify.md`) is recorded in §6 `### Advisor 3-lens verdict`; `add.py audit` surfaces an unfilled block as `advisor_verdict_unrecorded` — a shape lint alongside `refute_unrecorded`.
|
|
73
|
+
- **The rendered gate report (§3/§6).** Report-template.md's ceremony is recorded, not just performed — a `Reported: yes` line in §3/§6; `add.py audit` surfaces an unrecorded one as `contract_report_unrecorded`/`verify_report_unrecorded`; a human spot-audit is the backstop.
|
|
73
74
|
- **The `advisor-gate-relax` pathway.** A `risk: high` + `sensitivity: mechanical` task whose §6 Advisor 3-lens verdict records Verdict `PASS` and Residue `none` may auto-complete via `add.py gate PASS` **without** a lowered autonomy level. Security and every non-mechanical sensitivity class are never relaxed by this pathway — the high-risk guard still applies.
|
|
74
75
|
- **Always escalates to a human (never auto-passed):** any **security** finding (HARD-STOP, always);
|
|
75
76
|
a **concurrency**/timing risk the tests cannot exercise; an **architecture**/layering violation;
|
package/skill/add/streams.md
CHANGED
|
@@ -55,6 +55,9 @@ tier hint: top → dag-scheduler, setup-run-mode; mid → the rest
|
|
|
55
55
|
- **Spend your strongest model on the critical path.** Critical-path tasks gate the most
|
|
56
56
|
downstream work; off-path tasks take **mid**. The tier hint is advisory — override when you
|
|
57
57
|
know a task is harder than its position suggests.
|
|
58
|
+
- **Prefer the named roster per worker** — `add-build` for a tests/build-phase worker, `add-design`
|
|
59
|
+
for design-phase, `add-verify` for verify-phase (full roster + when-to-spawn in advisor.md) — over
|
|
60
|
+
a generic ad-hoc spawn.
|
|
58
61
|
- **`--json`** (`{ milestone, waves, critical_path, critical_path_len, tiers, blocked }`) feeds
|
|
59
62
|
a runner that spawns programmatically. `blocked` lists tasks whose dep cannot be satisfied
|
|
60
63
|
within this milestone; a `dependency_cycle` is refused with the offending members named.
|
|
@@ -95,6 +98,10 @@ floor never drops to zero (`run.md:22`). Do not engineer around it.
|
|
|
95
98
|
gates this (`engine-merge-base-enforcement`): `add.py wave-verify` before the first merge-back
|
|
96
99
|
refuses a mismatched/pending echo (`unverified_fork_base`) or off-template ledger
|
|
97
100
|
(`wave_ledger_malformed`); `add.py check` is the standing monitor.
|
|
101
|
+
- **Materialize gitignored engine content** — `git worktree add` checks out TRACKED files only;
|
|
102
|
+
`.add/tooling` (engine) and `.add/docs` (book) are gitignored and will be ABSENT even when the
|
|
103
|
+
worktree's HEAD matches — copy them in before the worker's first `add.py` call, or its
|
|
104
|
+
`phase`/`advance` commands have no engine to run at all (confirmed 3-for-3 this session).
|
|
98
105
|
- **Lease + timeout** — record which worker holds which task (wave ledger); a dead worker releases
|
|
99
106
|
its claim back to READY.
|
|
100
107
|
- **Failure isolates** — a worker's STOP-and-escalate blocks only its own task; siblings run on, the
|
package/tooling/add.py
CHANGED
|
@@ -3055,6 +3055,30 @@ def cmd_check(args: argparse.Namespace) -> None:
|
|
|
3055
3055
|
f".add/design/captures/{_pname}.<png|svg|…> — render + confirm it "
|
|
3056
3056
|
"before build (design.md beat 4)"))
|
|
3057
3057
|
|
|
3058
|
+
# roster-uninstalled (roster-install-drift): the ADD-managed guideline block cites the agent
|
|
3059
|
+
# roster ("agents/*.md" tail — matches both the shipped `add-method/agents/*.md` attribution
|
|
3060
|
+
# citation and any older phrasing) but the project may have no roster installed at all — never
|
|
3061
|
+
# shipped in the package, synced before this fix, or from a build that regressed the agents/
|
|
3062
|
+
# tree — a dead reference with no signal anywhere. WARN, never red (measure-not-block);
|
|
3063
|
+
# presence-gated on the citation itself — a project whose guideline files don't cite a roster
|
|
3064
|
+
# at all is silently exempt, never retro-flagged.
|
|
3065
|
+
_project_root = root.parent
|
|
3066
|
+
_cites_roster = False
|
|
3067
|
+
for _gname in GUIDELINE_FILES:
|
|
3068
|
+
try:
|
|
3069
|
+
if "agents/*.md" in (_project_root / _gname).read_text(encoding="utf-8"):
|
|
3070
|
+
_cites_roster = True
|
|
3071
|
+
break
|
|
3072
|
+
except OSError:
|
|
3073
|
+
pass
|
|
3074
|
+
if _cites_roster:
|
|
3075
|
+
_agents_dir = _project_root / ".claude" / "agents"
|
|
3076
|
+
if not (_agents_dir.is_dir() and any(_agents_dir.glob("add-*.md"))):
|
|
3077
|
+
warnings.append(("roster_uninstalled",
|
|
3078
|
+
"guideline file(s) cite the agent roster but no `.claude/agents/"
|
|
3079
|
+
"add-*.md` files are installed — run `add.py update` (or re-run the "
|
|
3080
|
+
"CLI installer) to materialize them"))
|
|
3081
|
+
|
|
3058
3082
|
passed = sum(1 for ok, _, _ in checks if ok)
|
|
3059
3083
|
failed = len(checks) - passed
|
|
3060
3084
|
if as_json:
|
|
@@ -6037,6 +6061,21 @@ def _freeze_skip_notices(state: dict) -> list[dict]:
|
|
|
6037
6061
|
# region only (mirrors _RISK_HIGH_RE's anchoring so a title substring can never look like one).
|
|
6038
6062
|
_RISK_ANY_RE = re.compile(r"(?:^|·)[ \t]*risk:[ \t]*\S", re.MULTILINE)
|
|
6039
6063
|
|
|
6064
|
+
# A single `Reported:` line (report-rendered-trace) — scoped to ONE phase body at a time by the
|
|
6065
|
+
# caller (bodies.get(3, "")/bodies.get(6, "")), so §3 and §6 never cross-contaminate each other.
|
|
6066
|
+
_REPORTED_LINE_RE = re.compile(r"(?m)^Reported:[ \t]*(.*)$")
|
|
6067
|
+
|
|
6068
|
+
|
|
6069
|
+
def _reported_unrecorded(body_text: str) -> bool:
|
|
6070
|
+
"""True iff a 'Reported:' line is PRESENT but still an unfilled `<...>` placeholder or blank.
|
|
6071
|
+
ABSENT line -> False (grandfathered — a pre-existing task's TASK.md predates this template
|
|
6072
|
+
field), mirroring _section_unfilled's absent-block convention."""
|
|
6073
|
+
m = _REPORTED_LINE_RE.search(body_text)
|
|
6074
|
+
if m is None:
|
|
6075
|
+
return False
|
|
6076
|
+
val = m.group(1).strip()
|
|
6077
|
+
return (not val) or bool(re.fullmatch(r"<[^>\n]*>", val))
|
|
6078
|
+
|
|
6040
6079
|
|
|
6041
6080
|
def _guarantee_lint_notices(root: Path, state: dict) -> dict:
|
|
6042
6081
|
"""PRESENCE-ONLY, MEASURE-NOT-BLOCK lints SURFACED (never failed-on) by `add.py audit`
|
|
@@ -6049,16 +6088,31 @@ def _guarantee_lint_notices(root: Path, state: dict) -> dict:
|
|
|
6049
6088
|
refute-record, M4) — the earned-green verdict the AI must record under
|
|
6050
6089
|
`auto`; ABSENT block grandfathers exactly like shallow. MEASURE-NOT-BLOCK:
|
|
6051
6090
|
never auto-blocks a gate, only surfaced here for review + a human spot-audit.
|
|
6052
|
-
|
|
6091
|
+
rule_coverage_gap[]= this task's OWN §1 Must/Reject IDs have >=1 rule with no §2 scenario tag
|
|
6092
|
+
and no §4 `covers:` line (_rule_coverage_gaps — same opt-in-by-tag-presence
|
|
6093
|
+
grandfather as `add.py check`'s whole-project sweep) — surfaced the moment
|
|
6094
|
+
THIS task reaches verify, not only via a separate `check` invocation someone
|
|
6095
|
+
has to remember to run; `check` still owns the per-rule detail.
|
|
6096
|
+
contract_report_unrecorded[] = §3's `Reported:` line present-but-unfilled (report-rendered-
|
|
6097
|
+
trace) — the freeze report (banner/ARC/SHAPE) was cited by the guide but
|
|
6098
|
+
never recorded as rendered; ABSENT line grandfathers a pre-field task.
|
|
6099
|
+
verify_report_unrecorded[] = §6's `Reported:` line present-but-unfilled — the gate report
|
|
6100
|
+
(banner/ARC) was never recorded as rendered; same grandfather rule.
|
|
6101
|
+
Honest visibility for six verify guarantees; NEVER a finding (audit stays exit 0). PURE — reads
|
|
6053
6102
|
TASK.md + state only, writes nothing."""
|
|
6054
6103
|
shallow, risk_unset, refute_unrecorded, sensitivity_unset = [], [], [], []
|
|
6055
6104
|
advisor_verdict_unrecorded = []
|
|
6056
6105
|
advisor_reviewer_is_author = []
|
|
6057
6106
|
advisor_residue_on_mechanical_mis_tier = []
|
|
6107
|
+
rule_coverage_gap = []
|
|
6108
|
+
contract_report_unrecorded = []
|
|
6109
|
+
verify_report_unrecorded = []
|
|
6058
6110
|
for slug in sorted(state.get("tasks") or {}):
|
|
6059
6111
|
if (state["tasks"][slug] or {}).get("phase") not in ("verify", "observe", "done"):
|
|
6060
6112
|
continue
|
|
6061
|
-
|
|
6113
|
+
bodies = _raw_phase_bodies(root, slug)
|
|
6114
|
+
body3 = bodies.get(3, "")
|
|
6115
|
+
body6 = bodies.get(6, "")
|
|
6062
6116
|
hdr = _task_header(root, slug)
|
|
6063
6117
|
if _section_unfilled(body6, "### Deep checks"):
|
|
6064
6118
|
shallow.append(slug)
|
|
@@ -6066,12 +6120,21 @@ def _guarantee_lint_notices(root: Path, state: dict) -> dict:
|
|
|
6066
6120
|
refute_unrecorded.append(slug)
|
|
6067
6121
|
if _section_unfilled(body6, "### Advisor 3-lens verdict"):
|
|
6068
6122
|
advisor_verdict_unrecorded.append(slug)
|
|
6123
|
+
if _reported_unrecorded(body3):
|
|
6124
|
+
contract_report_unrecorded.append(slug)
|
|
6125
|
+
if _reported_unrecorded(body6):
|
|
6126
|
+
verify_report_unrecorded.append(slug)
|
|
6069
6127
|
if not _RISK_ANY_RE.search(hdr):
|
|
6070
6128
|
risk_unset.append(slug)
|
|
6071
6129
|
# sensitivity_unset (risk-sensitivity-taxonomy): a verify-reached task with no
|
|
6072
6130
|
# human-declared sensitivity — MEASURE-NOT-BLOCK, same class as risk_unset.
|
|
6073
6131
|
if _task_sensitivity(hdr) is None:
|
|
6074
6132
|
sensitivity_unset.append(slug)
|
|
6133
|
+
# rule_coverage_gap (verify-traceability-glint): this task's own §1 Must/Reject vs
|
|
6134
|
+
# §2/§4 tags — the SAME predicate `check` uses project-wide, scoped to just this task
|
|
6135
|
+
# and surfaced right at verify, not only via a separate whole-project sweep.
|
|
6136
|
+
if _rule_coverage_gaps(bodies.get(1, ""), bodies.get(2, ""), bodies.get(4, "")):
|
|
6137
|
+
rule_coverage_gap.append(slug)
|
|
6075
6138
|
# advisor_reviewer_is_author / advisor_residue_on_mechanical_mis_tier
|
|
6076
6139
|
# (advisor-verdict-audit): MEASURE-NOT-BLOCK lints on the filled advisor block.
|
|
6077
6140
|
# Both require the block to be PRESENT AND FILLED (not just unfilled).
|
|
@@ -6105,7 +6168,10 @@ def _guarantee_lint_notices(root: Path, state: dict) -> dict:
|
|
|
6105
6168
|
"sensitivity_unset": sensitivity_unset,
|
|
6106
6169
|
"advisor_verdict_unrecorded": advisor_verdict_unrecorded,
|
|
6107
6170
|
"advisor_reviewer_is_author": advisor_reviewer_is_author,
|
|
6108
|
-
"advisor_residue_on_mechanical_mis_tier": advisor_residue_on_mechanical_mis_tier
|
|
6171
|
+
"advisor_residue_on_mechanical_mis_tier": advisor_residue_on_mechanical_mis_tier,
|
|
6172
|
+
"rule_coverage_gap": rule_coverage_gap,
|
|
6173
|
+
"contract_report_unrecorded": contract_report_unrecorded,
|
|
6174
|
+
"verify_report_unrecorded": verify_report_unrecorded}
|
|
6109
6175
|
|
|
6110
6176
|
|
|
6111
6177
|
def cmd_audit(args: argparse.Namespace) -> None:
|
|
@@ -6142,6 +6208,11 @@ def cmd_audit(args: argparse.Namespace) -> None:
|
|
|
6142
6208
|
av = glints["advisor_verdict_unrecorded"]
|
|
6143
6209
|
print(f"audit: advisor_verdict_unrecorded — {len(av)} task(s): {', '.join(av)} "
|
|
6144
6210
|
f"— record the 3-lens advisor verdict (§6); a spot-audit is the backstop")
|
|
6211
|
+
if glints["rule_coverage_gap"]:
|
|
6212
|
+
rc = glints["rule_coverage_gap"]
|
|
6213
|
+
print(f"audit: rule_coverage_gap — {len(rc)} task(s): {', '.join(rc)} "
|
|
6214
|
+
f"— a §1 Must/Reject has no §2 scenario tag or §4 covers: line "
|
|
6215
|
+
f"(run `add.py check` for the per-rule detail)")
|
|
6145
6216
|
if glints["sensitivity_unset"]:
|
|
6146
6217
|
su = glints["sensitivity_unset"]
|
|
6147
6218
|
print(f"audit: sensitivity_unset — {len(su)} task(s) reached verify with no "
|
|
@@ -6155,11 +6226,22 @@ def cmd_audit(args: argparse.Namespace) -> None:
|
|
|
6155
6226
|
print(f"audit: advisor_residue_on_mechanical_mis_tier — {len(am)} task(s): "
|
|
6156
6227
|
f"{', '.join(am)} — mechanical tier with non-none residue and PASS verdict "
|
|
6157
6228
|
f"is incoherent; consider re-tiering")
|
|
6229
|
+
if glints["contract_report_unrecorded"]:
|
|
6230
|
+
cr = glints["contract_report_unrecorded"]
|
|
6231
|
+
print(f"audit: contract_report_unrecorded — {len(cr)} task(s): {', '.join(cr)} "
|
|
6232
|
+
f"— record the rendered freeze report (§3 `Reported: yes`); a spot-audit is the backstop")
|
|
6233
|
+
if glints["verify_report_unrecorded"]:
|
|
6234
|
+
vr = glints["verify_report_unrecorded"]
|
|
6235
|
+
print(f"audit: verify_report_unrecorded — {len(vr)} task(s): {', '.join(vr)} "
|
|
6236
|
+
f"— record the rendered gate report (§6 `Reported: yes`); a spot-audit is the backstop")
|
|
6158
6237
|
if not findings and not skips and not glints["shallow"] and not glints["risk_unset"] \
|
|
6159
6238
|
and not glints["refute_unrecorded"] and not glints["advisor_verdict_unrecorded"] \
|
|
6160
6239
|
and not glints["sensitivity_unset"] \
|
|
6161
6240
|
and not glints["advisor_reviewer_is_author"] \
|
|
6162
|
-
and not glints["advisor_residue_on_mechanical_mis_tier"]
|
|
6241
|
+
and not glints["advisor_residue_on_mechanical_mis_tier"] \
|
|
6242
|
+
and not glints["rule_coverage_gap"] \
|
|
6243
|
+
and not glints["contract_report_unrecorded"] \
|
|
6244
|
+
and not glints["verify_report_unrecorded"]:
|
|
6163
6245
|
print(f"audit: clean ({checked} tasks checked)")
|
|
6164
6246
|
# MEASURE-NOT-BLOCK: only real findings raise the exit code; notices never do.
|
|
6165
6247
|
if findings:
|
|
@@ -82,6 +82,7 @@ Schema: <tables/fields touched, and access pattern>
|
|
|
82
82
|
|
|
83
83
|
Glossary deltas: <new domain term(s) this task introduces, `Term: definition` — or "none">
|
|
84
84
|
Status: DRAFT
|
|
85
|
+
Reported: <yes — the freeze report (banner/ARC/SHAPE) rendered before this froze | no>
|
|
85
86
|
<!-- The freeze IS the one approval — lead it with the bundle's lowest-confidence flag: the 1–2
|
|
86
87
|
points most likely wrong across the whole bundle, tagged [spec|scenario|contract|test], each
|
|
87
88
|
with why + cost (the §1 ⚠ assumptions feed it; a flag may point at a scenario or the contract
|
|
@@ -117,6 +118,7 @@ Scope (may touch): `./src/` <fill before the §3 freeze — every file the bui
|
|
|
117
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>
|
|
118
119
|
|
|
119
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>
|
|
121
|
+
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>
|
|
120
122
|
Known-problem fixes: <trap → planned fix — the failure modes this build must dodge; guidance, not enforced>
|
|
121
123
|
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>
|
|
122
124
|
Safety rule (feature-specific): <e.g. debit+credit in one atomic transaction>
|
|
@@ -187,6 +189,7 @@ Residue: <none | summary>
|
|
|
187
189
|
Binding: <yes — mechanical | advisory — <sensitivity>>
|
|
188
190
|
|
|
189
191
|
### GATE RECORD
|
|
192
|
+
Reported: <yes — the gate report (banner/ARC) rendered before this outcome recorded | no>
|
|
190
193
|
Outcome: <PASS | RISK-ACCEPTED | HARD-STOP>
|
|
191
194
|
If RISK-ACCEPTED -> owner: <name> · ticket: <link> · expires: <date> (never for a security gap)
|
|
192
195
|
Reviewed by: <name> · date: <date>
|