baldart 4.92.1 → 4.93.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 +11 -0
- package/VERSION +1 -1
- package/framework/.claude/agents/plan-auditor.md +9 -8
- package/framework/.claude/agents/prd-card-writer.md +8 -0
- package/framework/.claude/skills/prd/CHANGELOG.md +13 -0
- package/framework/.claude/skills/prd/SKILL.md +28 -7
- package/framework/.claude/skills/prd/references/audit-phase.md +33 -130
- package/framework/.claude/skills/prd/references/audit-teammate-prompt.md +144 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +28 -11
- package/framework/.claude/skills/prd/references/validation-phase.md +53 -57
- package/framework/scripts/stamp-holistic-audit.js +143 -0
- package/framework/scripts/validate-card-baseline.js +51 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to BALDART will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.93.0] - 2026-07-02
|
|
9
|
+
|
|
10
|
+
**`/prd` gate-hygiene wave — dai driver misurati (forensics di una sessione reale: 61.6M token, 48% del costo in fasi meccaniche, una famiglia di card con zero audit-trail, 96% del wall-clock in attesa umana).** Sette interventi chirurgici, nessuna nuova chiave di config:
|
|
11
|
+
|
|
12
|
+
- **Tool di misura onesto**: `scripts/new-session-audit.mjs` deduplica l'usage per `message.id` (sovracontava ~2.85× — 175M riportati vs 61.6M reali su FEAT-0061) e riconosce le sessioni `/prd` (`mode=prd`, anche quando la skill è invocata via Skill tool dopo un preambolo; backtrack `self`; confronto col reference FEAT-0041 saltato).
|
|
13
|
+
- **Severità assoluta evidence-anchored** (`prd` skill 1.2.0 + `plan-auditor`): via la quota posizionale "top 20% → HIGH must-apply" — fabbricava cicli di apply su piani puliti e demotava finding reali sui batch grandi. Zero-finding è un esito legittimo.
|
|
14
|
+
- **Validation eseguita, non recitata**: `validate-card-baseline.js --prd` (requirements condizionale PO1, epic `skip`, marker `[NEEDS CLARIFICATION]` bloccanti) + nuovo `framework/scripts/stamp-holistic-audit.js` (stamp `metadata.holistic_audit` deterministico, idempotente; chiamato da 6.9.4 e dal backstop 5b — i due gate-prosa che erano falliti insieme su FEAT-0064).
|
|
15
|
+
- **Context economy dell'orchestratore /prd**: contratto teammate audit estratto in `references/audit-teammate-prompt.md` e passato BY PATH (mai caricato dall'orchestratore; ~−9KB da audit-phase + regola generale per gli spec agent-facing incl. `api-perf-gate.md`); analisi mockup su file delegata a `ui-expert` read-only (misurati ~208KB di mockup in cache per ~300 eventi); Progress Bar solo ai phase-gate con riga compatta intra-fase (~20 righe × 30+ messaggi risparmiate; unifica la policy Codex già esistente).
|
|
16
|
+
|
|
17
|
+
**MINOR** — capability change su skill/agent/script condivisi; nessun cambio di layout install. **No new `baldart.config.yml` key** → la schema-change propagation rule NON si applica. **Codex parity: portable** (script node zero-dep; prosa skill condivisa ai due runtime; il branch file-backed Codex del teammate contract è documentato in-file).
|
|
18
|
+
|
|
8
19
|
## [4.92.1] - 2026-07-02
|
|
9
20
|
|
|
10
21
|
**Review avversariale post-release del motore new2 (coerenza interna).** Tre reviewer indipendenti a contesto fresco (dataflow / edge-case / contratti) sull'onda v4.90–4.92; ogni finding ri-verificato contro il codice prima di agire: **11 confermati e fixati, 9 refutati con evidenza empirica** (tra i refutati: la "race" del worker-pool adattivo — simulata: 0 buchi; `verify-item.sh` su bash 3.2 reale — funziona; `parseInt` con CRLF; un CAP attribuito a `new2.js` che non esiste).
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.93.0
|
|
@@ -348,14 +348,15 @@ Consider:
|
|
|
348
348
|
|
|
349
349
|
## SEVERITY CALIBRATION (after challenge pass)
|
|
350
350
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
351
|
+
Severity is **ABSOLUTE, per finding, anchored to its evidenced consequence** — never a relative ranking, never a quota. (Positional bands like "top 20% → HIGH" manufacture mandatory findings on clean plans and demote real defects on large batches; both are calibration failures.)
|
|
352
|
+
|
|
353
|
+
For each surviving finding ask: *"what happens if the card is implemented exactly as written?"*
|
|
354
|
+
|
|
355
|
+
- **HIGH** (must fix before implementation) — implementing the card as written produces a defect: data loss, security bypass, breaking change, claimed_path collision, ADR required missing, wrong behavior against a stated AC, an unimplementable AC (e.g. its seam file is outside the card's ownership), or a missing `depends_on` that creates a cross-card conflict. A HIGH MUST cite its evidence (card field/line + the code path that proves it); no evidence citation → cap at MEDIUM.
|
|
356
|
+
- **MEDIUM** (should fix, mark `[MANUAL]` if ambiguous) — the card is implementable but a gap will plausibly cost a review cycle: missing doc/file in `files_likely_touched`, vague-but-inferable AC, missing validation command.
|
|
357
|
+
- **LOW** (note only, do not edit structured fields) — informational, no behavioral consequence.
|
|
358
|
+
|
|
359
|
+
Zero HIGH — or zero findings at all — on a well-formed card is a legitimate, reportable outcome. Do NOT stretch severities to fill bands.
|
|
359
360
|
|
|
360
361
|
## QUANTIFIED RISK SCORING (MANDATORY)
|
|
361
362
|
|
|
@@ -91,6 +91,14 @@ the card reaches `status: DONE`.
|
|
|
91
91
|
|
|
92
92
|
6. **Populate `data_fields`** for every card that reads/writes the persistence layer (see Required Fields).
|
|
93
93
|
|
|
94
|
+
**Never guess silently — mark the ambiguity.** The grounding rule generalizes beyond field
|
|
95
|
+
names: whenever card material forces you to GUESS (an unresolved behavior, an unspecified
|
|
96
|
+
control/state, a value the PRD/Discovery never pinned down), do NOT pick a plausible answer
|
|
97
|
+
silently — write `[NEEDS CLARIFICATION: <the exact question>]` inline at the ambiguous spot.
|
|
98
|
+
The `/prd` validation gate (`validate-card-baseline.js --prd`) BLOCKS commit while any marker
|
|
99
|
+
is unresolved, so the ambiguity reaches a human instead of shipping as a hidden assumption.
|
|
100
|
+
A marker is a legitimate intermediate state, never a defect — the defect is the silent guess.
|
|
101
|
+
|
|
94
102
|
## Mission
|
|
95
103
|
|
|
96
104
|
You receive:
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.2.0 — 2026-07-02
|
|
6
|
+
|
|
7
|
+
**Gate-hygiene wave** — dai driver misurati sul forensics di una sessione /prd reale (61.6M token; 48% del costo in fasi meccaniche a piena finestra; una famiglia di card spedita con zero audit-trail; ~20 righe di progress bar × 30+ messaggi):
|
|
8
|
+
|
|
9
|
+
- **Severità assoluta evidence-anchored** (`audit-phase.md` § Severity Calibration + `plan-auditor.md`): rimossa la calibrazione a quota posizionale ("top 20% → HIGH must-apply") che fabbricava finding HIGH su piani puliti e demotava finding reali sui batch grandi. HIGH ora richiede citazione dell'evidenza; zero-finding è un esito legittimo.
|
|
10
|
+
- **Validation ESEGUITA, non recitata** (`validation-phase.md` Step 6 item 0-i): il gate deterministico gira via `validate-card-baseline.js --prd` (enum, requirements condizionale PO1, epic `review_profile: skip`, matrice campi) — i check semantici restano in prosa (0-ii). Nuovo check bloccante: marker `[NEEDS CLARIFICATION: …]` irrisolti (l'ambiguità di planning è un artefatto gated, mai un'assunzione silenziosa; `prd-card-writer` istruito a marcare invece di indovinare).
|
|
11
|
+
- **Stamp holistic deterministico** (`framework/scripts/stamp-holistic-audit.js`, nuovo, zero-dep): `metadata.holistic_audit` scritto da script idempotente/additivo/epic-skipping/self-verifying, chiamato da `audit-phase.md` 6.9.4 E dal backstop `validation-phase.md` 5b (entrambi i call-site erano prosa e sono falliti INSIEME su un run reale).
|
|
12
|
+
- **Prompt agent-facing passati BY PATH** (context economy, ~−30KB dal contesto orchestratore): il contratto teammate dell'audit estratto in `references/audit-teammate-prompt.md` (letto dai teammate, MAI dall'orchestratore); `api-perf-gate.md` dichiarato agent-facing nella flow table (era già delegato, ma la regola "read before each phase" lo faceva caricare comunque); regola generale in SKILL.md.
|
|
13
|
+
- **Mockup mai nel contesto orchestratore** (`discovery-phase.md` 1.6.5): l'analisi mockup su file è DELEGATA a `ui-expert` read-only (ritorna solo lo YAML `mockup_analysis`); misurati ~208KB di mockup decodificato trascinati in cache per ~300 eventi. Eccezione: immagini incollate in chat (già in contesto).
|
|
14
|
+
- **Progress Bar ai phase-gate** (SKILL.md HARD RULE 11): tabella completa solo alle transizioni di fase; riga compatta sugli STOP intra-fase; niente sui messaggi tecnici — adottata su Claude la policy già scritta per Codex (unificazione, non fork).
|
|
15
|
+
|
|
16
|
+
Codex parity: portable (script node zero-dep + prosa skill condivisa; il branch Codex file-backed del teammate contract è documentato nel file stesso).
|
|
17
|
+
|
|
5
18
|
## 1.1.2 — 2026-07-01
|
|
6
19
|
|
|
7
20
|
- Context economy: trimmed the HARD-RULES **Runtime portability** citation to a one-liner (cite, don't restate — the detail lives in `runtime-portability-protocol.md`). ~330 chars off the SKILL.md core, which sits in context every turn. No behavior change; RULE 4/11 Codex branches unchanged.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prd
|
|
3
3
|
effort: high
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
description: "Structured PRD creation skill. Use when the user says /prd, 'crea un prd', 'nuova funzionalita', 'pianifica feature', or wants to plan a new feature end-to-end. Also handles /prd-add, 'aggiungi requisito', 'serve anche', 'manca un endpoint' for change requests on active PRD sessions — runs ICIAS impact analysis to determine which phases need SKIP/PATCH/REDO. Produces PRD + UI design + backlog cards, all validated and committed. Multi-turn conversation: asks questions, waits for answers, iterates through discovery, design, spec writing, card creation, and validation phases."
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -46,14 +46,21 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
46
46
|
| 2.5 | Research (opzionale) | [research-phase.md](references/research-phase.md) |
|
|
47
47
|
| 3 | UI Design (3a-3d) | [ui-design-phase.md](references/ui-design-phase.md) |
|
|
48
48
|
| 4 | Write PRD | [prd-writing-phase.md](references/prd-writing-phase.md) |
|
|
49
|
-
| 4.5 | API Performance Gate | [api-perf-gate.md](references/api-perf-gate.md) |
|
|
49
|
+
| 4.5 | API Performance Gate | [api-perf-gate.md](references/api-perf-gate.md) — **agent-facing**: passed BY PATH to `api-perf-cost-auditor` (spawn per prd-writing-phase § 4.5); the orchestrator does NOT read it |
|
|
50
50
|
| 4b | Confirm Specs (§ "Present and Confirm") | [prd-writing-phase.md](references/prd-writing-phase.md) |
|
|
51
51
|
| 5 | Backlog Cards | [backlog-phase.md](references/backlog-phase.md) |
|
|
52
52
|
| 6 | Quality Audit | [validation-phase.md](references/validation-phase.md) |
|
|
53
53
|
| 7 | Resolution, Commit & Merge | [validation-phase.md](references/validation-phase.md) |
|
|
54
54
|
| 7.6 | Obsidian back-reference — FINAL (upgrades the 1.2 provisional block; only if a spec note was given) | [validation-phase.md](references/validation-phase.md) + [obsidian-backref.md](references/obsidian-backref.md) |
|
|
55
55
|
|
|
56
|
-
**Before starting each phase, read the corresponding reference file
|
|
56
|
+
**Before starting each phase, read the corresponding reference file** — with ONE
|
|
57
|
+
exception class: **agent-facing specs are passed BY PATH to the agent that consumes
|
|
58
|
+
them and MUST NOT be loaded into the orchestrator context.** Today these are
|
|
59
|
+
`references/api-perf-gate.md` (read by `api-perf-cost-auditor` at Step 4.5 and at the
|
|
60
|
+
Step 6 audit) and `references/audit-teammate-prompt.md` (read by each Step 6.6 audit
|
|
61
|
+
teammate). Rationale: a spec loaded by a subagent costs one cheap Read in a disposable
|
|
62
|
+
context; the same spec in the orchestrator rides EVERY subsequent full-window API call
|
|
63
|
+
for the rest of the run.
|
|
57
64
|
|
|
58
65
|
---
|
|
59
66
|
|
|
@@ -73,7 +80,17 @@ message. You ask questions, wait for answers, and iterate.
|
|
|
73
80
|
**Worktree-mode exception (HARD RULE 17):** when the PRD runs inside a docs worktree (default since v3.22.0), the session-start update is **SKIPPED** — writing `project-status.md` in the worktree wouldn't be visible to other terminals working on the main repo, and writing it on the main repo would pollute `git status` for parallel sessions (exactly what worktree isolation is meant to prevent). The at-commit update still applies and runs inside the worktree as the last write before rebase + merge — see [validation-phase.md](references/validation-phase.md) § Step 7.
|
|
74
81
|
10. **STOP means STOP.** When you see `STOP`, end your message. Do not make more tool
|
|
75
82
|
calls. Do not continue to the next step. Wait for the user.
|
|
76
|
-
11. **Progress visibility
|
|
83
|
+
11. **Progress visibility — at PHASE GATES, not on every message (context economy,
|
|
84
|
+
unified with the Codex policy).** Display the FULL Progress Bar table only at
|
|
85
|
+
**phase transitions**: entering/completing a numbered phase (1.6, 2, 2.5, 3, 4,
|
|
86
|
+
4b, 5, 6, 7) and on the final message. On intra-phase STOP messages (e.g. each
|
|
87
|
+
discovery question) end with ONE compact line instead:
|
|
88
|
+
`📋 <slug> · Fase <N> <nome> · <indicatore> · Prossimo: <cosa>` (for discovery
|
|
89
|
+
the indicator is `X/11 dimensioni`). Technical/tool messages carry neither.
|
|
90
|
+
The state file is updated after each phase either way and stays the recovery
|
|
91
|
+
SSOT. (Measured driver: ~20 lines × 30+ messages of orchestrator output per
|
|
92
|
+
run bought no signal the compact line doesn't carry.) The discovery **user
|
|
93
|
+
gates** (one question per message) stay conversational on both runtimes.
|
|
77
94
|
12. **Scope Expansion Detection.** During discovery (Step 2), after every user answer,
|
|
78
95
|
check if the response introduces NEW structural entities (endpoints, collections,
|
|
79
96
|
pages, roles, flows) not in the original feature description. If detected, pause
|
|
@@ -297,9 +314,12 @@ the value lives only in the state file `## Worktree` section.
|
|
|
297
314
|
|
|
298
315
|
---
|
|
299
316
|
|
|
300
|
-
## PROGRESS BAR —
|
|
317
|
+
## PROGRESS BAR — AT PHASE GATES (see HARD RULE 11)
|
|
301
318
|
|
|
302
|
-
At the END of every message (
|
|
319
|
+
At the END of every **phase-transition** message (a numbered phase entered or
|
|
320
|
+
completed, and the final message), display the full table below. On intra-phase
|
|
321
|
+
STOP messages use only the compact line from HARD RULE 11; on technical/tool
|
|
322
|
+
messages display nothing.
|
|
303
323
|
|
|
304
324
|
```
|
|
305
325
|
---
|
|
@@ -324,7 +344,8 @@ Prossimo passo: <what happens next>
|
|
|
324
344
|
|
|
325
345
|
Legend: ⬜ = da fare, 🔄 = in corso, ✅ = completato, ⏭️ = saltato (N/A)
|
|
326
346
|
|
|
327
|
-
**If
|
|
347
|
+
**If a phase-transition message doesn't end with this table — or an intra-phase STOP
|
|
348
|
+
message lacks the compact line — you violated the skill rules.**
|
|
328
349
|
|
|
329
350
|
---
|
|
330
351
|
|
|
@@ -106,7 +106,7 @@ Use `TaskCreate` to create one task per audit agent per card:
|
|
|
106
106
|
|
|
107
107
|
- For **N cards × M agent types**, create **N × M tasks** (one task per agent type per card; excluding Codex plan-audit — see 6.6d). In Step 6.6c, ONE teammate agent per type handles all N tasks in its queue — M agents run in parallel, not N×M.
|
|
108
108
|
- Each task subject: `[CARD-ID] <agent-type> audit`
|
|
109
|
-
- Each task description: full card YAML + adjacent card summaries + file paths + PRD path
|
|
109
|
+
- Each task description: full card YAML + adjacent card summaries + file paths + PRD path. The audit instructions are NOT embedded — each teammate reads them from `audit-teammate-prompt.md` (see 6.6e).
|
|
110
110
|
|
|
111
111
|
**Claim-lock (atomicity on the N×M fan-out)**: each task carries a `claimed_by` field, empty at creation. Before a teammate processes a task it MUST atomically set `claimed_by: <its name>` via `TaskUpdate` and re-read; if the field is already non-empty and not its own name, it SKIPS that task (another worker owns it). This prevents two workers — for example a re-spawn after a transient failure, or an overlapping re-audit (Step 6.9) — from both processing the same card+agent pair and producing duplicate findings. The teammate workflow (Step 6.6e) embeds the claim step.
|
|
112
112
|
|
|
@@ -259,138 +259,31 @@ Suppress findings where the strongest false-positive argument is convincing.
|
|
|
259
259
|
- If `codebase-architect` was already spawned earlier this session (its summary is in the session tracker artifact registry), pass that summary in the prompt and instruct plan-auditor NOT to re-invoke `codebase-architect`.
|
|
260
260
|
- If `security_review_needed: true` (security-reviewer already ran as a teammate), instruct plan-auditor NOT to spawn `security-reviewer`; its security findings are already in the task store and will be merged at Step 6.7. plan-auditor should rely on those rather than running an independent, un-merged second review.
|
|
261
261
|
|
|
262
|
-
### 6.6e. Teammate prompt
|
|
262
|
+
### 6.6e. Teammate prompt — passed BY PATH (context economy)
|
|
263
263
|
|
|
264
|
-
|
|
264
|
+
The full teammate contract — identity, task-queue workflow (claim-lock included), the
|
|
265
|
+
per-role audit instructions, the output format with `[Target:]` tags, the challenge
|
|
266
|
+
pass, and the severity calibration — lives in
|
|
267
|
+
[audit-teammate-prompt.md](audit-teammate-prompt.md). That file is an **AGENT-FACING
|
|
268
|
+
spec**: each teammate reads it in ITS OWN context window; the orchestrator MUST NOT
|
|
269
|
+
load it into its own (in a teammate it costs one cheap Read — in the orchestrator it
|
|
270
|
+
would ride every subsequent full-window API call for the rest of the run).
|
|
265
271
|
|
|
266
|
-
|
|
267
|
-
## Identity
|
|
268
|
-
|
|
269
|
-
You are a SKEPTICAL auditor for a pre-development audit team ("check-audit").
|
|
270
|
-
Your default stance is that the card is NOT ready for implementation.
|
|
271
|
-
Do not rationalize away issues. Do not give benefit of the doubt.
|
|
272
|
-
If something COULD be a problem, flag it. The challenge pass (later) will filter false positives.
|
|
273
|
-
Your job is RECALL, not precision — catch everything, filter later.
|
|
274
|
-
|
|
275
|
-
## Your Workflow
|
|
276
|
-
|
|
277
|
-
1. Call `TaskList` to see your assigned tasks.
|
|
278
|
-
2. For each task (in ID order):
|
|
279
|
-
a. Call `TaskGet` to read the full task description (card YAML + adjacent cards + file paths).
|
|
280
|
-
a2. **Claim the task**: if `claimed_by` is already set to a name other than yours, SKIP this task (another worker owns it). Otherwise set `claimed_by: <your name>` via `TaskUpdate`, then re-read via `TaskGet` to confirm your claim held; if a different name won the race, SKIP.
|
|
281
|
-
b. Mark task as `in_progress` via `TaskUpdate`.
|
|
282
|
-
c. Read any source files or PRDs referenced in the task (use Read tool).
|
|
283
|
-
d. Perform your audit (see instructions below).
|
|
284
|
-
e. Run the Challenge Pass on your findings (see below).
|
|
285
|
-
f. Run Severity Calibration on surviving findings (see below).
|
|
286
|
-
g. **Write findings into the task description** via `TaskUpdate` — append a `## FINDINGS` section.
|
|
287
|
-
h. Send a brief notification to orchestrator via `SendMessage` (task ID + one-line summary only).
|
|
288
|
-
i. Mark task as `completed` via `TaskUpdate`.
|
|
289
|
-
3. After all tasks: send "all tasks complete" to orchestrator.
|
|
290
|
-
|
|
291
|
-
**IMPORTANT**: Always write findings to task description (step g) before notification (step h). Task description is durable; message is just a ping.
|
|
292
|
-
|
|
293
|
-
## Audit Instructions
|
|
294
|
-
|
|
295
|
-
{AGENT_SPECIFIC_INSTRUCTIONS}
|
|
296
|
-
|
|
297
|
-
## Output Format (mandatory evidence quotes)
|
|
298
|
-
|
|
299
|
-
For each card, return findings as:
|
|
300
|
-
|
|
301
|
-
### [CARD-ID] — {Agent Role} Findings
|
|
302
|
-
|
|
303
|
-
- [ ] **Finding title** — Description of the issue, risk, or gap. (Severity: HIGH/MEDIUM/LOW) [Target: <field>]
|
|
304
|
-
> **Evidence:** "<exact quote from the card YAML, PRD, or source file>"
|
|
305
|
-
> **Source:** `<file path or field name>`
|
|
306
|
-
|
|
307
|
-
**MANDATORY**: Every finding MUST include an evidence quote — a direct excerpt that grounds it. Findings without quotable evidence MUST be discarded. State: "Considered but discarded — no quotable evidence found."
|
|
308
|
-
|
|
309
|
-
If no findings: "No issues found for [CARD-ID]."
|
|
310
|
-
|
|
311
|
-
### `[Target: <field>]` tag reference (mandatory on every finding)
|
|
312
|
-
|
|
313
|
-
| Target tag | When to use |
|
|
314
|
-
|---|---|
|
|
315
|
-
| `[Target: requirements]` | Missing or wrong requirement text |
|
|
316
|
-
| `[Target: acceptance_criteria]` | Missing AC, vague AC needing rewrite |
|
|
317
|
-
| `[Target: definition_of_done]` | Missing DoD checkbox |
|
|
318
|
-
| `[Target: files_likely_touched]` | Missing file path |
|
|
319
|
-
| `[Target: depends_on]` | Missing dependency card ID |
|
|
320
|
-
| `[Target: areas]` | Missing area entry (api, docs, data, ui) |
|
|
321
|
-
| `[Target: git_strategy]` | `git_strategy: TBD` or wrong value |
|
|
322
|
-
| `[Target: unknowns]` | Unresolved unknown to surface |
|
|
323
|
-
| `[Target: existing_patterns]` | Missing or stale pattern reference |
|
|
324
|
-
| `[Target: validation_commands]` | Missing verification command |
|
|
325
|
-
| `[Target: anti_patterns]` | Missing DO NOT constraint |
|
|
326
|
-
| `[Target: scope_boundaries]` | Missing scope boundary item |
|
|
327
|
-
| `[Target: input_output_examples]` | Missing or incorrect I/O example |
|
|
328
|
-
| `[Target: error_handling]` | Missing failure mode spec |
|
|
329
|
-
| `[Target: reuse_analysis]` | Missing reuse opportunity or wrong path |
|
|
330
|
-
| `[Target: notes]` | LOW severity only — informational |
|
|
331
|
-
|
|
332
|
-
## Challenge Pass (mandatory before reporting)
|
|
333
|
-
|
|
334
|
-
After generating initial findings, challenge EACH one:
|
|
335
|
-
|
|
336
|
-
"What is the strongest argument that this is a false positive?"
|
|
337
|
-
|
|
338
|
-
Consider:
|
|
339
|
-
- Is this already handled elsewhere in the codebase?
|
|
340
|
-
- Is this a convention in this project I'm unfamiliar with?
|
|
341
|
-
- Is the card intentionally deferring this to a later card?
|
|
342
|
-
- Am I applying a generic best practice that doesn't fit this context?
|
|
343
|
-
|
|
344
|
-
**Suppress the finding if the FP argument is convincing.** Record suppressed findings:
|
|
272
|
+
Spawn each teammate with this THIN prompt (fill the placeholders, nothing more):
|
|
345
273
|
|
|
346
|
-
|
|
347
|
-
<
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
After challenge pass, rank ALL surviving findings relative to each other by impact:
|
|
354
|
-
|
|
355
|
-
1. List all surviving findings in order of impact (most impactful first).
|
|
356
|
-
2. Assign severity based on position:
|
|
357
|
-
- Top 20% → HIGH (must apply)
|
|
358
|
-
- Middle 40% → MEDIUM (should apply)
|
|
359
|
-
- Bottom 40% → LOW (notes only)
|
|
360
|
-
3. Exception: data loss, security bypass, or breaking change = automatically HIGH regardless of position.
|
|
361
|
-
|
|
362
|
-
### Severity Calibration Examples
|
|
363
|
-
|
|
364
|
-
**HIGH** (must fix before implementation):
|
|
365
|
-
- "acceptance_criteria says 'user can see <list>' but doesn't specify pagination → unbounded persistence-layer read"
|
|
366
|
-
> Evidence: "AC-2: <persona from identity.audience_segments[]> views <entity>" — no limit/pagination mentioned
|
|
367
|
-
|
|
368
|
-
**MEDIUM** (should fix, skip if ambiguous):
|
|
369
|
-
- "files_likely_touched missing the API route doc update"
|
|
370
|
-
> Evidence: files_likely_touched lists "src/app/api/v1/<domain>/route.ts" but not "${paths.references_dir}/api/<domain>.md"
|
|
371
|
-
|
|
372
|
-
**LOW** (note only):
|
|
373
|
-
- "Card title could be more descriptive"
|
|
374
|
-
> Evidence: title is "Booking API" — functional but generic
|
|
274
|
+
```
|
|
275
|
+
You are teammate "<name>" (role: <agent-type>) in team "check-audit".
|
|
276
|
+
FIRST ACTION: Read <references-path>/audit-teammate-prompt.md and follow it exactly —
|
|
277
|
+
it defines your identity, task-queue workflow (TaskList/TaskGet/claim/TaskUpdate),
|
|
278
|
+
your role's audit instructions (§ "<agent-type>"), the output format, the challenge
|
|
279
|
+
pass, and the severity calibration. Then work your task queue until every task you
|
|
280
|
+
can claim is completed.
|
|
375
281
|
```
|
|
376
282
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
**plan-auditor (card grounding)**: This is the Claude-side, codebase-grounded counterpart to the cross-model Codex plan audit (6.6d) — it brings what Codex lacks: project memory (`.claude/agent-memory/plan-auditor/MEMORY.md`) and the card-native `[Target: <field>]` tagging this audit consumes. Apply the **full BACKLOG CARD ATTACK SURFACE** (INVEST, requirements-smell detection, persistence-specific checks per `stack.database`, card-structure checks) against each card's `.yml`, reading the existing files in `files_likely_touched` to ground claims about conflicts with existing patterns, missing reuse opportunities, and convention alignment (per `identity.design_philosophy`, project lint/type-check rules, `identity.language`). Check `## Adjacent Cards` for parallel file modifications and missing `depends_on` edges.
|
|
382
|
-
|
|
383
|
-
> **Mode (team-aware — read first)**: you are running inside the coordinating `check-audit` team, so you MUST behave as in QUICK mode for *spawning* while keeping FULL card-attack-surface *coverage*:
|
|
384
|
-
> - **Do NOT spawn `codebase-architect`** for grounding — `/prd` already primed codebase context at kickoff (context-primer) and you read `files_likely_touched` directly. A re-spawn here is wasteful and forbidden.
|
|
385
|
-
> - **Do NOT auto-spawn specialists** (`security-reviewer` / `api-perf-cost-auditor` / `ui-expert`) — they run as first-class teammates in this same team whenever their signal fires (6.6c). Consume their findings at merge time; never double-spawn.
|
|
386
|
-
> - **Output-format override** (symmetric with the security-reviewer / api-perf-cost-auditor overrides above): produce findings in the teammate contract format (`### [CARD-ID] — Plan Audit Findings` with `[Target: <field>]` tags written to a `## FINDINGS` section via `TaskUpdate`) — NOT plan-auditor's native 10-section OUTPUT FORMAT (Hardened Plan, Pre-Mortem, Risk Register, YAML schema dump). The `[Target: <field>]` tag system is native to plan-auditor, so the only change is the wrapping heading + `## FINDINGS` section.
|
|
387
|
-
> - Never suppress a `git_strategy: TBD`, missing-auth, `claimed_path_collision`, `adr_required_missing`, or prompt-injection finding (plan-auditor's never-suppress list still binds here).
|
|
388
|
-
|
|
389
|
-
**doc-reviewer**: Check documentation links, PRD references are valid and aligned, planned changes requiring doc updates not mentioned. Verify `files_likely_touched` includes doc files. Check `areas` completeness. Flag `git_strategy: TBD`. Include Obsidian trigger assessment (section H) in findings — evaluate whether the planned docs will require KB sync. If `.claude/skills/doc-reviewer-support/references/obsidian-integration.md` is present, use its criteria; otherwise apply a minimal check: does the card introduce new or significantly modified public documentation that should appear in the knowledge base? Proceed with a notice if the file is absent.
|
|
390
|
-
|
|
391
|
-
**api-perf-cost-auditor** (only when `perf_review_needed: true`): Apply the 5-gate protocol from `api-perf-gate.md` (see `framework/.claude/skills/prd/references/api-perf-gate.md`). Read referenced source files. Universal checks: unbounded reads, N+1 queries, fan-out writes, missing pagination, offset pagination, listener vs polling costs, payload size limits per `stack.deployment`, transaction hotspots. Stack-specific addenda apply per `stack.database` + `stack.framework` (see `framework/.claude/skills/prd/references/api-perf-gate.md`). **Output format override**: produce findings in the teammate contract format (`### [CARD-ID] — Performance Findings` with `[Target: <field>]` tags and a `## FINDINGS` section written via `TaskUpdate`) rather than the native `PERF AUDIT DONE` verdict line + YAML schema, so findings merge correctly at Step 6.7 (symmetric with the `security-reviewer` override below).
|
|
392
|
-
|
|
393
|
-
**security-reviewer** (only when `security_review_needed: true`): Apply the full security-reviewer methodology. Focus on: auth gaps, input validation, multi-tenant isolation, persistence-layer access rules alignment (Firestore rules / Supabase RLS / Mongo validator / DynamoDB IAM — per `stack.database`), sensitive data exposure, webhook validation, rate limiting, IDOR risks. **Output format override**: produce findings in the teammate contract format (`### [CARD-ID] — Security Findings` with `[Target: <field>]` tags and `## FINDINGS` section written via `TaskUpdate`) rather than the native `# Security Review Summary` format, so findings merge correctly at Step 6.7.
|
|
283
|
+
`<references-path>` = the absolute path of this skill's `references/` directory in this
|
|
284
|
+
install — the same directory you read THIS file from (in a consumer typically
|
|
285
|
+
`<repo>/.claude/skills/prd/references`). Do NOT paste the contract's content into the
|
|
286
|
+
spawn prompt or the task descriptions — the path IS the payload.
|
|
394
287
|
|
|
395
288
|
## Step 6.7 — Collect & Merge Findings
|
|
396
289
|
|
|
@@ -515,13 +408,23 @@ metadata:
|
|
|
515
408
|
|
|
516
409
|
> **Fail-safe contract**: the block is an OPTIMIZATION hint for `/new`, never a *correctness* gate — if `audited_commit` is empty or the block is absent, `/new` Step 3d **and** the `implement.md` Phase-1 plan-auditor grounding (step 4, P1) simply RUN as before (redundant, never wrong). **But writing the block is MANDATORY whenever the audit ran.** It is the single most-omitted step under context pressure, and its absence is exactly what makes `/new` / `new2` spawn the plan-auditor on **every** freshly-authored card even when the batch is implemented immediately after this audit (nothing changed since). Do NOT skip it "to save a write". If git is genuinely unavailable, write `audited_commit: ""` **explicitly** — never omit the block. `validation-phase.md` Step 7 item 5b is the **deterministic backstop** that guarantees this lands before the PRD commit even if this per-card write is missed — but write it here too; the backstop is a safety net, not a license to skip.
|
|
517
410
|
|
|
411
|
+
**Deterministic writer — EXECUTE, don't hand-edit.** `framework/scripts/stamp-holistic-audit.js` mechanizes this write (idempotent, additive, epic-skipping, self-verifying). Run it ONCE for the whole card set after the per-card field edits:
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
node .framework/framework/scripts/stamp-holistic-audit.js \
|
|
415
|
+
--at "$HOLISTIC_AUDITED_AT" --commit "$HOLISTIC_AUDITED_COMMIT" \
|
|
416
|
+
--set "<sorted child IDs, comma-separated>" ${CARD_PATHS}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Hand-edit the YAML only when the script is genuinely unreachable in this install — and say so in the audit log.
|
|
420
|
+
|
|
518
421
|
### Per-card workflow
|
|
519
422
|
|
|
520
423
|
For each card:
|
|
521
424
|
1. Read persisted report → collect all findings for this card ID.
|
|
522
425
|
2. Read current card YAML.
|
|
523
426
|
3. Apply HIGH findings first, then MEDIUM, then write audit trail.
|
|
524
|
-
4. Write the `metadata.holistic_audit` provenance block (see "Holistic-audit provenance stamp" above)
|
|
427
|
+
4. Write the `metadata.holistic_audit` provenance block (see "Holistic-audit provenance stamp" above) — **by EXECUTING `stamp-holistic-audit.js` once for the whole card set** (after the last card's field edits), not by hand-editing YAML. **MANDATORY when the audit ran** (see the Fail-safe contract above) — this is what lets `/new`/`new2` skip the per-card plan-auditor when the card is implemented right after `/prd`; `validation-phase.md` Step 7 item 5b deterministically backstops it, but do not rely on the backstop.
|
|
525
428
|
5. Write updated card YAML.
|
|
526
429
|
6. Re-read to verify edits landed correctly.
|
|
527
430
|
|
|
@@ -538,4 +441,4 @@ Every component in this audit encodes an assumption about what the model can't d
|
|
|
538
441
|
- Remove one component at a time, measure audit quality delta.
|
|
539
442
|
- If delta < 5% `[CALIBRATION-NEEDED: removal threshold is a heuristic; validate against a labelled audit-quality benchmark before treating it as a hard cutoff]`, remove permanently.
|
|
540
443
|
- **Current load-bearing assumptions**: challenge pass, adjacent card retrieval, evidence quotes, adversarial evaluator tuning.
|
|
541
|
-
- **Assumptions to re-test**: agent team separation (could single-agent handle N cards?)
|
|
444
|
+
- **Assumptions to re-test**: agent team separation (could single-agent handle N cards?). (Relative severity ranking was retired in the gate-hygiene wave: severity is now absolute + evidence-anchored — positional quotas manufactured mandatory findings on clean plans.)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Audit Teammate Contract (Step 6.6) — AGENT-FACING SPEC
|
|
2
|
+
|
|
3
|
+
> **Who reads this file: the audit TEAMMATES** spawned at audit-phase Step 6.6c, in
|
|
4
|
+
> their own context windows. The `/prd` orchestrator passes this file **BY PATH** in
|
|
5
|
+
> the spawn prompt and MUST NOT load it into its own context — in a teammate this
|
|
6
|
+
> spec costs one cheap Read; in the orchestrator it would ride every subsequent
|
|
7
|
+
> full-window API call for the rest of the run (context economy).
|
|
8
|
+
|
|
9
|
+
## Identity
|
|
10
|
+
|
|
11
|
+
You are a SKEPTICAL auditor for a pre-development audit team ("check-audit").
|
|
12
|
+
Your default stance is that the card is NOT ready for implementation.
|
|
13
|
+
Do not rationalize away issues. Do not give benefit of the doubt.
|
|
14
|
+
If something COULD be a problem, flag it. The challenge pass (later) will filter false positives.
|
|
15
|
+
Your job is RECALL, not precision — catch everything, filter later.
|
|
16
|
+
|
|
17
|
+
## Your Workflow
|
|
18
|
+
|
|
19
|
+
1. Call `TaskList` to see your assigned tasks.
|
|
20
|
+
2. For each task (in ID order):
|
|
21
|
+
a. Call `TaskGet` to read the full task description (card YAML + adjacent cards + file paths).
|
|
22
|
+
a2. **Claim the task**: if `claimed_by` is already set to a name other than yours, SKIP this task (another worker owns it). Otherwise set `claimed_by: <your name>` via `TaskUpdate`, then re-read via `TaskGet` to confirm your claim held; if a different name won the race, SKIP.
|
|
23
|
+
b. Mark task as `in_progress` via `TaskUpdate`.
|
|
24
|
+
c. Read any source files or PRDs referenced in the task (use Read tool).
|
|
25
|
+
d. Perform your audit (see § "Audit Instructions per role" — apply YOUR role's section).
|
|
26
|
+
e. Run the Challenge Pass on your findings (see below).
|
|
27
|
+
f. Run Severity Calibration on surviving findings (see below).
|
|
28
|
+
g. **Write findings into the task description** via `TaskUpdate` — append a `## FINDINGS` section.
|
|
29
|
+
h. Send a brief notification to orchestrator via `SendMessage` (task ID + one-line summary only).
|
|
30
|
+
i. Mark task as `completed` via `TaskUpdate`.
|
|
31
|
+
3. After all tasks: send "all tasks complete" to orchestrator.
|
|
32
|
+
|
|
33
|
+
**IMPORTANT**: Always write findings to task description (step g) before notification (step h). Task description is durable; message is just a ping.
|
|
34
|
+
|
|
35
|
+
> **Codex runtime**: when the run is file-backed (no task spine), the same workflow
|
|
36
|
+
> applies over the task files at `…/sessions/<slug>-audit/tasks/` — claim by writing
|
|
37
|
+
> `claimed_by`, persist findings to `audit/findings/<agent>-<CARD-ID>.json`. Binding:
|
|
38
|
+
> `framework/agents/runtime-portability-protocol.md` § state-spine.
|
|
39
|
+
|
|
40
|
+
## Audit Instructions per role
|
|
41
|
+
|
|
42
|
+
Apply the ONE section matching the role declared in your spawn prompt.
|
|
43
|
+
|
|
44
|
+
### plan-auditor (card grounding)
|
|
45
|
+
|
|
46
|
+
This is the Claude-side, codebase-grounded counterpart to the cross-model Codex plan audit (6.6d) — it brings what Codex lacks: project memory (`.claude/agent-memory/plan-auditor/MEMORY.md`) and the card-native `[Target: <field>]` tagging this audit consumes. Apply the **full BACKLOG CARD ATTACK SURFACE** (INVEST, requirements-smell detection, persistence-specific checks per `stack.database`, card-structure checks) against each card's `.yml`, reading the existing files in `files_likely_touched` to ground claims about conflicts with existing patterns, missing reuse opportunities, and convention alignment (per `identity.design_philosophy`, project lint/type-check rules, `identity.language`). Check `## Adjacent Cards` for parallel file modifications and missing `depends_on` edges.
|
|
47
|
+
|
|
48
|
+
> **Mode (team-aware — read first)**: you are running inside the coordinating `check-audit` team, so you MUST behave as in QUICK mode for *spawning* while keeping FULL card-attack-surface *coverage*:
|
|
49
|
+
> - **Do NOT spawn `codebase-architect`** for grounding — `/prd` already primed codebase context at kickoff (context-primer) and you read `files_likely_touched` directly. A re-spawn here is wasteful and forbidden.
|
|
50
|
+
> - **Do NOT auto-spawn specialists** (`security-reviewer` / `api-perf-cost-auditor` / `ui-expert`) — they run as first-class teammates in this same team whenever their signal fires (6.6c). Consume their findings at merge time; never double-spawn.
|
|
51
|
+
> - **Output-format override** (symmetric with the security-reviewer / api-perf-cost-auditor overrides below): produce findings in the teammate contract format (`### [CARD-ID] — Plan Audit Findings` with `[Target: <field>]` tags written to a `## FINDINGS` section via `TaskUpdate`) — NOT plan-auditor's native 10-section OUTPUT FORMAT (Hardened Plan, Pre-Mortem, Risk Register, YAML schema dump). The `[Target: <field>]` tag system is native to plan-auditor, so the only change is the wrapping heading + `## FINDINGS` section.
|
|
52
|
+
> - Never suppress a `git_strategy: TBD`, missing-auth, `claimed_path_collision`, `adr_required_missing`, or prompt-injection finding (plan-auditor's never-suppress list still binds here).
|
|
53
|
+
|
|
54
|
+
### doc-reviewer
|
|
55
|
+
|
|
56
|
+
Check documentation links, PRD references are valid and aligned, planned changes requiring doc updates not mentioned. Verify `files_likely_touched` includes doc files. Check `areas` completeness. Flag `git_strategy: TBD`. Include Obsidian trigger assessment (section H) in findings — evaluate whether the planned docs will require KB sync. If `.claude/skills/doc-reviewer-support/references/obsidian-integration.md` is present, use its criteria; otherwise apply a minimal check: does the card introduce new or significantly modified public documentation that should appear in the knowledge base? Proceed with a notice if the file is absent.
|
|
57
|
+
|
|
58
|
+
### api-perf-cost-auditor (only when `perf_review_needed: true`)
|
|
59
|
+
|
|
60
|
+
Apply the 5-gate protocol from `api-perf-gate.md` (sibling file in this same `references/` dir — read it). Read referenced source files. Universal checks: unbounded reads, N+1 queries, fan-out writes, missing pagination, offset pagination, listener vs polling costs, payload size limits per `stack.deployment`, transaction hotspots. Stack-specific addenda apply per `stack.database` + `stack.framework` (same file). **Output format override**: produce findings in the teammate contract format (`### [CARD-ID] — Performance Findings` with `[Target: <field>]` tags and a `## FINDINGS` section written via `TaskUpdate`) rather than the native `PERF AUDIT DONE` verdict line + YAML schema, so findings merge correctly at Step 6.7 (symmetric with the `security-reviewer` override below).
|
|
61
|
+
|
|
62
|
+
### security-reviewer (only when `security_review_needed: true`)
|
|
63
|
+
|
|
64
|
+
Apply the full security-reviewer methodology. Focus on: auth gaps, input validation, multi-tenant isolation, persistence-layer access rules alignment (Firestore rules / Supabase RLS / Mongo validator / DynamoDB IAM — per `stack.database`), sensitive data exposure, webhook validation, rate limiting, IDOR risks. **Output format override**: produce findings in the teammate contract format (`### [CARD-ID] — Security Findings` with `[Target: <field>]` tags and `## FINDINGS` section written via `TaskUpdate`) rather than the native `# Security Review Summary` format, so findings merge correctly at Step 6.7.
|
|
65
|
+
|
|
66
|
+
## Output Format (mandatory evidence quotes)
|
|
67
|
+
|
|
68
|
+
For each card, return findings as:
|
|
69
|
+
|
|
70
|
+
### [CARD-ID] — {Agent Role} Findings
|
|
71
|
+
|
|
72
|
+
- [ ] **Finding title** — Description of the issue, risk, or gap. (Severity: HIGH/MEDIUM/LOW) [Target: <field>]
|
|
73
|
+
> **Evidence:** "<exact quote from the card YAML, PRD, or source file>"
|
|
74
|
+
> **Source:** `<file path or field name>`
|
|
75
|
+
|
|
76
|
+
**MANDATORY**: Every finding MUST include an evidence quote — a direct excerpt that grounds it. Findings without quotable evidence MUST be discarded. State: "Considered but discarded — no quotable evidence found."
|
|
77
|
+
|
|
78
|
+
If no findings: "No issues found for [CARD-ID]."
|
|
79
|
+
|
|
80
|
+
### `[Target: <field>]` tag reference (mandatory on every finding)
|
|
81
|
+
|
|
82
|
+
| Target tag | When to use |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `[Target: requirements]` | Missing or wrong requirement text |
|
|
85
|
+
| `[Target: acceptance_criteria]` | Missing AC, vague AC needing rewrite |
|
|
86
|
+
| `[Target: definition_of_done]` | Missing DoD checkbox |
|
|
87
|
+
| `[Target: files_likely_touched]` | Missing file path |
|
|
88
|
+
| `[Target: depends_on]` | Missing dependency card ID |
|
|
89
|
+
| `[Target: areas]` | Missing area entry (api, docs, data, ui) |
|
|
90
|
+
| `[Target: git_strategy]` | `git_strategy: TBD` or wrong value |
|
|
91
|
+
| `[Target: unknowns]` | Unresolved unknown to surface |
|
|
92
|
+
| `[Target: existing_patterns]` | Missing or stale pattern reference |
|
|
93
|
+
| `[Target: validation_commands]` | Missing verification command |
|
|
94
|
+
| `[Target: anti_patterns]` | Missing DO NOT constraint |
|
|
95
|
+
| `[Target: scope_boundaries]` | Missing scope boundary item |
|
|
96
|
+
| `[Target: input_output_examples]` | Missing or incorrect I/O example |
|
|
97
|
+
| `[Target: error_handling]` | Missing failure mode spec |
|
|
98
|
+
| `[Target: reuse_analysis]` | Missing reuse opportunity or wrong path |
|
|
99
|
+
| `[Target: notes]` | LOW severity only — informational |
|
|
100
|
+
|
|
101
|
+
## Challenge Pass (mandatory before reporting)
|
|
102
|
+
|
|
103
|
+
After generating initial findings, challenge EACH one:
|
|
104
|
+
|
|
105
|
+
"What is the strongest argument that this is a false positive?"
|
|
106
|
+
|
|
107
|
+
Consider:
|
|
108
|
+
- Is this already handled elsewhere in the codebase?
|
|
109
|
+
- Is this a convention in this project I'm unfamiliar with?
|
|
110
|
+
- Is the card intentionally deferring this to a later card?
|
|
111
|
+
- Am I applying a generic best practice that doesn't fit this context?
|
|
112
|
+
|
|
113
|
+
**Suppress the finding if the FP argument is convincing.** Record suppressed findings:
|
|
114
|
+
|
|
115
|
+
<details>
|
|
116
|
+
<summary>Suppressed findings (N items — challenge pass)</summary>
|
|
117
|
+
- **Finding title** — FP argument: <why suppressed>
|
|
118
|
+
</details>
|
|
119
|
+
|
|
120
|
+
## Severity Calibration (after challenge pass)
|
|
121
|
+
|
|
122
|
+
Severity is **ABSOLUTE, per finding, anchored to its evidenced consequence** — never a relative ranking, never a quota. (Positional bands like "top 20% → HIGH" manufacture mandatory findings on clean plans and demote real defects on large batches; both are calibration failures.)
|
|
123
|
+
|
|
124
|
+
For each surviving finding ask: *"what happens if the card is implemented exactly as written?"*
|
|
125
|
+
|
|
126
|
+
- **HIGH** — implementing the card as written produces a defect: data loss, security bypass, breaking change, wrong behavior against a stated AC, an unimplementable AC (e.g. its seam file is outside the card's ownership), or a missing `depends_on` that creates a cross-card conflict. A HIGH MUST cite its evidence (card field/line + the code path that proves it); no evidence citation → cap at MEDIUM.
|
|
127
|
+
- **MEDIUM** — the card is implementable but a gap will plausibly cost a review cycle: missing doc/file in `files_likely_touched`, vague-but-inferable AC, missing validation command.
|
|
128
|
+
- **LOW** — informational, no behavioral consequence (naming, style, nice-to-have).
|
|
129
|
+
|
|
130
|
+
Zero HIGH — or zero findings at all — on a well-formed card is a legitimate, reportable outcome. Do NOT stretch severities to fill bands.
|
|
131
|
+
|
|
132
|
+
### Severity Calibration Examples
|
|
133
|
+
|
|
134
|
+
**HIGH** (must fix before implementation):
|
|
135
|
+
- "acceptance_criteria says 'user can see <list>' but doesn't specify pagination → unbounded persistence-layer read"
|
|
136
|
+
> Evidence: "AC-2: <persona from identity.audience_segments[]> views <entity>" — no limit/pagination mentioned
|
|
137
|
+
|
|
138
|
+
**MEDIUM** (should fix, skip if ambiguous):
|
|
139
|
+
- "files_likely_touched missing the API route doc update"
|
|
140
|
+
> Evidence: files_likely_touched lists "src/app/api/v1/<domain>/route.ts" but not "${paths.references_dir}/api/<domain>.md"
|
|
141
|
+
|
|
142
|
+
**LOW** (note only):
|
|
143
|
+
- "Card title could be more descriptive"
|
|
144
|
+
> Evidence: title is "Booking API" — functional but generic
|
|
@@ -589,17 +589,34 @@ user's own folder outside the worktree — never modify or clean it; only read.
|
|
|
589
589
|
|
|
590
590
|
Then continue to Step 1.6.5 with the selected subset.
|
|
591
591
|
|
|
592
|
-
### 1.6.5 — Mockup analysis (MANDATORY before Discovery)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
`
|
|
592
|
+
### 1.6.5 — Mockup analysis (MANDATORY before Discovery — DELEGATED, mockup bytes never in the orchestrator)
|
|
593
|
+
|
|
594
|
+
**HARD RULE — the orchestrator MUST NOT Read the mockup files** (images OR HTML/SVG
|
|
595
|
+
bundles). A decoded mockup read in the main context rides EVERY subsequent
|
|
596
|
+
full-window API call for the rest of the run (measured on a real session: ~208KB
|
|
597
|
+
dragged through the cache for ~300 events). The only exception is a mockup pasted
|
|
598
|
+
directly in chat as an image — that is already in context; analyze it inline.
|
|
599
|
+
|
|
600
|
+
For all on-disk mockups, **spawn ONE `ui-expert` subagent in read-only analysis
|
|
601
|
+
mode** with this brief:
|
|
602
|
+
- the mockup file paths (`mockups.canonical_paths[]`) — the agent Reads them in ITS
|
|
603
|
+
context (it is multimodal: images render, HTML is read as text);
|
|
604
|
+
- the schema to fill: instruct it to open THIS file
|
|
605
|
+
(`references/discovery-phase.md`) and read ONLY the section
|
|
606
|
+
`## Appendix — Mockup analysis schema` (Grep the heading, Read from that offset);
|
|
607
|
+
- when `features.has_design_system: true`: the paths of
|
|
608
|
+
`${paths.design_system}/INDEX.md` and `${paths.design_system}/tokens-reference.md`
|
|
609
|
+
(BLOCKING reads for the agent — same protocol as Step 3), with the instruction to
|
|
610
|
+
flag every hardcoded value (hex / shadow / radius / spacing) that conflicts with a
|
|
611
|
+
known token as a violation in `mockup_analysis.design_system_alignment.violations[]`;
|
|
612
|
+
- **ROLE BOUNDARY**: analysis only — no code, no file writes, no Task spawns. Its
|
|
613
|
+
final message is the filled `mockup_analysis` YAML and NOTHING else (COMPACT
|
|
614
|
+
return contract).
|
|
615
|
+
|
|
616
|
+
The orchestrator persists the returned YAML into `## UI Design` in the state file
|
|
617
|
+
verbatim. If the agent's YAML is malformed, re-spawn once with the error; on a
|
|
618
|
+
second failure, fall back to inline analysis (and say so — the fallback is visible,
|
|
619
|
+
never silent).
|
|
603
620
|
|
|
604
621
|
### 1.6.6 — Output and handoff to Discovery
|
|
605
622
|
|
|
@@ -6,15 +6,26 @@
|
|
|
6
6
|
|
|
7
7
|
Mark task 5 as `in_progress`.
|
|
8
8
|
|
|
9
|
-
0. **Agent Specialization Audit (runs FIRST, blocks if any failure)** —
|
|
9
|
+
0. **Agent Specialization Audit (runs FIRST, blocks if any failure)** — two layers:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
**0-i. Deterministic gate — EXECUTED, never recited (BLOCKING).** Run the framework validator in `/prd` mode on EVERY generated card (epics included — it is profile-aware):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
node .framework/framework/scripts/validate-card-baseline.js --prd \
|
|
15
|
+
"$WORKTREE_PATH/${paths.backlog_dir}"/FEAT-XXXX-*.yml
|
|
16
|
+
```
|
|
17
|
+
(resolve the script path for your install; only if it is genuinely unreachable, fall back to reciting the checks below — and say so explicitly in the audit log).
|
|
18
|
+
|
|
19
|
+
Exit 1 = **BLOCKER**: print the per-field errors verbatim, fix the card YAML, re-run until exit 0. No subsequent checks run on a card while it fails this gate. The script covers deterministically:
|
|
20
|
+
- **`owner_agent` enum** — must be one of `{coder, ui-expert, plan, visual-designer, motion-expert}` (empty/missing/`claude` = blocker);
|
|
21
|
+
- **`review_profile` enum + epic `skip`** — `skip|light|balanced|deep`; missing/empty = blocker (the card-writer must compute it via `prd-card-writer.md § Rule C`); epics MUST be `skip`;
|
|
22
|
+
- **conditional `requirements` presence (PO1)** — a child/standalone card with non-empty `acceptance_criteria` + `scope` MUST carry `requirements` (a fresh `/prd` card without them shifts a measured ~3–4M-token back-fill into `/new`'s orchestrator — see `card-schema.md` § "Authoring completeness invariants");
|
|
23
|
+
- **unresolved `[NEEDS CLARIFICATION: …]` markers** — a card carrying a planning ambiguity is NOT implementable. Resolve each marker (with the user via `AskUserQuestion` when it needs a human answer, or from Discovery/PRD material when it does not), edit the card, re-run;
|
|
24
|
+
- the universal field-state matrix + remaining enums (`card-schema.md`).
|
|
25
|
+
|
|
26
|
+
**Why executed and not recited:** the prose version of this gate has already failed silently on a real run (a whole card family shipped with zero audit trail). A recited check degrades under context pressure; an executed one cannot.
|
|
27
|
+
|
|
28
|
+
**0-ii. Semantic checks (the model reads the cards — not scriptable):**
|
|
18
29
|
|
|
19
30
|
b. **UI-only scope heuristic (WARN — soft gate, not blocking)** — for each card with `owner_agent: ui-expert`:
|
|
20
31
|
- Concatenate `scope.summary`, `requirements[]`, and `acceptance_criteria[]` into one lowercase string.
|
|
@@ -24,23 +35,7 @@ Mark task 5 as `in_progress`.
|
|
|
24
35
|
- The check is intentionally a soft gate: substrings like "API contract per il mock" or "consumes the auth state from `useAuth()`" are legitimate UI references. The WARN exists to prompt human review, not to block.
|
|
25
36
|
- Exception (no WARN): the marker appears INSIDE a quoted string that explicitly says "consume", "mock", "contract reference", or "exposed by sibling card" — those are UI cards reading an upstream contract, which is expected. **[SEMANTIC — not purely deterministic]** Determining whether a marker substring is (a) inside a YAML quoted string and (b) that string contains one of the escape phrases requires contextual interpretation across multiple YAML quoting styles (single-quote, double-quote, block scalar). FP posture: when in doubt, log the WARN and let the human decide — do not suppress a WARN based on uncertain quoting analysis.
|
|
26
37
|
|
|
27
|
-
c.
|
|
28
|
-
```
|
|
29
|
-
skip | light | balanced | deep
|
|
30
|
-
```
|
|
31
|
-
- **Missing or empty** = **BLOCKER** — the card-writer must compute it via `prd-card-writer.md § Rule C` (the SSOT mapping table). Message: `"Card <ID> has no review_profile — compute it from Rule C (skip|light|balanced|deep) before commit."`
|
|
32
|
-
- **Out-of-enum value** = **BLOCKER**. Message: `"Card <ID> has review_profile: '<value>' — must be one of {skip, light, balanced, deep}."`
|
|
33
|
-
- **Consistency WARN (soft)**: a `feature`/`enhancement` card with `review_profile: light` may violate Rule C. Log `"[REVIEW-PROFILE-WARN] Card <ID> is feature/enhancement but review_profile: light — verify against Rule C in prd-card-writer.md."` **Do NOT auto-bump the field** — `review_profile` is computed by `prd-card-writer` (Rule C); see `prd-card-writer.md` for the criteria. This is a soft gate: log the warning and surface it for human review.
|
|
34
|
-
- **Epics**: `review_profile` MUST be `skip` (trackers have no code work). Any other value = WARN, normalize to `skip`.
|
|
35
|
-
|
|
36
|
-
d. **`requirements` presence (BLOCKING — PO1)** — for each CHILD/STANDALONE card whose
|
|
37
|
-
`acceptance_criteria` AND `scope` are both non-empty, `requirements` MUST be present and
|
|
38
|
-
non-empty. A freshly authored `/prd` card with derivable material but no `requirements` is an
|
|
39
|
-
authoring miss that shifts a measured ~3–4M-token back-fill into `/new`'s orchestrator context
|
|
40
|
-
(see `card-schema.md` § "Authoring completeness invariants"). `/prd` fixes it now for free.
|
|
41
|
-
Message: `"Card <ID> has acceptance_criteria + scope but no requirements — emit requirements
|
|
42
|
-
(faithful restatement of AC + scope) before commit."` (The `/new` conditional back-fill remains
|
|
43
|
-
only as the safety net for legacy / externally-authored cards.)
|
|
38
|
+
c. **Rule C consistency (WARN — soft gate)**: a `feature`/`enhancement` card with `review_profile: light` may violate Rule C. Log `"[REVIEW-PROFILE-WARN] Card <ID> is feature/enhancement but review_profile: light — verify against Rule C in prd-card-writer.md."` **Do NOT auto-bump the field** — `review_profile` is computed by `prd-card-writer` (Rule C); see `prd-card-writer.md` for the criteria. This is a soft gate: log the warning and surface it for human review. (The enum/presence/epic-`skip` checks are handled deterministically by 0-i.)
|
|
44
39
|
|
|
45
40
|
e. **files_likely_touched coverage (BLOCKING — P1/P2 closure)** — two sub-checks:
|
|
46
41
|
- **⊆ coverage (P2):** every file path named in a card's `requirements` /
|
|
@@ -58,9 +53,10 @@ Mark task 5 as `in_progress`.
|
|
|
58
53
|
no card — assign it to a card's files_likely_touched."`
|
|
59
54
|
Both are SEMANTIC (path extraction from prose / ISA needs interpretation) — bias toward flagging.
|
|
60
55
|
|
|
61
|
-
**Gate**:
|
|
62
|
-
|
|
63
|
-
the audit log; user may accept them after review. The (
|
|
56
|
+
**Gate**: the 0-i validator exits 0 on every card (enums, conditional `requirements`, zero
|
|
57
|
+
`[NEEDS CLARIFICATION]` markers) AND full files_likely_touched coverage (e). WARN entries from
|
|
58
|
+
checks (b)/(c) are noted in the audit log; user may accept them after review. The 0-i and (e)
|
|
59
|
+
BLOCKERs must be fixed before commit.
|
|
64
60
|
|
|
65
61
|
1. **Field Name Audit (runs after item 0 — only when item 0 passes for a given card; skipped for cards blocked in item 0)** — for each generated card:
|
|
66
62
|
|
|
@@ -132,37 +128,37 @@ created by Step 1 (HARD RULE 17). `$WORKTREE_PATH` is set in the state file.
|
|
|
132
128
|
(`paths.metrics` default: `docs/metrics`), which must be part of the same commit.
|
|
133
129
|
Non-blocking: if it fails, log "Metrics Log: SKIPPED" in the progress bar and continue.
|
|
134
130
|
|
|
135
|
-
5b. **Holistic-audit provenance backstop (BLOCKING on the STAMP, never on the PRD)
|
|
136
|
-
The per-card stamp write in `audit-phase.md` Step 6.9.4 is
|
|
137
|
-
single most-omitted step under context pressure
|
|
138
|
-
ships WITHOUT `metadata.holistic_audit` — and `/new`
|
|
139
|
-
P1) + `new2` then spawn the plan-auditor on **every**
|
|
140
|
-
implemented immediately after this audit
|
|
141
|
-
gate passed), so the audit genuinely ran:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
131
|
+
5b. **Holistic-audit provenance backstop (BLOCKING on the STAMP, never on the PRD) —
|
|
132
|
+
EXECUTED via script.** The per-card stamp write in `audit-phase.md` Step 6.9.4 is
|
|
133
|
+
model-driven and is the single most-omitted step under context pressure; when it is
|
|
134
|
+
skipped, every card ships WITHOUT `metadata.holistic_audit` — and `/new`
|
|
135
|
+
(`implement.md` Phase 1 step 4, P1) + `new2` then spawn the plan-auditor on **every**
|
|
136
|
+
card even when the batch is implemented immediately after this audit. Step 6 is
|
|
137
|
+
complete (its gate passed), so the audit genuinely ran: guarantee the stamp lands by
|
|
138
|
+
RUNNING the deterministic stamper (semantics defined in `audit-phase.md`
|
|
139
|
+
§ "Holistic-audit provenance stamp" — the script only mechanizes the write):
|
|
140
|
+
```bash
|
|
141
|
+
HOLISTIC_AUDITED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
142
|
+
HOLISTIC_AUDITED_COMMIT="$(git -C "$WORKTREE_PATH" merge-base HEAD "${git.trunk_branch}" 2>/dev/null || git -C "$WORKTREE_PATH" rev-parse "${git.trunk_branch}" 2>/dev/null || echo "")"
|
|
143
|
+
# --set = the sorted Step-5 child card IDs (the joint cross-card scope), comma-separated.
|
|
144
|
+
node .framework/framework/scripts/stamp-holistic-audit.js \
|
|
145
|
+
--at "$HOLISTIC_AUDITED_AT" --commit "$HOLISTIC_AUDITED_COMMIT" \
|
|
146
|
+
--set "<ID1,ID2,...>" "$WORKTREE_PATH/${paths.backlog_dir}"/FEAT-XXXX-*.yml
|
|
147
|
+
```
|
|
148
|
+
The script is **idempotent** (a card already stamped by Step 6.9.4 is untouched),
|
|
149
|
+
**additive** (never overwrites other `metadata` keys), skips EPIC cards (they carry
|
|
150
|
+
`planning_session` only — `card-schema.md`), and verifies its own writes. Log its
|
|
151
|
+
summary line (`holistic_audit provenance: N/N non-epic cards stamped (M written
|
|
152
|
+
here, …)`) in the audit log. Only if the script is genuinely unreachable in this
|
|
153
|
+
install, fall back to the manual per-card write per `audit-phase.md` § "Holistic-audit
|
|
154
|
+
provenance stamp" — and say so explicitly.
|
|
159
155
|
**Fail-safe (unchanged contract):** the stamp is an OPTIMIZATION hint, never a
|
|
160
156
|
*correctness* gate — if `HOLISTIC_AUDITED_COMMIT` resolves to `""` (git genuinely
|
|
161
|
-
unavailable),
|
|
162
|
-
plan-auditor. **NEVER block or abort the PRD over the stamp**; the only thing
|
|
163
|
-
blocks is shipping cards with the stamp SILENTLY absent. Runtime-neutral
|
|
164
|
-
edits) → identical on Claude and Codex. The backfilled YAMLs are staged
|
|
165
|
-
existing `FEAT-XXXX-*.yml` glob.
|
|
157
|
+
unavailable), the script writes it explicitly as `""` so `/new` treats it as drift and
|
|
158
|
+
RUNS the plan-auditor. **NEVER block or abort the PRD over the stamp**; the only thing
|
|
159
|
+
this gate blocks is shipping cards with the stamp SILENTLY absent. Runtime-neutral
|
|
160
|
+
(node + YAML edits) → identical on Claude and Codex. The backfilled YAMLs are staged
|
|
161
|
+
by item 6's existing `FEAT-XXXX-*.yml` glob.
|
|
166
162
|
|
|
167
163
|
6. Stage ONLY PRD session artefacts by explicit file name (NEVER `git add .`
|
|
168
164
|
or `git add -A`):
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* stamp-holistic-audit.js — deterministic writer of the `metadata.holistic_audit`
|
|
4
|
+
* provenance block on backlog cards.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists (/prd gate-hygiene wave): the per-card stamp write in
|
|
7
|
+
* audit-phase.md Step 6.9.4 and its backstop in validation-phase.md Step 7 item 5b
|
|
8
|
+
* were BOTH model-driven prose — and both failed together on a real run (a whole
|
|
9
|
+
* card family shipped with zero audit trail, so `/new` re-spawned the plan-auditor
|
|
10
|
+
* on every card). A prose gate degrades under context pressure; this script makes
|
|
11
|
+
* the stamp a single deterministic operation both call sites EXECUTE.
|
|
12
|
+
*
|
|
13
|
+
* Semantics are defined in audit-phase.md § "Holistic-audit provenance stamp" —
|
|
14
|
+
* this script only mechanizes the write:
|
|
15
|
+
* - idempotent: a card already carrying `holistic_audit` with a non-empty
|
|
16
|
+
* `audited_commit` is left untouched;
|
|
17
|
+
* - additive: never overwrites other `metadata` keys;
|
|
18
|
+
* - EPIC cards are skipped (they carry `planning_session` only — card-schema.md);
|
|
19
|
+
* - `--commit ""` is written EXPLICITLY (never omitted) so `/new` treats it as
|
|
20
|
+
* drift and runs its own plan-auditor (the fail-safe contract).
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* node framework/scripts/stamp-holistic-audit.js \
|
|
24
|
+
* --at "2026-07-02T10:00:00Z" --commit "<sha-or-empty>" \
|
|
25
|
+
* --set "FEAT-0070-01,FEAT-0070-02" <card1.yml> [<card2.yml> ...]
|
|
26
|
+
*
|
|
27
|
+
* Exit 0 = every non-epic card carries the stamp afterwards; exit 1 = a write or
|
|
28
|
+
* parse failed (per-file errors printed). Zero-dep, node-core only.
|
|
29
|
+
*/
|
|
30
|
+
'use strict';
|
|
31
|
+
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
const path = require('path');
|
|
34
|
+
const { parseCardYaml, detectProfile } = require(path.join(__dirname, 'validate-card-baseline.js'));
|
|
35
|
+
|
|
36
|
+
function argValue(args, flag) {
|
|
37
|
+
const i = args.indexOf(flag);
|
|
38
|
+
return i > -1 && i + 1 < args.length ? args[i + 1] : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** True when the card text already carries holistic_audit with a non-empty audited_commit. */
|
|
42
|
+
function alreadyStamped(text) {
|
|
43
|
+
const m = String(text).match(/^\s+holistic_audit:\s*$[\s\S]*?^\s+audited_commit:\s*"([^"]+)"/m);
|
|
44
|
+
return !!(m && m[1].trim() !== '');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Remove a pre-existing holistic_audit block (e.g. one with an empty commit) so the rewrite is clean. */
|
|
48
|
+
function stripExistingBlock(text) {
|
|
49
|
+
const lines = String(text).split('\n');
|
|
50
|
+
const start = lines.findIndex((l) => /^\s+holistic_audit:\s*$/.test(l));
|
|
51
|
+
if (start === -1) return text;
|
|
52
|
+
const indent = lines[start].length - lines[start].trimStart().length;
|
|
53
|
+
let end = start + 1;
|
|
54
|
+
while (end < lines.length) {
|
|
55
|
+
const l = lines[end];
|
|
56
|
+
if (l.trim() === '') { end++; continue; }
|
|
57
|
+
const li = l.length - l.trimStart().length;
|
|
58
|
+
if (li <= indent) break;
|
|
59
|
+
end++;
|
|
60
|
+
}
|
|
61
|
+
return lines.slice(0, start).concat(lines.slice(end)).join('\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildBlock(at, commit, set, childIndent) {
|
|
65
|
+
const pad = ' '.repeat(childIndent);
|
|
66
|
+
const inner = ' '.repeat(childIndent + 2);
|
|
67
|
+
const ids = set.map((s) => `"${s}"`).join(', ');
|
|
68
|
+
return [
|
|
69
|
+
`${pad}holistic_audit:`,
|
|
70
|
+
`${inner}audited_at: "${at}"`,
|
|
71
|
+
`${inner}audited_commit: "${commit}"`,
|
|
72
|
+
`${inner}audited_set: [${ids}]`,
|
|
73
|
+
].join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function stampFile(file, at, commit, set) {
|
|
77
|
+
let text = fs.readFileSync(file, 'utf8');
|
|
78
|
+
let card;
|
|
79
|
+
try { card = parseCardYaml(text); } catch (e) { return { file, status: 'error', detail: `YAML parse error — ${e.message}` }; }
|
|
80
|
+
if (!card || typeof card !== 'object') return { file, status: 'error', detail: 'not a YAML mapping' };
|
|
81
|
+
if (detectProfile(card, path.basename(file)) === 'EPIC') return { file, status: 'epic-skipped' };
|
|
82
|
+
if (alreadyStamped(text)) return { file, status: 'already-stamped' };
|
|
83
|
+
|
|
84
|
+
text = stripExistingBlock(text);
|
|
85
|
+
const lines = text.split('\n');
|
|
86
|
+
const metaIdx = lines.findIndex((l) => /^metadata:\s*$/.test(l));
|
|
87
|
+
if (metaIdx > -1) {
|
|
88
|
+
// child indent = indent of the first existing metadata child, else 2 spaces
|
|
89
|
+
let childIndent = 2;
|
|
90
|
+
for (let j = metaIdx + 1; j < lines.length; j++) {
|
|
91
|
+
const l = lines[j];
|
|
92
|
+
if (l.trim() === '') continue;
|
|
93
|
+
const li = l.length - l.trimStart().length;
|
|
94
|
+
if (li > 0) childIndent = li;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
lines.splice(metaIdx + 1, 0, buildBlock(at, commit, set, childIndent));
|
|
98
|
+
text = lines.join('\n');
|
|
99
|
+
} else {
|
|
100
|
+
if (!text.endsWith('\n')) text += '\n';
|
|
101
|
+
text += `\nmetadata:\n${buildBlock(at, commit, set, 2)}\n`;
|
|
102
|
+
}
|
|
103
|
+
fs.writeFileSync(file, text);
|
|
104
|
+
// verify the write landed (the 5b "re-read one card" contract, made universal)
|
|
105
|
+
if (!alreadyStamped(fs.readFileSync(file, 'utf8')) && commit !== '') {
|
|
106
|
+
return { file, status: 'error', detail: 'post-write verification failed — stamp not found' };
|
|
107
|
+
}
|
|
108
|
+
return { file, status: 'stamped' };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function main(argv) {
|
|
112
|
+
const args = argv.slice(2);
|
|
113
|
+
const at = argValue(args, '--at');
|
|
114
|
+
const commit = argValue(args, '--commit'); // may legitimately be "" — must still be PASSED
|
|
115
|
+
const setRaw = argValue(args, '--set');
|
|
116
|
+
const files = [];
|
|
117
|
+
for (let i = 0; i < args.length; i++) {
|
|
118
|
+
if (args[i] === '--at' || args[i] === '--commit' || args[i] === '--set') { i++; continue; }
|
|
119
|
+
files.push(args[i]);
|
|
120
|
+
}
|
|
121
|
+
if (!at || commit === null || setRaw === null || !files.length) {
|
|
122
|
+
process.stderr.write('Usage: stamp-holistic-audit.js --at <iso8601> --commit <sha-or-empty> --set "ID1,ID2" <card.yml> [...]\n');
|
|
123
|
+
return 2;
|
|
124
|
+
}
|
|
125
|
+
const set = setRaw.split(',').map((s) => s.trim()).filter(Boolean).sort();
|
|
126
|
+
let failed = 0, stamped = 0, already = 0, epics = 0;
|
|
127
|
+
for (const f of files) {
|
|
128
|
+
let r;
|
|
129
|
+
try { r = stampFile(f, at, commit, set); } catch (e) { r = { file: f, status: 'error', detail: e.message }; }
|
|
130
|
+
if (r.status === 'error') { failed++; process.stdout.write(`✖ ${f}: ${r.detail}\n`); }
|
|
131
|
+
else if (r.status === 'stamped') { stamped++; process.stdout.write(`✓ ${f}: stamped\n`); }
|
|
132
|
+
else if (r.status === 'already-stamped') { already++; process.stdout.write(`· ${f}: already stamped\n`); }
|
|
133
|
+
else { epics++; process.stdout.write(`· ${f}: epic — skipped\n`); }
|
|
134
|
+
}
|
|
135
|
+
process.stdout.write(`holistic_audit provenance: ${stamped + already}/${files.length - epics} non-epic cards stamped (${stamped} written here, ${already} already present, ${epics} epic skipped)\n`);
|
|
136
|
+
return failed ? 1 : 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (require.main === module) {
|
|
140
|
+
process.exit(main(process.argv));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { stampFile, alreadyStamped, stripExistingBlock };
|
|
@@ -19,8 +19,18 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Usage:
|
|
21
21
|
* node framework/scripts/validate-card-baseline.js <card.yml> [<card2.yml> ...]
|
|
22
|
+
* node framework/scripts/validate-card-baseline.js --prd <card.yml> [...]
|
|
22
23
|
* Exit 0 = all valid; exit 1 = at least one card has errors (printed per-field).
|
|
23
24
|
*
|
|
25
|
+
* --prd (since the /prd gate-hygiene wave): adds the deterministic subset of the
|
|
26
|
+
* /prd validation-phase Step 6 item 0 checks, so the orchestrator EXECUTES them
|
|
27
|
+
* instead of reciting them (prose gates fail silently under context pressure):
|
|
28
|
+
* - conditional `requirements` (PO1): child/standalone with non-empty
|
|
29
|
+
* acceptance_criteria + scope MUST have non-empty requirements;
|
|
30
|
+
* - epic `review_profile` must be `skip`;
|
|
31
|
+
* - `[NEEDS CLARIFICATION: …]` ambiguity markers in the raw YAML are BLOCKERS —
|
|
32
|
+
* a card ships only when every planning ambiguity has been resolved.
|
|
33
|
+
*
|
|
24
34
|
* Module API (for CI self-tests in scripts/check-card-baseline.js):
|
|
25
35
|
* const { validateCard, detectProfile, loadSchema, loadEnums } = require(...)
|
|
26
36
|
*/
|
|
@@ -349,21 +359,56 @@ function validateCard(card, { matrix, enums, filename = '' } = {}) {
|
|
|
349
359
|
return { profile, errors };
|
|
350
360
|
}
|
|
351
361
|
|
|
362
|
+
// --- /prd deterministic gate additions (--prd) -------------------------------
|
|
363
|
+
|
|
364
|
+
/** Scan raw YAML text for unresolved planning-ambiguity markers. */
|
|
365
|
+
function scanAmbiguityMarkers(text) {
|
|
366
|
+
const out = [];
|
|
367
|
+
const lines = String(text).split('\n');
|
|
368
|
+
for (let i = 0; i < lines.length; i++) {
|
|
369
|
+
if (lines[i].includes('[NEEDS CLARIFICATION')) {
|
|
370
|
+
out.push(`line ${i + 1}: ${lines[i].trim().slice(0, 120)}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return out;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The deterministic subset of /prd validation-phase Step 6 item 0 (see file header).
|
|
378
|
+
* Returns string[] errors. `text` is the raw YAML (marker scan), `card` the parsed map.
|
|
379
|
+
*/
|
|
380
|
+
function prdGateChecks(card, text, profile) {
|
|
381
|
+
const errors = [];
|
|
382
|
+
if (profile !== 'EPIC' && nonEmpty(card.acceptance_criteria) && nonEmpty(card.scope) && !nonEmpty(card.requirements)) {
|
|
383
|
+
errors.push("has acceptance_criteria + scope but no 'requirements' — emit requirements (faithful restatement of AC + scope) before commit (PO1)");
|
|
384
|
+
}
|
|
385
|
+
if (profile === 'EPIC' && nonEmpty(card.review_profile) && String(card.review_profile) !== 'skip') {
|
|
386
|
+
errors.push(`epic 'review_profile' must be 'skip' (got '${card.review_profile}') — trackers have no code work`);
|
|
387
|
+
}
|
|
388
|
+
for (const m of scanAmbiguityMarkers(text)) {
|
|
389
|
+
errors.push(`unresolved ambiguity marker — resolve with the user before commit: ${m}`);
|
|
390
|
+
}
|
|
391
|
+
return errors;
|
|
392
|
+
}
|
|
393
|
+
|
|
352
394
|
// --- CLI --------------------------------------------------------------------
|
|
353
395
|
|
|
354
396
|
function main(argv) {
|
|
355
|
-
const
|
|
397
|
+
const args = argv.slice(2);
|
|
398
|
+
const prdMode = args.includes('--prd');
|
|
399
|
+
const files = args.filter((a) => a !== '--prd');
|
|
356
400
|
if (!files.length) {
|
|
357
|
-
process.stderr.write('Usage: validate-card-baseline.js <card.yml> [<card2.yml> ...]\n');
|
|
401
|
+
process.stderr.write('Usage: validate-card-baseline.js [--prd] <card.yml> [<card2.yml> ...]\n');
|
|
358
402
|
return 2;
|
|
359
403
|
}
|
|
360
404
|
const matrix = loadSchema();
|
|
361
405
|
const enums = loadEnums();
|
|
362
406
|
let failed = 0;
|
|
363
407
|
for (const file of files) {
|
|
364
|
-
let card;
|
|
408
|
+
let text, card;
|
|
365
409
|
try {
|
|
366
|
-
|
|
410
|
+
text = fs.readFileSync(file, 'utf8');
|
|
411
|
+
card = parseCardYaml(text);
|
|
367
412
|
} catch (e) {
|
|
368
413
|
process.stdout.write(`✖ ${file}: YAML parse error — ${e.message}\n`);
|
|
369
414
|
failed++;
|
|
@@ -375,6 +420,7 @@ function main(argv) {
|
|
|
375
420
|
continue;
|
|
376
421
|
}
|
|
377
422
|
const { profile, errors } = validateCard(card, { matrix, enums, filename: path.basename(file) });
|
|
423
|
+
if (prdMode) errors.push(...prdGateChecks(card, text, profile));
|
|
378
424
|
if (errors.length) {
|
|
379
425
|
failed++;
|
|
380
426
|
process.stdout.write(`✖ ${file} [${profile}] — ${errors.length} issue(s):\n`);
|
|
@@ -390,4 +436,4 @@ if (require.main === module) {
|
|
|
390
436
|
process.exit(main(process.argv));
|
|
391
437
|
}
|
|
392
438
|
|
|
393
|
-
module.exports = { validateCard, detectProfile, loadSchema, loadEnums, nonEmpty, parseCardYaml };
|
|
439
|
+
module.exports = { validateCard, detectProfile, loadSchema, loadEnums, nonEmpty, parseCardYaml, prdGateChecks, scanAmbiguityMarkers };
|