baldart 5.7.0 → 5.9.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 +54 -0
- package/README.md +1 -1
- package/VERSION +1 -1
- package/framework/.claude/agents/CHANGELOG.md +18 -0
- package/framework/.claude/agents/prd-card-writer.md +23 -0
- package/framework/.claude/agents/security-reviewer.md +53 -1
- package/framework/.claude/skills/new/CHANGELOG.md +15 -0
- package/framework/.claude/skills/new/SKILL.md +1 -1
- package/framework/.claude/skills/new/references/final-review.md +36 -1
- package/framework/.claude/skills/new2/CHANGELOG.md +11 -0
- package/framework/.claude/skills/new2/SKILL.md +1 -1
- package/framework/.claude/skills/prd/CHANGELOG.md +28 -0
- package/framework/.claude/skills/prd/SKILL.md +1 -1
- package/framework/.claude/skills/prd/assets/epic-template.yml +6 -0
- package/framework/.claude/skills/prd/references/backlog-phase.md +36 -0
- package/framework/.claude/skills/prd/references/validation-phase.md +30 -0
- package/framework/.claude/workflows/new2.js +19 -2
- package/framework/agents/card-schema.md +50 -0
- package/framework/agents/index.md +2 -1
- package/framework/agents/security-review-protocol.md +233 -0
- package/framework/agents/security.md +50 -46
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,60 @@ 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
|
+
## [5.9.0] - 2026-07-06
|
|
9
|
+
|
|
10
|
+
**`security-reviewer` upgraded to the 2026 AppSec state of the art — benchmarked against Codex `codex-security` + Claude Code `security-guidance`, imported as a single-sourced protocol module.**
|
|
11
|
+
The `security-reviewer` was a high-quality *monolithic* agent (senior persona, dual review/apply, pooled YAML, generic Challenge/CoVe via `review-protocol.md`) but stuck at a single-pass "read → list findings by severity" model. A benchmark against two real corpora — Codex's phased `codex-security` workbench (threat-model → discovery → validation → attack-path → severity, coverage ledgers, instance-preserving proof) and Claude Code's `security-guidance` agentic reviewer (adversarial self-refute, attacker≠victim, modern high-miss patterns) — found BALDART at ~60% of current best practice. This wave closes the gap **without a monolithic rewrite**, following the agent-spine 3-layer model.
|
|
12
|
+
|
|
13
|
+
- **New protocol module [`framework/agents/security-review-protocol.md`](framework/agents/security-review-protocol.md)** — the AppSec verification engine, the security twin of `review-protocol.md`. `SECTION=` dispatch: `boundary-gate` (read `SECURITY.md` + classify `product_surface`/`source_trust`/`boundary_crossed` before confirming), `threat-model` (repository-scoped, never diff-biased), `discovery-lens` (the modern **high-miss** taxonomy: fail-open state drift, allowlist semantic escape, control regression, gate/action field mismatch, parser/validator differential, sensitive-to-observability, CI/CD trust, IaC omitted arg, over-broad grant, stale identity mapping, security-registry fanout, resource-bound placement), `proof-tuples` (class-specific validation tuples + confidence anchored to validation **method** not bug class + instance-preserving), `attack-path` (structured facts → **mechanical** severity), `adversarial-refute` (SURVIVES-unless-refuted, the **attacker≠victim** privilege-boundary test first, precondition-vs-counterevidence discipline, diff newly-introduced-only), `coverage-ledger` (surface dispositions so a clean verdict is honest). It **EXTENDS** `review-protocol.md`'s generic passes, never duplicates them — no dual-SSOT.
|
|
14
|
+
- **`security-reviewer` agent (v1.0.0)** — gains a versioned frontmatter + a "Security Review Passes (BINDING)" block that cites the module with 1-line binding statements (agent-spine: core inline + module on demand). Mode A pooled YAML schema gains `vuln_class`, `cwe: [CWE-###]`, `validation_method`, and a structured `attack_path` block; the summary header carries a compact `coverage:` block. New **secret-redaction** BINDING (report a leaked credential as first 2–4 chars + `****`, never the live value — a review artifact reproducing a secret is itself a leak).
|
|
15
|
+
- **`framework/agents/security.md`** — the consumer-facing security template modernized from **OWASP Top 10:2017 → 2021** (XXE folded into Security Misconfiguration, insecure deserialization into Software & Data Integrity Failures, XSS into Injection; new A04 Insecure Design + A10 SSRF), with a 2025-draft note.
|
|
16
|
+
- Routing wired in `agents/index.md` (security routing rule + Modules list). README security-reviewer inventory line updated.
|
|
17
|
+
- **Codex parity: portable** — the module is markdown read at runtime by both runtimes; the agent-body edits transpile to `.codex/agents/security-reviewer.toml` automatically. **No new `baldart.config.yml` key** → the schema-change propagation rule does NOT apply.
|
|
18
|
+
|
|
19
|
+
## [5.8.0] - 2026-07-06
|
|
20
|
+
|
|
21
|
+
**Suggested launch plan at the end of `/prd` — waves with a NAME, persisted on the epic card.**
|
|
22
|
+
`/prd` used to close with a progress bar and a bare summary (PRD path, card IDs, branch, PR/SHA)
|
|
23
|
+
but never told you **how to launch `/new`** — one run or several waves, where the boundaries are,
|
|
24
|
+
which engine. You had to reason it out by hand. Now every `/prd` run ends with a mandatory
|
|
25
|
+
**"🚀 Piano di implementazione suggerito"** section, and the plan is **persisted on the epic
|
|
26
|
+
card** so `/new`/`/new2` can, at the end of a subset run, remind you which waves remain to close
|
|
27
|
+
the epic — each wave carries a short, memorable **name** ("Wave 1 · Fondamenta server").
|
|
28
|
+
|
|
29
|
+
The design was hardened by a two-reviewer adversarial pass that refuted the obvious v1
|
|
30
|
+
(a separate `implementation_plan` block, an `engine` field on the card, a new reference module,
|
|
31
|
+
the mechanical card-writer computing the heuristic). The shipped shape:
|
|
32
|
+
|
|
33
|
+
- **Drift-free persistence — no new block, no new module.** The wave partition is three OPTIONAL
|
|
34
|
+
sub-keys (`wave` / `wave_name` / `wave_rationale`) on the EXISTING `execution_strategy.groups[]`
|
|
35
|
+
entries — the single card-ID partition already consumed by `/new` for parallelism. No parallel
|
|
36
|
+
`waves[]` array re-listing card IDs (that would drift). Because they are sub-keys of an
|
|
37
|
+
already-`R` field, the baseline validator never sees them: **no new field-state-matrix row, no
|
|
38
|
+
migration of legacy epics**. Schema SSOT: `framework/agents/card-schema.md`.
|
|
39
|
+
- **Compute-by-orchestrator / persist-by-writer** (the `provenance.planning_session` split):
|
|
40
|
+
the `/prd` orchestrator (opus/high) computes the single-run-vs-waves judgment + names + rationale
|
|
41
|
+
— it needs the discovery context (migration-early, `review_profile: deep` pressure, server↔UI
|
|
42
|
+
cut) the mechanical `prd-card-writer` never holds — and persists it with a targeted Edit on the
|
|
43
|
+
epic card. The card-writer emits the keys ONLY when handed them; else it omits them (explicit
|
|
44
|
+
else branch → no fabrication). Single-run is the default (no wave tags).
|
|
45
|
+
- **Engine + hard rules are render-time, never persisted.** The `/new` vs `/new2` recommendation
|
|
46
|
+
is computed at render and is **runtime-aware** — `/new2` needs the Workflow tool and is
|
|
47
|
+
Claude-only, so on Codex the plan recommends `/new` only. A durable, cross-tool artifact must
|
|
48
|
+
not carry a runtime-capability recommendation a Codex consumer cannot execute.
|
|
49
|
+
- **`/new` + `/new2` remaining-waves.** `/new`'s Step F.6 "Prossimi Passi" and `/new2`'s
|
|
50
|
+
`buildReport()` "Epic goal" render the remaining waves BY NAME (fallback to the flat card list
|
|
51
|
+
when the epic has no wave tags — no regression). `/new2` runs fs-less, so the `wave_name` is
|
|
52
|
+
threaded through `cardGraph` (new `waveName` node field derived by the preflight agent).
|
|
53
|
+
- Project-specific between-wave cautions (migration/deploy gate, compile check, stash) stay in
|
|
54
|
+
`.baldart/overlays/prd.md` and are surfaced at render-time — no stack names in the base.
|
|
55
|
+
|
|
56
|
+
No new `baldart.config.yml` key (rides on the existing `execution_strategy` + `features.*`) → the
|
|
57
|
+
schema-change propagation rule does NOT apply. **Codex parity: portable** (runtime-neutral field;
|
|
58
|
+
engine computed at render-time; `/new2` remaining-waves via `cardGraph`).
|
|
59
|
+
|
|
60
|
+
Skills bumped: `prd` 1.6.1→1.7.0, `new` 2.3.1→2.4.0, `new2` 1.8.1→1.9.0.
|
|
61
|
+
|
|
8
62
|
## [5.7.0] - 2026-07-06
|
|
9
63
|
|
|
10
64
|
**Feature-gated skill linking — stop wasting Codex's 2% skill-description budget.** Codex
|
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ No additional activation steps needed — once installed, Claude Code (and Codex
|
|
|
92
92
|
1. **codebase-architect**: MANDATORY before planning/implementation - understands codebase structure
|
|
93
93
|
2. **coder**: Writes production code with build/test/lint verification + Post-Approval Complexity Gate
|
|
94
94
|
3. **code-reviewer**: Reviews for bugs, security, quality, maintainability — confidence-based filtering + Findings Schema
|
|
95
|
-
4. **security-reviewer**: Dedicated AppSec auditor for auth/secrets/multi-tenant/infra
|
|
95
|
+
4. **security-reviewer**: Dedicated AppSec auditor for auth/secrets/multi-tenant/infra — boundary-gate → repo-scoped threat-model → modern high-miss discovery-lens → class proof-tuples → structured attack-path → adversarial-refute (`security-review-protocol.md`)
|
|
96
96
|
5. **doc-reviewer**: Audits and writes documentation, SSOT sync, doc debt tracking, design-system drift detection
|
|
97
97
|
6. **wiki-curator**: Maintains the derived LLM wiki overlay (`docs/wiki/`) — synthesis candidates, anchor + frontmatter validation
|
|
98
98
|
7. **prd**: Creates PRDs, implementation plans, and backlog cards with autonomous tech decisions
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.9.0
|
|
@@ -7,6 +7,24 @@ per-item agent merge, the doctor probes, and the discovery hooks — like
|
|
|
7
7
|
|
|
8
8
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · per-agent SemVer.
|
|
9
9
|
|
|
10
|
+
## [security-reviewer v1.0.0] — 2026-07-06 (framework v5.9.0 — AppSec state-of-the-art wave)
|
|
11
|
+
|
|
12
|
+
- **First versioned entry.** Benchmarked against Codex `codex-security` (phased
|
|
13
|
+
workbench) + Claude Code `security-guidance` (agentic self-refute); imported the
|
|
14
|
+
gap as a single-sourced protocol module rather than a monolithic rewrite.
|
|
15
|
+
- **New "Security Review Passes (BINDING)" block** — cites the new
|
|
16
|
+
`agents/security-review-protocol.md` with 1-line binding statements per section
|
|
17
|
+
(boundary-gate, threat-model, discovery-lens, proof-tuples, attack-path,
|
|
18
|
+
adversarial-refute, coverage-ledger). Agent-spine 3-layer: core inline + module
|
|
19
|
+
on demand. EXTENDS `review-protocol.md`, never duplicates it.
|
|
20
|
+
- **Mode A pooled YAML schema extended**: `vuln_class`, `cwe: [CWE-###]`,
|
|
21
|
+
`validation_method`, structured `attack_path` block; summary header gains a
|
|
22
|
+
compact `coverage:` block. Other reviewers ignore the security-only fields.
|
|
23
|
+
- **Secret-redaction BINDING** added to Behavior Rules — a leaked credential is
|
|
24
|
+
reported as first 2–4 chars + `****`, never the live value.
|
|
25
|
+
- Codex parity: agent body transpiles to `.codex/agents/security-reviewer.toml`
|
|
26
|
+
automatically; the module is portable markdown. No new config key.
|
|
27
|
+
|
|
10
28
|
## [prd-card-writer v1.2.0] — 2026-07-04 (framework v5.6.0 — FEAT-0068 post-mortem)
|
|
11
29
|
|
|
12
30
|
- **Epic AC rules**: mai count di figli hardcodato ("le 6 sub-card"); AC con
|
|
@@ -754,6 +754,29 @@ The consumer (`new/SKILL.md`) reads `execution_strategy.groups[].level` and
|
|
|
754
754
|
`.cards`. Use these exact keys — `groups`/`level`, not the legacy
|
|
755
755
|
`parallel_groups`/`group`.
|
|
756
756
|
|
|
757
|
+
**Launch-wave sub-fields (`wave` / `wave_name` / `wave_rationale`) — write ONLY if handed to you.**
|
|
758
|
+
The `/prd` orchestrator may pass a launch-wave partition (a run-sequencing judgment it computes
|
|
759
|
+
AFTER you return the Parallel Execution Map — it needs discovery context you do not hold). When
|
|
760
|
+
and only when the orchestrator hands you these values, add them as optional sub-keys on the
|
|
761
|
+
matching `groups[]` entries:
|
|
762
|
+
|
|
763
|
+
```yaml
|
|
764
|
+
groups:
|
|
765
|
+
- level: 0
|
|
766
|
+
cards: [FEAT-XXXX-01]
|
|
767
|
+
description: "Foundation/scaffold"
|
|
768
|
+
wave: 1 # only if passed
|
|
769
|
+
wave_name: "Fondamenta server" # only if passed
|
|
770
|
+
wave_rationale: "…" # only if passed
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
If the orchestrator does NOT hand you a wave partition, **OMIT all three keys** — do NOT invent a
|
|
774
|
+
wave grouping, a name, or a rationale (a launch judgment fabricated from the card graph alone is
|
|
775
|
+
worse than absent; single-run epics legitimately carry no wave tags). This is the same
|
|
776
|
+
compute-by-orchestrator / persist-by-writer split as `provenance.planning_session`. Schema + the
|
|
777
|
+
render-time derivation live in [`framework/agents/card-schema.md`](../../agents/card-schema.md)
|
|
778
|
+
§ "`execution_strategy.groups[].wave*`".
|
|
779
|
+
|
|
757
780
|
## Output Format
|
|
758
781
|
|
|
759
782
|
**Self-validation BEFORE returning (v5.6.0 — you own your domain's deterministic hygiene).**
|
|
@@ -4,6 +4,7 @@ description: "Use this agent when code needs a security review, when reviewing P
|
|
|
4
4
|
model: sonnet
|
|
5
5
|
color: red
|
|
6
6
|
memory: project
|
|
7
|
+
version: 1.0.0
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
> **YOLO MODE**: All agents spawned via the Task tool MUST use `mode: "bypassPermissions"`. No exceptions.
|
|
@@ -73,6 +74,7 @@ file you EXPECT to match is a binary-mode trap, not evidence: re-run with `rg` (
|
|
|
73
74
|
- NEVER hand-wave or give shallow "looks good" feedback.
|
|
74
75
|
- NEVER approve insecure code because it is "probably internal".
|
|
75
76
|
- NEVER recommend storing secrets in code, env files committed to git, client-side code, or logs.
|
|
77
|
+
- **Secret redaction (BINDING).** When you report a leaked/hardcoded credential, NEVER write its full value — redact to the first 2–4 characters + `****`. Cite the `file:line` (the canonical source location), state what the credential grants and whether it is prod vs test, and recommend rotation if it is live. A review artifact that reproduces a live secret is itself a leak.
|
|
76
78
|
- NEVER suggest disabling security controls for convenience unless explicitly discussing a temporary local-only dev workaround, clearly labeled as unsafe.
|
|
77
79
|
|
|
78
80
|
## Threat-Modeling Mindset
|
|
@@ -88,6 +90,43 @@ For every review, actively reason about:
|
|
|
88
90
|
- Insider threat / compromised service misuse
|
|
89
91
|
- Multi-tenant isolation boundaries (critical for any multi-customer platform)
|
|
90
92
|
|
|
93
|
+
## Security Review Passes (BINDING — full procedure in the protocol module)
|
|
94
|
+
|
|
95
|
+
These extend `review-protocol.md`'s generic passes (Challenge / Simulation /
|
|
96
|
+
CoVe / risk-scoring) with the AppSec specifics. Keep the 1-line binding here;
|
|
97
|
+
Read the matching section of `agents/security-review-protocol.md SECTION=<name>`
|
|
98
|
+
for the procedure. Order: boundary-gate (once) → threat-model → discovery-lens →
|
|
99
|
+
per candidate proof-tuples → attack-path → adversarial-refute → mechanical
|
|
100
|
+
severity → coverage-ledger.
|
|
101
|
+
|
|
102
|
+
- **BINDING — boundary-gate**: before confirming anything, read `SECURITY.md` /
|
|
103
|
+
deployment notes and classify the surface (`product_surface`, `source_trust`,
|
|
104
|
+
`boundary_crossed`); no boundary crossed → not a finding. `SECTION=boundary-gate`.
|
|
105
|
+
- **BINDING — threat-model**: adopt or generate a REPOSITORY-scoped threat model
|
|
106
|
+
(never diff-biased); it picks your focus paths. `SECTION=threat-model`.
|
|
107
|
+
- **BINDING — discovery-lens**: sweep the modern high-miss patterns (fail-open,
|
|
108
|
+
allowlist-escape, control-regression, gate/action mismatch, parser
|
|
109
|
+
differential, sensitive-to-observability, CI/CD trust, IaC omitted arg,
|
|
110
|
+
over-broad grant, stale identity, registry fanout) beyond the OWASP core.
|
|
111
|
+
`SECTION=discovery-lens`.
|
|
112
|
+
- **BINDING — proof-tuples**: ground every finding in its class-specific tuple
|
|
113
|
+
(authz = path + missing guard + protected object; injection = bytes + sanitizer
|
|
114
|
+
result + sink; SSRF; deserialization; auth/token; XSS); confidence is anchored
|
|
115
|
+
to validation METHOD, not to how scary the bug class sounds; never collapse
|
|
116
|
+
independently exploitable sibling instances. `SECTION=proof-tuples`.
|
|
117
|
+
- **BINDING — attack-path**: write structured attack-path facts (attacker,
|
|
118
|
+
entrypoint, preconditions, dataflow source→sink, outcome, impact×likelihood)
|
|
119
|
+
BEFORE severity, then apply severity mechanically. `SECTION=attack-path`.
|
|
120
|
+
- **BINDING — adversarial-refute**: a finding SURVIVES unless refuted; test the
|
|
121
|
+
privilege boundary FIRST (attacker == victim on own machine → refute); keep
|
|
122
|
+
precondition (narrows likelihood) distinct from counterevidence (kills the
|
|
123
|
+
finding). In diff review, flag only newly-introduced/reachable surface.
|
|
124
|
+
`SECTION=adversarial-refute`.
|
|
125
|
+
- **BINDING — coverage-ledger**: record a disposition for every surface in scope
|
|
126
|
+
(`reported` / `no_issue_found` / `not_applicable` / `needs_follow_up` /
|
|
127
|
+
`deferred`) so a clean verdict is honest and a gap is never silent; zero
|
|
128
|
+
findings on a fully-covered surface is a legitimate outcome. `SECTION=coverage-ledger`.
|
|
129
|
+
|
|
91
130
|
## Review Methodology
|
|
92
131
|
|
|
93
132
|
For each file, code block, PR, or diff you review:
|
|
@@ -129,18 +168,27 @@ determine as `N/A` rather than dropping the finding):
|
|
|
129
168
|
title: <one-line>
|
|
130
169
|
source: security-reviewer
|
|
131
170
|
category: security
|
|
171
|
+
vuln_class: <concrete class, e.g. "Authorization bypass / IDOR" — never a generic "security finding">
|
|
172
|
+
cwe: [<CWE-###>, ...] # e.g. [CWE-22, CWE-434]; [] if none maps cleanly
|
|
132
173
|
target: <one of the orchestrator's TARGET TAG values, or "notes" for informational>
|
|
133
174
|
severity: BLOCKER | HIGH | MEDIUM | LOW
|
|
134
|
-
confidence: 0-100
|
|
175
|
+
confidence: 0-100 # anchored to validation_method (proof-tuples), not to the bug class
|
|
135
176
|
evidence:
|
|
136
177
|
file: <path or "N/A">
|
|
137
178
|
lines: <range or "N/A">
|
|
138
179
|
quote: |
|
|
139
180
|
<exact code snippet, ≤8 lines>
|
|
140
181
|
cove_verified: true | false # true if you verified file/line via Glob/Grep/Read
|
|
182
|
+
validation_method: <poc | test | debugger | interface-repro | static-trace | code-understanding>
|
|
141
183
|
repro_steps: <exploitation scenario — how an attacker reaches and triggers this>
|
|
142
184
|
expected_behavior: <the secure behavior>
|
|
143
185
|
actual_behavior: <the vulnerable behavior present in the code>
|
|
186
|
+
attack_path: # structured facts → severity falls out mechanically
|
|
187
|
+
attacker: <who, trust level, from where>
|
|
188
|
+
entrypoint: <exact reachable entry — route/RPC/CLI/webhook/MCP tool>
|
|
189
|
+
preconditions: <conditions required; "none" if unconditional>
|
|
190
|
+
dataflow: <source → sink, one line>
|
|
191
|
+
outcome: <concrete impact — data read/written, code executed, tenant crossed>
|
|
144
192
|
risk:
|
|
145
193
|
impact: 1-5
|
|
146
194
|
likelihood: 1-5
|
|
@@ -148,6 +196,10 @@ determine as `N/A` rather than dropping the finding):
|
|
|
148
196
|
recommendation: <concrete, minimal, production-ready fix; ≤3 sentences>
|
|
149
197
|
```
|
|
150
198
|
|
|
199
|
+
The `# Security Review Summary` header that follows the YAML list carries a
|
|
200
|
+
compact `coverage:` block (surfaces + dispositions per `SECTION=coverage-ledger`)
|
|
201
|
+
so the orchestrator can tell a clean verdict from an incomplete one.
|
|
202
|
+
|
|
151
203
|
Map your severity labels to the pooled enum: Critical → `BLOCKER`, High → `HIGH`, Medium →
|
|
152
204
|
`MEDIUM`, Low/Informational → `LOW`. Follow the YAML list with a 3–4 line plain-text
|
|
153
205
|
`# Security Review Summary` (scope / overall risk / main attack surfaces / most critical concern) so
|
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 2.4.0 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Prossime wave per NOME nel messaggio finale.** Il Step F.6 "Prossimi Passi"
|
|
8
|
+
(`references/final-review.md`) ora è **wave-aware**: legge la partizione di lancio persistita
|
|
9
|
+
da `/prd` sull'epic card (sotto-campi opzionali `wave` / `wave_name` su
|
|
10
|
+
`execution_strategy.groups[]`) e, quando presente, mostra le **wave rimanenti per nome**
|
|
11
|
+
("Prossime wave: **Wave 2 · Superfici UI**") invece della sola lista card piatta — così, dopo
|
|
12
|
+
aver implementato un subset (es. `/new FEAT-0069-01..06`), il riepilogo dice cosa resta per
|
|
13
|
+
chiudere l'epic senza doverlo ricordare. Engine della prossima wave suggerito runtime-aware
|
|
14
|
+
(Codex → solo `/new`). Richiede un Read esplicito dell'epic card a F.6 (non è in contesto:
|
|
15
|
+
letto solo transitoriamente in Phase 0). **Fallback grazioso** alla lista card piatta quando
|
|
16
|
+
l'epic non ha tag wave (single-run / legacy) → nessuna regressione. Schema SSOT:
|
|
17
|
+
`framework/agents/card-schema.md` § `execution_strategy.groups[].wave*`.
|
|
18
|
+
- Codex parity: portable (il campo è runtime-neutral; l'engine è calcolato a render-time).
|
|
19
|
+
|
|
5
20
|
## 2.3.1 — 2026-07-06
|
|
6
21
|
|
|
7
22
|
- **Feature-gate**: added `requires_feature: has_backlog` frontmatter — this skill is now linked into a consumer only when `features.has_backlog` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
@@ -439,7 +439,42 @@ that is a **gate violation**: log it as
|
|
|
439
439
|
| xargs grep -L "status: DONE"
|
|
440
440
|
```
|
|
441
441
|
|
|
442
|
-
|
|
442
|
+
**Wave-aware next steps (v5.8.0).** `/prd` may have persisted a **launch-wave partition** on the
|
|
443
|
+
epic card — optional `wave` / `wave_name` sub-keys on `execution_strategy.groups[]` (schema:
|
|
444
|
+
`framework/agents/card-schema.md` § "`execution_strategy.groups[].wave*`"). The epic card is NOT
|
|
445
|
+
in context here (it was read only transiently in Phase 0) — so **Read the epic card now**
|
|
446
|
+
(`${paths.backlog_dir}/<EPIC-ID>.yml`) to fetch the wave tags. Then:
|
|
447
|
+
- **If wave tags exist:** map each remaining not-DONE card to its `wave_name` (via the group whose
|
|
448
|
+
`.cards` contains it), and render the **remaining WAVES by name** instead of a flat card list — a
|
|
449
|
+
wave is "rimanente" when ≥1 of its cards is not-DONE. This keeps the thread across runs: the user
|
|
450
|
+
recognises "Wave 2 · Superfici UI" from the `/prd` plan. Suggest the engine for the NEXT wave
|
|
451
|
+
runtime-aware (Claude → `/new2` for a high-volume wave, else `/new`; Codex → `/new` only —
|
|
452
|
+
`/new2` is Claude-only).
|
|
453
|
+
- **If NO wave tags** (legacy epic / single-run): render the flat "Card rimanenti nell'epic" table
|
|
454
|
+
exactly as before (backwards-compat, no regression).
|
|
455
|
+
|
|
456
|
+
Present the section — WAVE-AWARE form (when wave tags exist):
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
## Prossimi Passi
|
|
460
|
+
|
|
461
|
+
### Prossime wave da sviluppare
|
|
462
|
+
| Wave | Nome | Card rimanenti | Engine suggerito |
|
|
463
|
+
|------|------|----------------|------------------|
|
|
464
|
+
| 2 | Superfici UI & closer | FEAT-XXXX-07…13 | /new2 |
|
|
465
|
+
|
|
466
|
+
### Lancio prossima wave (copia e incolla)
|
|
467
|
+
```
|
|
468
|
+
/new2 FEAT-XXXX-07-FEAT-XXXX-13
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Card completate in questa sessione
|
|
472
|
+
| Card | Titolo | Commit |
|
|
473
|
+
|------|--------|--------|
|
|
474
|
+
| FEAT-XXXX-01 | [title] | abc1234 |
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
FLAT form (no wave tags — unchanged):
|
|
443
478
|
|
|
444
479
|
```
|
|
445
480
|
## Prossimi Passi
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.9.0 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Prossime wave per NOME nel report finale (parità con `/new`).** `buildReport()` (`new2.js`)
|
|
8
|
+
ora, per ogni epic APERTA nella sezione "Epic goal", elenca le **wave rimanenti per nome**
|
|
9
|
+
dalla partizione di lancio persistita da `/prd` sull'epic. Poiché il workflow gira senza
|
|
10
|
+
accesso al filesystem, il `wave_name` è **portato via `cardGraph`** (nuovo campo `waveName`
|
|
11
|
+
sul nodo — l'agente preflight lo deriva dai `execution_strategy.groups[]` dell'epic). Scope
|
|
12
|
+
onesto: visibili solo le wave le cui card sono NEL BATCH (le wave interamente fuori dal batch
|
|
13
|
+
non sono caricate). `''` quando l'epic non ha tag wave (single-run / legacy) → nessuna riga.
|
|
14
|
+
- Codex parity: portable (il campo è runtime-neutral).
|
|
15
|
+
|
|
5
16
|
## 1.8.1 — 2026-07-06
|
|
6
17
|
|
|
7
18
|
- **Feature-gate**: added `requires_feature: has_backlog` frontmatter — this skill is now linked into a consumer only when `features.has_backlog` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.7.0 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Piano di implementazione suggerito nel messaggio finale (sempre).** Il run di `/prd` ora
|
|
8
|
+
chiude con una sezione **"🚀 Piano di implementazione suggerito"** che dice come lanciare
|
|
9
|
+
`/new` — unica run vs **wave con NOME**, confini, engine (`/new` vs `/new2`) — invece di
|
|
10
|
+
lasciare all'utente il compito di ricavarla a mano. La logica del piano è **persistita
|
|
11
|
+
sull'epic card** così `/new`/`/new2` a fine run possono ricordare quali wave restano.
|
|
12
|
+
- **Persistenza drift-free:** nessun blocco nuovo — sotto-campi OPZIONALI `wave` / `wave_name`
|
|
13
|
+
/ `wave_rationale` sui `groups[]` esistenti di `execution_strategy` (unica partizione di
|
|
14
|
+
card-ID; niente `waves[]` parallelo che drifta). Sono sotto-chiavi di un campo già `R` →
|
|
15
|
+
il baseline validator non le vede, **nessuna riga nuova nella field-state matrix**, epic
|
|
16
|
+
legacy invariate. Schema SSOT: `framework/agents/card-schema.md`.
|
|
17
|
+
- **Compute-by-orchestrator / persist-by-writer** (stesso split di `provenance.planning_session`):
|
|
18
|
+
l'**orchestratore `/prd`** calcola la partizione wave + nomi + rationale (giudizio di lancio
|
|
19
|
+
che serve il contesto di discovery — migration-early, pressione review `deep`, taglio
|
|
20
|
+
server/UI) e la persiste con un Edit mirato sull'epic card; il `prd-card-writer` scrive i
|
|
21
|
+
sotto-campi **solo se gli vengono passati**, altrimenti li omette (ramo else esplicito →
|
|
22
|
+
niente fabbricazione). Single-run resta il default: nessun tag wave.
|
|
23
|
+
- **Engine + hard-rule = render-time, non persistiti.** La scelta `/new` vs `/new2` è calcolata
|
|
24
|
+
a render ed è runtime-aware (Codex → solo `/new`, `/new2` è Claude-only) — un artefatto
|
|
25
|
+
cross-tool durabile non porta una raccomandazione che un consumer Codex non può eseguire.
|
|
26
|
+
- **Base generico + overlay:** cautele project-specific tra-wave (migration/deploy gate, compile
|
|
27
|
+
check, stash) restano in `.baldart/overlays/prd.md`, innestate a render-time.
|
|
28
|
+
- File: `references/backlog-phase.md` (compute+persist), `references/validation-phase.md`
|
|
29
|
+
(render), `assets/epic-template.yml` (chiavi commentate), `framework/agents/card-schema.md`
|
|
30
|
+
(schema), `framework/.claude/agents/prd-card-writer.md` (omit-if-not-passed).
|
|
31
|
+
- Codex parity: portable (campo runtime-neutral; engine calcolato a render-time).
|
|
32
|
+
|
|
5
33
|
## 1.6.1 — 2026-07-06
|
|
6
34
|
|
|
7
35
|
- **Feature-gate**: added `requires_feature: has_prd_workflow` frontmatter — this skill is now linked into a consumer only when `features.has_prd_workflow` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prd
|
|
3
3
|
effort: high
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
requires_feature: has_prd_workflow
|
|
6
6
|
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."
|
|
7
7
|
---
|
|
@@ -135,6 +135,12 @@ execution_strategy:
|
|
|
135
135
|
- level: 0
|
|
136
136
|
cards: [{{FEAT-XXXX-01}}]
|
|
137
137
|
rationale: "{{foundation / first dependency layer}}"
|
|
138
|
+
# Optional launch-wave tags — written by the /prd ORCHESTRATOR only (not the card-writer),
|
|
139
|
+
# and only when the epic is launched across multiple /new runs. Omit entirely for
|
|
140
|
+
# single-run epics. Schema: agents/card-schema.md § execution_strategy.groups[].wave*
|
|
141
|
+
# wave: 1
|
|
142
|
+
# wave_name: "{{Fondamenta server}}"
|
|
143
|
+
# wave_rationale: "{{migration + config + resolver + enforcement server}}"
|
|
138
144
|
- level: 1
|
|
139
145
|
cards: [{{FEAT-XXXX-02}}, {{FEAT-XXXX-03}}]
|
|
140
146
|
rationale: "{{parallel after L0}}"
|
|
@@ -290,6 +290,42 @@ The `prd-card-writer` agent owns the entire card writing pipeline:
|
|
|
290
290
|
- Presenting results to the user (the agent returns summary, main context displays it)
|
|
291
291
|
- Proceeding to Step 6 (validation phase)
|
|
292
292
|
|
|
293
|
+
## Launch-wave partition (orchestrator judgment — YOU compute it, not the card-writer)
|
|
294
|
+
|
|
295
|
+
After the card-writer returns the Parallel Execution Map, **you** (the `/prd` orchestrator)
|
|
296
|
+
decide how the epic should be **launched** across `/new` runs — a judgment the mechanical
|
|
297
|
+
card-writer cannot make because it needs the discovery context you hold (Tier, review-profile
|
|
298
|
+
mix, migration, UI/server split). This is the durable half of the "Piano di implementazione
|
|
299
|
+
suggerito" the final message renders (validation-phase.md § Final output).
|
|
300
|
+
|
|
301
|
+
**Decide single-run vs waves (scoped — default single-run).** Keep the epic as ONE run unless
|
|
302
|
+
at least one split trigger fires; if none fires, do NOT tag any wave (single-run is the explicit
|
|
303
|
+
else branch — never fabricate a wave grouping just to have one):
|
|
304
|
+
- **Large batch** — many children over several dependency levels (a long single run drifts the
|
|
305
|
+
trunk mid-batch → merge conflicts instead of fast-forward; keep this rationale generic, do not
|
|
306
|
+
name a stack).
|
|
307
|
+
- **Migration / schema card present** — land it in an EARLY wave so its migration/deploy gate runs
|
|
308
|
+
once and later waves build on the real schema.
|
|
309
|
+
- **Review pressure** — many `review_profile: deep` children; splitting keeps the orchestrator
|
|
310
|
+
context lucid on exactly the cards that need lucid review.
|
|
311
|
+
- **Clear server↔UI cut** — server/data foundation vs UI surfaces is a natural wave boundary
|
|
312
|
+
(UI/E2E cards exercise the server, so they belong in the later wave).
|
|
313
|
+
- **Cross-domain file conflicts** — `execution_strategy.file_conflicts` that straddle a domain
|
|
314
|
+
boundary force sequencing.
|
|
315
|
+
|
|
316
|
+
**Assign waves over the EXISTING groups (one partition, no re-listing of card IDs).** Waves are a
|
|
317
|
+
contiguous grouping of `execution_strategy.groups[]` in `level` order. For each group in a wave,
|
|
318
|
+
set `wave` (1-based, ascending), `wave_name` (a short, unique, human-memorable name derived from
|
|
319
|
+
the wave's dominant domain/role — e.g. "Fondamenta server", "Superfici UI & closer"), and
|
|
320
|
+
`wave_rationale` (one line). Persist them by **editing the epic card** (`backlog/<slug>-00.yml`)
|
|
321
|
+
in the worktree — a targeted Edit adding the three sub-keys to the matching `groups[]` entries.
|
|
322
|
+
Do NOT re-spawn the card-writer; do NOT create a parallel `waves[]` block. Schema:
|
|
323
|
+
`framework/agents/card-schema.md` § "`execution_strategy.groups[].wave*`".
|
|
324
|
+
|
|
325
|
+
**Do NOT persist the engine (`/new` vs `/new2`) or the hard rules** — those are render-time,
|
|
326
|
+
runtime-aware (validation-phase.md § Final output). On a single-run epic, skip this step entirely
|
|
327
|
+
(no wave keys written).
|
|
328
|
+
|
|
293
329
|
## Present and Proceed
|
|
294
330
|
|
|
295
331
|
Present the plan to the user (traceability matrix + parallel execution map) for
|
|
@@ -410,4 +410,34 @@ A run that prints "local `git.trunk_branch` did not fast-forward, handle the fil
|
|
|
410
410
|
yourself and then pull" has FAILED Step 7.5 — go back and resolve it before
|
|
411
411
|
printing the summary.
|
|
412
412
|
|
|
413
|
+
Then, ALWAYS, append the launch plan:
|
|
414
|
+
|
|
415
|
+
### 🚀 Piano di implementazione suggerito (MANDATORY — always render)
|
|
416
|
+
|
|
417
|
+
Derive it from the epic card's `execution_strategy` (the wave partition you persisted in
|
|
418
|
+
backlog-phase.md § "Launch-wave partition"); nothing here is recomputed from scratch. Rules +
|
|
419
|
+
schema: `framework/agents/card-schema.md` § "`execution_strategy.groups[].wave*`" (Launch waves).
|
|
420
|
+
|
|
421
|
+
- **Single-run** (≤1 distinct `wave_name`, e.g. a light-lane 1-child epic): render ONE line —
|
|
422
|
+
`**Lancia in un'unica run:** \`/new <all card IDs>\`` — no table. Stop here.
|
|
423
|
+
- **Waves** (≥2 distinct `wave_name`): render a table, one row per wave in `wave` order:
|
|
424
|
+
|
|
425
|
+
| Wave | Nome | Card | Contenuto |
|
|
426
|
+
|------|------|------|-----------|
|
|
427
|
+
| 1 | Fondamenta server | FEAT-XXXX-01…06 | <wave_rationale> |
|
|
428
|
+
| 2 | Superfici UI & closer | FEAT-XXXX-07…13 | <wave_rationale> |
|
|
429
|
+
|
|
430
|
+
Then, below the table:
|
|
431
|
+
- **Engine (runtime-aware — computed here, never read from the card).** On **Claude** (the
|
|
432
|
+
`Workflow` tool is available) recommend `/new2` for a high-volume wave (off-context batch, no
|
|
433
|
+
interactive gates) and `/new` (classic) for a wave that wants human gates (migration / risky
|
|
434
|
+
review) — a hybrid (`/new` on the risky early wave, `/new2` on the rest) is a valid call. On
|
|
435
|
+
**Codex** recommend `/new` only — `/new2` needs the `Workflow` tool and does not exist there
|
|
436
|
+
(`framework/agents/runtime-portability-protocol.md` § "Workflow acceleration"). Give the exact
|
|
437
|
+
command per wave, e.g. `/new2 FEAT-XXXX-01-FEAT-XXXX-06`.
|
|
438
|
+
- **Hard rule:** niente altro lavoro in parallelo sugli stessi file tra una wave e l'altra.
|
|
439
|
+
- **Post-wave gates:** if `.baldart/overlays/prd.md` declares project-specific between-wave
|
|
440
|
+
cautions (e.g. a migration/deploy gate, a compile check, a stash check), surface them after the
|
|
441
|
+
relevant wave. Omit this line when the overlay declares none — never invent stack-specific steps.
|
|
442
|
+
|
|
413
443
|
Display completed Progress Bar.
|
|
@@ -219,6 +219,11 @@ const PREFLIGHT_SCHEMA = {
|
|
|
219
219
|
// Drives the epic-closure blocklist (an epic with open scope residuals/follow-ups must
|
|
220
220
|
// not close) and the Epic goal section of the final report.
|
|
221
221
|
parentEpic: { type: 'string', description: "parent epic id from group.parent or parent: ('' when none)" },
|
|
222
|
+
// v5.8.0 — the launch-wave this card belongs to, IFF /prd persisted a wave partition on
|
|
223
|
+
// the epic card (optional wave_name on execution_strategy.groups[] whose .cards includes
|
|
224
|
+
// this id — schema: agents/card-schema.md § execution_strategy.groups[].wave*). '' when the
|
|
225
|
+
// epic has no wave tags (single-run / legacy). Drives the "Prossime wave" report line.
|
|
226
|
+
waveName: { type: 'string', description: "launch wave_name from the epic's execution_strategy.groups[] ('' when none)" },
|
|
222
227
|
},
|
|
223
228
|
},
|
|
224
229
|
},
|
|
@@ -305,7 +310,7 @@ try {
|
|
|
305
310
|
g3Bullet +
|
|
306
311
|
`• G4 card-field validation (setup.md 1b/1c, card-schema.md consumer contract): card missing acceptance_criteria/files_likely_touched/scope → EXCLUDE (excluded[] + reason). \`requirements\` is CONDITIONAL, not an auto-exclude: if it is missing BUT acceptance_criteria + scope are both present and non-empty, FAITHFULLY DERIVE it (a concrete restatement/decomposition of the existing AC + scope — faithful, never inventing new scope) and WRITE it back to the card YAML on disk in ${MAIN}/${paths.backlog_dir || 'backlog'} (F-040: main repo, not the worktree copy; the card artifact is NOT a source/doc file, so this is allowed despite the role boundary), logging \`[BACKFILL] <id>: requirements=<N items, AC+scope-derived>\`. Only EXCLUDE for missing requirements when acceptance_criteria OR scope is ALSO empty (a genuinely thin card). Never HALT for one bad card.\n` +
|
|
307
312
|
`• G5 depends-on: a card whose depends_on names a non-DONE card NOT in this batch → EXCLUDE it AND every in-batch card that transitively depends on it.\n` +
|
|
308
|
-
`• cardGraph (REQUIRED, F-021): for every runnable card return { id, dependsOn:[IN-BATCH deps only], ownerAgent (the card's owner_agent; G25 unknown→'coder'), hasMockup (BOOLEAN — true IFF the card BUILDS the mocked UI: links.design_src non-empty, OR links.design non-empty AND (component_bindings non-empty OR files_likely_touched intersects component/style/UI source paths). A bare links.design on a docs/test/meta-only card is a POINTER to the epic's explainer, not a build target — v5.6.0, FEAT-0068: the bare test clamped a docs+ADR card to ui-expert; drives the mockup-first build override — implement.md §6a), parentEpic (the card's parent epic id from group.parent or a top-level parent: field; '' when none — drives the epic-closure blocklist + the Epic goal report section), designSrcDir (ABS path of links.design_src / the PRD mockups/_src dir; '' when absent), designHtml (ABS path when links.design is an .html file; '' otherwise), hasBindings (BOOLEAN — non-empty component_bindings), reviewProfile (the card's review_profile; default 'balanced'), policyDeferredACs, alreadyCommitted, alreadyCommittedSha, isEpic (implement.md §6b epic guard: id ends '-00' OR filename ends '-epic.yml' OR group.is_epic:true OR review_profile 'skip' with no requirements), filesLikelyTouched (verbatim from the YAML) }.\n` +
|
|
313
|
+
`• cardGraph (REQUIRED, F-021): for every runnable card return { id, dependsOn:[IN-BATCH deps only], ownerAgent (the card's owner_agent; G25 unknown→'coder'), hasMockup (BOOLEAN — true IFF the card BUILDS the mocked UI: links.design_src non-empty, OR links.design non-empty AND (component_bindings non-empty OR files_likely_touched intersects component/style/UI source paths). A bare links.design on a docs/test/meta-only card is a POINTER to the epic's explainer, not a build target — v5.6.0, FEAT-0068: the bare test clamped a docs+ADR card to ui-expert; drives the mockup-first build override — implement.md §6a), parentEpic (the card's parent epic id from group.parent or a top-level parent: field; '' when none — drives the epic-closure blocklist + the Epic goal report section), waveName (v5.8.0 — when the parent epic card carries a launch-wave partition, the wave_name of the execution_strategy.groups[] entry whose .cards includes this id; '' when the epic has no wave tags — single-run/legacy; drives the "Prossime wave" report line), designSrcDir (ABS path of links.design_src / the PRD mockups/_src dir; '' when absent), designHtml (ABS path when links.design is an .html file; '' otherwise), hasBindings (BOOLEAN — non-empty component_bindings), reviewProfile (the card's review_profile; default 'balanced'), policyDeferredACs, alreadyCommitted, alreadyCommittedSha, isEpic (implement.md §6b epic guard: id ends '-00' OR filename ends '-epic.yml' OR group.is_epic:true OR review_profile 'skip' with no requirements), filesLikelyTouched (verbatim from the YAML) }.\n` +
|
|
309
314
|
`• B1/F-026 idempotency (per card, AFTER the worktree exists): set alreadyCommitted:true (+ alreadyCommittedSha) IFF ALL hold: (a) a commit referencing the card id exists in ${TRUNK}..HEAD of the worktree; (b) the card's validation_commands re-run GREEN right now; (c) NO open follow-up card for it exists in ${paths.backlog_dir || 'backlog'}. On a FRESH worktree ${TRUNK}..HEAD is empty → all false, zero extra work.\n` +
|
|
310
315
|
`• F-016 AC↔ownership consistency: for each acceptance_criterion, derive the file(s) it requires editing. If those files are NOT a subset of the card's MAY-EDIT/files_likely_touched → add the AC to policyDeferredACs:[{n,text,owningCard|owningFile,reason}] (it will become ONE follow-up, never a resolve). Do the same for any AC whose remedy is an owner-gated infra action (remote db push / deploy / secret / DNS).\n` +
|
|
311
316
|
(migrationApplied
|
|
@@ -1687,7 +1692,19 @@ function buildReport(o) {
|
|
|
1687
1692
|
const epicHigh = residuals.filter((r) => ((graphById[r.card] && graphById[r.card].parentEpic) === e || r.card === e) && (sevRank[residualSeverity(r)] || 0) >= 2)
|
|
1688
1693
|
if (closedEpics.includes(e) && !closureBlockedEpics.has(e)) L.push(`- **${e}: CHIUSA** (children DONE + AC epic verificati dal merge agent)`)
|
|
1689
1694
|
else if (closedEpics.includes(e)) L.push(`- **${e}: ⚠️ CHIUSA CONTRO BLOCKLIST (false DONE)** — riaprire: ${epicHigh.length} residuo/i HIGH aperti`)
|
|
1690
|
-
else
|
|
1695
|
+
else {
|
|
1696
|
+
L.push(`- **${e}: APERTA** — ${reason || (closureBlockedEpics.has(e) ? 'blocklist: residui/follow-up aperti' : 'merge non eseguito o children aperti')}${epicHigh.length ? ` · ${epicHigh.length} residuo/i HIGH` : ''}`)
|
|
1697
|
+
// v5.8.0 — remaining launch waves by NAME (parity with /new final-review F.6). Uses the
|
|
1698
|
+
// waveName threaded through cardGraph (buildReport has no fs access to re-read the epic).
|
|
1699
|
+
// Honest scope: only waves whose cards are IN THIS BATCH are visible — waves entirely
|
|
1700
|
+
// outside the batch aren't loaded, so this reflects the batch, not the whole epic.
|
|
1701
|
+
const openWaves = dedupe(
|
|
1702
|
+
cardGraph
|
|
1703
|
+
.filter((n) => (n.parentEpic || '') === e && n.waveName && (perCardResults.find((r) => r.card === n.id) || {}).status !== 'DONE')
|
|
1704
|
+
.map((n) => n.waveName)
|
|
1705
|
+
)
|
|
1706
|
+
if (openWaves.length) L.push(` - Prossime wave (batch): ${openWaves.map((w) => `**${w}**`).join(' · ')}`)
|
|
1707
|
+
}
|
|
1691
1708
|
}
|
|
1692
1709
|
}
|
|
1693
1710
|
if (finalSummary) {
|
|
@@ -230,6 +230,56 @@ Rules:
|
|
|
230
230
|
- Omit any sub-field whose session id is unavailable (env var unset) rather than writing a
|
|
231
231
|
placeholder.
|
|
232
232
|
|
|
233
|
+
### `execution_strategy.groups[].wave*` — launch-wave annotation (epic-only, optional, orchestrator-written)
|
|
234
|
+
|
|
235
|
+
The epic's `execution_strategy.groups[]` is the **single card-ID partition** consumed by `/new`
|
|
236
|
+
for parallelism (`groups[].level` + `.cards` — the machine schedule). A **launch wave** is a
|
|
237
|
+
coarser, human-facing partition layered ON TOP of those same groups so the user can sequence the
|
|
238
|
+
epic across multiple `/new` runs and refer to each run by a stable NAME. It is expressed as three
|
|
239
|
+
**optional sub-fields on the existing `groups[]` entries** — never a parallel block that re-lists
|
|
240
|
+
card IDs (that would be a second SSOT that drifts from `groups[].cards`):
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
execution_strategy:
|
|
244
|
+
recommended_mode: team | sequential # existing — scheduling shape, runtime-neutral
|
|
245
|
+
groups:
|
|
246
|
+
- level: 0
|
|
247
|
+
cards: [FEAT-0069-01, FEAT-0069-02] # existing — the ONLY card-ID partition
|
|
248
|
+
description: "Foundation/scaffold" # existing
|
|
249
|
+
wave: 1 # opt — 1-based wave index (contiguous over groups)
|
|
250
|
+
wave_name: "Fondamenta server" # opt — short, unique human NAME for the wave
|
|
251
|
+
wave_rationale: "migration + config + resolver + enforcement server" # opt — why this wave
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Rules:
|
|
255
|
+
- **Optional (`C`)**, epic-only. They are **sub-keys of `execution_strategy`** (already `R`), NOT
|
|
256
|
+
top-level fields — the field-state matrix carries **no new row** and the baseline validator (which
|
|
257
|
+
checks only top-level presence/non-emptiness) never sees them. Legacy epics lack them → no HALT,
|
|
258
|
+
no migration.
|
|
259
|
+
- **Ownership split (same as [`provenance`](#provenance-session-id-traceability--optional-skill-written)):**
|
|
260
|
+
the `/prd` **orchestrator** COMPUTES the wave partition + names + rationale (a launch judgment that
|
|
261
|
+
needs discovery context — migration-early, review-`deep` pressure, server↔UI cut — which the
|
|
262
|
+
mechanical `prd-card-writer` does not hold), and it is PERSISTED onto the epic card. The card-writer
|
|
263
|
+
emits these keys ONLY when handed them; otherwise it **omits** them.
|
|
264
|
+
- **Contiguity invariant:** waves partition the groups in `level` order — a wave is a contiguous run
|
|
265
|
+
of groups. `wave` numbers ascend 1,2,3…; every group in the same wave shares one `wave_name`.
|
|
266
|
+
- **Single-run:** ≤1 distinct `wave_name` across all groups (e.g. a light-lane 1-child epic) means
|
|
267
|
+
"launch in one run" — the orchestrator leaves the wave keys ABSENT rather than tagging everything
|
|
268
|
+
`wave: 1`.
|
|
269
|
+
|
|
270
|
+
**Launch waves (render-time — NOT persisted):** the human-facing launch plan is derived, never
|
|
271
|
+
stored beyond the three keys above:
|
|
272
|
+
- The **wave view** = group the epic's `groups[]` by `wave_name`, ordered by `wave`; each wave's
|
|
273
|
+
cards = the union of its groups' `.cards`. Consumers: `/prd` final message ("Piano di
|
|
274
|
+
implementazione suggerito") and `/new`/`/new2` final message ("prossime wave rimanenti", joining
|
|
275
|
+
wave membership with each card's on-disk `status`).
|
|
276
|
+
- The **engine recommendation** (`/new` vs `/new2`) is computed at render time and is **runtime-aware**
|
|
277
|
+
— `/new2` needs the Workflow tool and is **Claude-only** (see `framework/agents/runtime-portability-protocol.md`
|
|
278
|
+
§ "Workflow acceleration" — *no Codex equivalent*). It is **never persisted** on the card: a durable,
|
|
279
|
+
cross-tool artifact must not carry a runtime-capability recommendation a Codex consumer cannot execute.
|
|
280
|
+
- The anti-parallel hard rule ("no other work on the same files between waves") and any project-specific
|
|
281
|
+
post-wave gates (surfaced from `.baldart/overlays/prd.md`) are render-time constants, likewise not persisted.
|
|
282
|
+
|
|
233
283
|
## Acceptance-criteria grammar (EARS) + per-AC oracles (since the EARS+verify wave)
|
|
234
284
|
|
|
235
285
|
**Why**: a prose AC passes gates without being verifiable ("handle it correctly"), and
|
|
@@ -37,7 +37,7 @@ Route agents to the right module with minimal reading.
|
|
|
37
37
|
- If touching terminology or naming -> read `agents/coding-standards.md`.
|
|
38
38
|
- If touching env vars or scripts -> read `agents/runbook.md` and `agents/env-reference.md`.
|
|
39
39
|
- If touching operational procedures (backup, cleanup, secrets) -> read `docs/operations/` if it exists in your project.
|
|
40
|
-
- If touching security or auth risk -> read `agents/security.md
|
|
40
|
+
- If touching security or auth risk -> read `agents/security.md` (OWASP 2021 posture + secure-defaults). If performing a dedicated AppSec review (the `security-reviewer` agent, `/codexreview` security pass) -> also read `agents/security-review-protocol.md` (the matching `SECTION=` only): boundary-gate → repo-scoped threat-model → high-miss discovery-lens → class proof-tuples → structured attack-path → adversarial-refute (attacker≠victim) → coverage-ledger. It EXTENDS `review-protocol.md` (generic passes), never duplicates it.
|
|
41
41
|
- If touching performance limits or scaling -> read `agents/performance.md`.
|
|
42
42
|
- If touching monitoring/logging -> read `agents/observability.md`.
|
|
43
43
|
- If tuning reasoning depth — setting a skill's `effort:` baseline or honoring an inline `effort=<level>` override -> read `agents/effort-protocol.md`.
|
|
@@ -86,6 +86,7 @@ When adding or updating agents, update REGISTRY.md — not this file.
|
|
|
86
86
|
- `agents/runtime-portability-protocol.md` — Runtime-mechanics binding: the abstract-operation ↔ Claude/Codex map (spawn / permissions / workflow-accel / state-spine / decision-gate / read-write path / adversarial-vs-cross-model), detect-once capability contract; cited by `/new` + `/prd`. The runtime-mechanics twin of `effort-protocol.md` + `return-contract-protocol.md` (since the Codex-parity S4 wave)
|
|
87
87
|
- `agents/agent-operating-protocol.md` — Shared operating procedures for the core agents (injection guard, doc-retrieval consumption, persistent-memory hygiene, tool-budget discipline), `SECTION=` dispatch, read only the matching section; agents keep 1-line binding versions inline (since v5.0.0)
|
|
88
88
|
- `agents/review-protocol.md` — The shared verification engine for reviewers: Challenge + Actionability, Simulation (diff-walk / plan-walk), Chain-of-Verification, quantified risk scoring + absolute severity calibration, specialist-spawn discipline incl. orchestrated-mode `specialist_dispatch` suppression; `SECTION=` dispatch (since v5.0.0)
|
|
89
|
+
- `agents/security-review-protocol.md` — The AppSec verification engine for `security-reviewer`: boundary-gate, repository-scoped threat-model, modern high-miss discovery-lens (fail-open / allowlist-escape / control-regression / gate-action mismatch / parser differential / CI-CD trust / IaC omitted arg / over-broad grant), class-specific proof-tuples (method-anchored confidence, instance-preserving), structured attack-path → mechanical severity, adversarial-refute (attacker≠victim, precondition vs counterevidence), coverage-ledger; `SECTION=` dispatch. EXTENDS `review-protocol.md`, never duplicates it. Portable Claude+Codex (since v5.9.0)
|
|
89
90
|
- `agents/doc-audit-protocol.md` — doc-reviewer's audit-mode procedures (drift validator suite, topological generation, epistemic metadata, SCIP anchors, schema/registry drift, coverage gauges); read ONLY when invoked without card context; `SECTION=` dispatch (since v5.0.0)
|
|
90
91
|
- `agents/research-protocol.md` — Research discipline: `PROFILE=<decision|deep|compare|regulatory>` output contracts (+ `DEPTH=` iterative loop), the research library (`paths.research_dir` — layout, report frontmatter, INDEX, archive-not-delete), the reuse pre-flight (`FULL_REUSE`/`DELTA`/`NEW` + per-category TTLs), and the versioned source matrix with its growth loop (`SOURCE_MATRIX_CANDIDATE`); `SECTION=` dispatch. The research-side sibling of `analysis-profiles.md` (since v5.1.0)
|
|
91
92
|
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Security Review Protocol — the AppSec verification engine
|
|
2
|
+
|
|
3
|
+
**Purpose**: the security-specific passes that turn a generic code review into
|
|
4
|
+
an adversarial AppSec audit — repository-scoped threat modeling, the modern
|
|
5
|
+
high-miss discovery lens, class-specific proof tuples, structured attack-path →
|
|
6
|
+
mechanical severity, adversarial self-refutation (attacker vs victim), and a
|
|
7
|
+
coverage ledger so *"not observed" never reads as "not reviewed"*. Distilled
|
|
8
|
+
from the Codex `codex-security` workbench + the Claude Code `security-guidance`
|
|
9
|
+
agentic reviewer. This module is the **single SSOT** of that methodology.
|
|
10
|
+
|
|
11
|
+
**Relationship to `review-protocol.md`**: that module owns the GENERIC verifier
|
|
12
|
+
passes (Challenge, Actionability, Simulation, Chain-of-Verification, risk
|
|
13
|
+
scoring). This module **extends**, never duplicates them: `adversarial-refute`
|
|
14
|
+
is the security-specialized layer that runs *inside* the generic Challenge pass;
|
|
15
|
+
`proof-tuples` is the security grounding for the generic CoVe pass. On the
|
|
16
|
+
generic passes, `review-protocol.md` wins; on AppSec specifics, this module
|
|
17
|
+
wins.
|
|
18
|
+
|
|
19
|
+
**Consumer**: `security-reviewer` (body citations). Portable as-is across Claude
|
|
20
|
+
Code and Codex (read at runtime by both).
|
|
21
|
+
|
|
22
|
+
## Contract
|
|
23
|
+
|
|
24
|
+
- **Dispatch**: cite `agents/security-review-protocol.md SECTION=<threat-model|discovery-lens|proof-tuples|attack-path|adversarial-refute|coverage-ledger|boundary-gate>`.
|
|
25
|
+
Grep for `### SECTION: <name>` and Read ONLY that section.
|
|
26
|
+
- **Order of passes** (normative): `boundary-gate` (once, at kickoff) →
|
|
27
|
+
`threat-model` (adopt or generate) → discovery under the `discovery-lens` →
|
|
28
|
+
per candidate: `proof-tuples` (validation) → `attack-path` (structured facts)
|
|
29
|
+
→ `adversarial-refute` → mechanical severity (`review-protocol.md
|
|
30
|
+
SECTION=risk-scoring`) → `coverage-ledger` close. The generic Challenge + CoVe
|
|
31
|
+
passes still run; the security passes feed them.
|
|
32
|
+
- **Degrade-safe**: skipping a pass means less rigor, never a malformed report —
|
|
33
|
+
the report/YAML shape stays defined in the agent body.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### SECTION: boundary-gate
|
|
38
|
+
|
|
39
|
+
Run ONCE before confirming any finding. A vulnerability only exists when an
|
|
40
|
+
attacker crosses a trust boundary of a **shipped/deployed** surface — so
|
|
41
|
+
classify the surface first, or you will flag same-privilege local code as a
|
|
42
|
+
"vuln".
|
|
43
|
+
|
|
44
|
+
1. **Read the repo's own policy FIRST**: `SECURITY.md`, `AGENTS.md` deployment
|
|
45
|
+
notes, package metadata. They define which surfaces are in-scope and what the
|
|
46
|
+
supported threat model is. A finding that contradicts a documented,
|
|
47
|
+
intentional design is a policy question, not a confirmed vuln.
|
|
48
|
+
2. **Classify the surface under review**:
|
|
49
|
+
- `product_surface`: hosted service | library API | CLI | local dev UI |
|
|
50
|
+
MCP/tooling | example/demo | test | docs | generated | vendored.
|
|
51
|
+
- `source_trust` of the input: untrusted (remote/attacker) |
|
|
52
|
+
trusted_operator | trusted_developer_config | local_only |
|
|
53
|
+
intentional_code_execution.
|
|
54
|
+
- `boundary_crossed`: does attacker-controlled input reach the sink across a
|
|
55
|
+
real privilege/trust boundary? true | false | unknown.
|
|
56
|
+
3. **Gate**: no boundary crossed (attacker == victim, same privilege, local dev
|
|
57
|
+
tool with no request handler) → NOT a finding. Record the disposition in the
|
|
58
|
+
coverage ledger, do not emit it as LOW.
|
|
59
|
+
|
|
60
|
+
### SECTION: threat-model
|
|
61
|
+
|
|
62
|
+
Establish (or adopt) a **repository-scoped** threat model — deliberately NOT
|
|
63
|
+
biased by the current diff, so it stays valid for any change in the same repo.
|
|
64
|
+
|
|
65
|
+
- **Adopt** an existing model from the user, `SECURITY.md`, or `AGENTS.md` when
|
|
66
|
+
present. Only **generate** a fallback when none exists.
|
|
67
|
+
- Fallback generation, repository-level (not diff-scoped):
|
|
68
|
+
1. Real-world purpose + primary product/runtime surfaces actually exposed
|
|
69
|
+
(separate them from test/demo/tooling/docs paths).
|
|
70
|
+
2. Trust boundaries + the actors on each side; separate **attacker-controlled**
|
|
71
|
+
vs **operator-controlled** vs **developer-controlled** inputs explicitly.
|
|
72
|
+
3. Sensitive assets: user data, auth artifacts, authorization state,
|
|
73
|
+
secrets/keys, config, models/weights, audit logs, availability-critical
|
|
74
|
+
resources, tenant-isolation state.
|
|
75
|
+
4. Vulnerability CLASSES relevant to this repo's context (not the diff's
|
|
76
|
+
findings) + existing mitigations grounded in real files.
|
|
77
|
+
5. When realistic vs out-of-scope: state attacker (non-)capabilities so
|
|
78
|
+
severity is not inflated.
|
|
79
|
+
- Use it to pick **focus paths** for the review; do not let the diff recenter it
|
|
80
|
+
unless the user asks for a diff-scoped model.
|
|
81
|
+
|
|
82
|
+
### SECTION: discovery-lens
|
|
83
|
+
|
|
84
|
+
Beyond the OWASP core checklist (in the agent body), sweep for the **modern
|
|
85
|
+
high-miss patterns** — the ones a generic checklist walks past. For each changed
|
|
86
|
+
region, ask:
|
|
87
|
+
|
|
88
|
+
- **FAIL-OPEN state drift**: a default that *allows* on the unknown/error path —
|
|
89
|
+
catch-all `default: allow`, `unwrap_or({})`, a removed `finally` that left the
|
|
90
|
+
deny, an early-return before the check. Security must fail *closed*.
|
|
91
|
+
- **ALLOWLIST semantic escape**: a matcher weakened — a `||`/disjunction added, a
|
|
92
|
+
regex left unanchored (`^…$` missing), an allowlist checked by `startsWith`/
|
|
93
|
+
substring, a URL trusted by substring instead of parsed host.
|
|
94
|
+
- **CONTROL regression**: a deny-by-default replaced by a single positive
|
|
95
|
+
condition; an authz/confirm/audit control loosened or removed in the diff.
|
|
96
|
+
- **GATE / ACTION field mismatch**: the gate checks resource A (parent, id X) but
|
|
97
|
+
the action writes/reads resource B (child, id Y) — the check and the effect
|
|
98
|
+
disagree.
|
|
99
|
+
- **UNDER-VALIDATED sink arg**: user data flows into sink arg *X* while a sibling
|
|
100
|
+
path validates arg *Y* — asymmetry between siblings is the tell.
|
|
101
|
+
- **PARSER/VALIDATOR differential**: the validator and the consumer parse the
|
|
102
|
+
input differently (two URL parsers, JSON vs form, unicode/normalization) — the
|
|
103
|
+
gap is the exploit.
|
|
104
|
+
- **SENSITIVE-to-observability**: secrets/tokens/PII reaching logs, traces,
|
|
105
|
+
error responses, analytics, crash dumps.
|
|
106
|
+
- **CI/CD trust**: `pull_request_target` / `workflow_dispatch` / injectable
|
|
107
|
+
`${{ github.event.*.body }}` combined with secrets or repo-write; a workflow
|
|
108
|
+
running untrusted code with elevated tokens.
|
|
109
|
+
- **IaC omitted arg**: a Terraform/CDK/K8s resource missing a security flag
|
|
110
|
+
(public bucket, open SG, no encryption, over-broad IAM `*`).
|
|
111
|
+
- **OVER-BROAD grant**: an admin/`*` role/scope where a narrower one exists;
|
|
112
|
+
new IAM/RLS/role wider than the action needs.
|
|
113
|
+
- **STALE identity mapping**: a window between unregister/rotate and the new
|
|
114
|
+
binding where the old identity still resolves.
|
|
115
|
+
- **SECURITY-REGISTRY fanout**: a new entity type (model, scope, route, event)
|
|
116
|
+
added but missing from the sanitizer/allowlist/authz registries that must
|
|
117
|
+
enumerate it.
|
|
118
|
+
- **RESOURCE-bound placement**: a cap/limit on the wrong accumulator; an
|
|
119
|
+
under/overflow; a size check after the allocation.
|
|
120
|
+
|
|
121
|
+
### SECTION: proof-tuples
|
|
122
|
+
|
|
123
|
+
A security finding is a *hypothesis* until its **class-specific proof tuple** is
|
|
124
|
+
grounded against real code (this is the AppSec form of the CoVe pass). Confirm
|
|
125
|
+
every element by grep/read before emitting; drop the finding if any element is
|
|
126
|
+
missing.
|
|
127
|
+
|
|
128
|
+
- **authz / IDOR**: attacker reachability path + the missing/bypassed guard + the
|
|
129
|
+
protected object or state it exposes.
|
|
130
|
+
- **injection** (SQL/NoSQL/OS/LDAP/template): attacker-controlled bytes + the
|
|
131
|
+
sanitizer/parameterization result (or its absence) + the dangerous sink.
|
|
132
|
+
- **deserialization**: attacker serialized input + the unsafe loader + the
|
|
133
|
+
execution/instantiation effect.
|
|
134
|
+
- **SSRF / open redirect**: attacker-controlled destination + the control bypass
|
|
135
|
+
+ the network reach / side effect.
|
|
136
|
+
- **auth / token**: attacker-supplied value + the validator's exact semantics +
|
|
137
|
+
the mismatch with the trusted value.
|
|
138
|
+
- **XSS**: attacker string + the encoding/escaping context at output + the sink
|
|
139
|
+
(`innerHTML`/`dangerouslySetInnerHTML`/template) with no encode.
|
|
140
|
+
|
|
141
|
+
**Confidence anchored to validation METHOD, never to how scary the bug class
|
|
142
|
+
sounds**:
|
|
143
|
+
- reproduced PoC / failing test that triggers the sink → confidence 90–100.
|
|
144
|
+
- debugger/trace or a proven interface reproduction → 75–90.
|
|
145
|
+
- static source→sink trace fully grounded in read code → 60–80.
|
|
146
|
+
- code-understanding argument with a defensible but not fully-traced path →
|
|
147
|
+
30–55. Below that, it is a hypothesis — do not ship as HIGH.
|
|
148
|
+
|
|
149
|
+
**Instance-preserving**: do NOT collapse independently exploitable sibling
|
|
150
|
+
instances (`execute` / `executemany` / `executescript`; two routes with the same
|
|
151
|
+
flaw) into one finding. Each independently triggerable sink gets its own
|
|
152
|
+
finding/closure row.
|
|
153
|
+
|
|
154
|
+
### SECTION: attack-path
|
|
155
|
+
|
|
156
|
+
BEFORE assigning severity, write the attack path as **structured facts**, then
|
|
157
|
+
let severity fall out of them mechanically (`review-protocol.md
|
|
158
|
+
SECTION=risk-scoring`) — this stops severity from being argued emotionally.
|
|
159
|
+
|
|
160
|
+
- `attacker`: who, at what trust level, from where.
|
|
161
|
+
- `entrypoint`: the exact reachable entry (route/RPC/CLI/webhook/MCP tool).
|
|
162
|
+
- `preconditions`: conditions that must hold for the exploit (feature flag on,
|
|
163
|
+
authenticated user, specific config). A **precondition** narrows likelihood but
|
|
164
|
+
does NOT refute — keep it distinct from counterevidence (which kills the
|
|
165
|
+
finding, see `adversarial-refute`).
|
|
166
|
+
- `dataflow`: source → sink in one line.
|
|
167
|
+
- `outcome`: the concrete impact (data read/written, code executed, tenant
|
|
168
|
+
crossed).
|
|
169
|
+
- `impact` (1–5) and `likelihood` (1–5), each with one line of repo evidence.
|
|
170
|
+
|
|
171
|
+
Severity is then `review-protocol.md SECTION=risk-scoring` applied to
|
|
172
|
+
`impact × likelihood` — do not re-argue it afterward.
|
|
173
|
+
|
|
174
|
+
### SECTION: adversarial-refute
|
|
175
|
+
|
|
176
|
+
The security-specialized layer of the generic Challenge pass. Stance: **a finding
|
|
177
|
+
SURVIVES unless you produce concrete refuting evidence** — but the single most
|
|
178
|
+
common false positive is a **missing privilege boundary**, so test that first.
|
|
179
|
+
|
|
180
|
+
**Privilege-boundary test (BINDING)**: name the attacker (who controls the input)
|
|
181
|
+
and the victim (who is harmed). If attacker == victim operating on their own
|
|
182
|
+
machine / same-privilege process (a CLI arg in a same-trust process, a dev
|
|
183
|
+
script, a local tool with no remote entrypoint) → **REFUTE**. Keep it only when
|
|
184
|
+
the attacker is a lower-trust actor and the impact reaches other users, tenants,
|
|
185
|
+
or infrastructure.
|
|
186
|
+
|
|
187
|
+
**Refute the finding when**:
|
|
188
|
+
- it is **pre-existing** code untouched by the diff (in diff-scoped review, flag
|
|
189
|
+
only newly introduced or newly reachable surface; an off-diff finding must name
|
|
190
|
+
the specific `+`/`-` line that enables the sink).
|
|
191
|
+
- a sanitizer / validator / parameterization / authz check on the real path
|
|
192
|
+
already prevents the exploit.
|
|
193
|
+
- the sink is not actually dangerous here (typed-schema decoder, hardcoded
|
|
194
|
+
`https` URL, ORM/parameterized query, non-HTML context for an "XSS").
|
|
195
|
+
- a frontend-only gate is independently enforced by the backend (or vice-versa).
|
|
196
|
+
- validation is delegated to a caller/framework that provably runs it.
|
|
197
|
+
- it is throwaway/dev-only code (`scripts/`, `dev/`, `__main__`, fixtures, tests)
|
|
198
|
+
with no shipped surface.
|
|
199
|
+
- a dependency bump moved the control into the library (documented).
|
|
200
|
+
|
|
201
|
+
**Precondition vs counterevidence (keep precise)**: "deployment is internal-only"
|
|
202
|
+
is a *precondition* on the finding (still exploitable if it holds) → keep, lower
|
|
203
|
+
likelihood. "the code is test-only" is *counterevidence* → refute. Do not confuse
|
|
204
|
+
the two.
|
|
205
|
+
|
|
206
|
+
**Never-refute guard**: an item the agent's never-demote list marks (auth bypass,
|
|
207
|
+
RCE, secret exposure, cross-tenant breach with a grounded proof tuple) is never a
|
|
208
|
+
false positive — do not refute it however convincing the argument sounds.
|
|
209
|
+
|
|
210
|
+
Record refuted candidates (title + one-line refutation) so the audit trail shows
|
|
211
|
+
they were considered, not missed.
|
|
212
|
+
|
|
213
|
+
### SECTION: coverage-ledger
|
|
214
|
+
|
|
215
|
+
Make the review's *coverage* explicit so a clean verdict is honest and a gap is
|
|
216
|
+
never silent. For each security-relevant surface in scope, record a disposition:
|
|
217
|
+
|
|
218
|
+
- `reported` — a finding was emitted.
|
|
219
|
+
- `no_issue_found` — reviewed, no credible issue survived the passes.
|
|
220
|
+
- `not_applicable` — the class does not apply to this surface (with the reason).
|
|
221
|
+
- `needs_follow_up` — a real concern out of this review's scope/ownership
|
|
222
|
+
(surfaces as residual).
|
|
223
|
+
- `deferred` — could not complete (tool/access/time), with the reason + paths.
|
|
224
|
+
|
|
225
|
+
Rules:
|
|
226
|
+
- A **complete** review has zero `deferred` / `needs_follow_up` rows; otherwise
|
|
227
|
+
it is **partial** — say so. Zero findings on a fully-covered surface is a
|
|
228
|
+
legitimate, reportable outcome.
|
|
229
|
+
- A seeded obligation (a CVE/advisory/user pointer at a specific file/function)
|
|
230
|
+
stays OPEN until its exact row is closed as reported / not_applicable /
|
|
231
|
+
deferred — a neighboring same-family finding does not close it.
|
|
232
|
+
- In the pooled YAML mode, carry this as a compact `coverage:` block in the
|
|
233
|
+
`# Security Review Summary` header, not as fake LOW findings.
|
|
@@ -55,72 +55,76 @@ Document security requirements, threats, and mitigation strategies.
|
|
|
55
55
|
- [Sensitive data masking in logs]
|
|
56
56
|
- [Secrets management]
|
|
57
57
|
|
|
58
|
-
## Common Vulnerabilities (OWASP Top 10)
|
|
58
|
+
## Common Vulnerabilities (OWASP Top 10 — 2021)
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
> Modernized to the OWASP Top 10:2021 categories (the 2017 list — with XXE and
|
|
61
|
+
> "Sensitive Data Exposure" as standalone entries — is superseded). XXE now folds
|
|
62
|
+
> into Security Misconfiguration; insecure deserialization into Software & Data
|
|
63
|
+
> Integrity Failures; XSS into Injection. A 2025 revision is in draft — revisit
|
|
64
|
+
> when it is finalized (expected consolidation of supply-chain risks).
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
- Validate and sanitize inputs
|
|
64
|
-
- Implement least privilege database access
|
|
66
|
+
### A01 Broken Access Control
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
- Enforce authorization on every request, server-side; deny by default
|
|
69
|
+
- Prevent IDOR — never trust a user-supplied record/object identifier
|
|
70
|
+
- Enforce ownership + tenant isolation on every sensitive action
|
|
71
|
+
- Principle of least privilege; no privilege escalation via business logic
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
- Use strong password policies
|
|
70
|
-
- Implement account lockout
|
|
71
|
-
- Protect against brute force
|
|
73
|
+
### A02 Cryptographic Failures
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
- Encrypt sensitive data at rest and in transit (HTTPS/TLS everywhere)
|
|
76
|
+
- Use strong, modern algorithms (no MD5/SHA1 for security, no AES-ECB, no static IV)
|
|
77
|
+
- Proper key management + rotation; never log secrets or PII
|
|
78
|
+
- Strong password hashing (argon2/bcrypt/scrypt) with per-user salt
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
- Use HTTPS everywhere
|
|
77
|
-
- Don't log sensitive information
|
|
78
|
-
- Implement secure key management
|
|
80
|
+
### A03 Injection (incl. XSS)
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
- Parameterized queries / ORM; never build queries by string concatenation
|
|
83
|
+
- Validate + sanitize inputs; allowlists over denylists
|
|
84
|
+
- Least-privilege database access
|
|
85
|
+
- Output encoding at the right context + Content Security Policy for XSS
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
- Use safe XML parsers
|
|
84
|
-
- Validate XML inputs
|
|
87
|
+
### A04 Insecure Design
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
- Threat-model the feature before building; secure-by-default patterns
|
|
90
|
+
- Establish trust boundaries and enforce them; fail closed
|
|
91
|
+
- Rate limits, resource bounds, and abuse cases considered up front
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
- Validate permissions on every request
|
|
90
|
-
- Use principle of least privilege
|
|
93
|
+
### A05 Security Misconfiguration (incl. XXE)
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
- Harden defaults; remove unnecessary features/debug endpoints in prod
|
|
96
|
+
- Set security headers; keep software updated
|
|
97
|
+
- Disable XML external-entity processing; use safe parsers/validators
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
- Keep software updated
|
|
96
|
-
- Remove unnecessary features
|
|
97
|
-
- Implement security headers
|
|
99
|
+
### A06 Vulnerable & Outdated Components
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
- Keep dependencies patched; monitor security advisories (CVE/GHSA)
|
|
102
|
+
- Run dependency + vulnerability scanning in CI (e.g. audit / govulncheck)
|
|
103
|
+
- Remove unused dependencies and features
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
- Use Content Security Policy
|
|
103
|
-
- Validate and sanitize inputs
|
|
104
|
-
- Use framework protections
|
|
105
|
+
### A07 Identification & Authentication Failures
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
- Secure session management (rotation, expiry, secure cookies)
|
|
108
|
+
- Strong password policy, account lockout / brute-force protection, MFA where applicable
|
|
109
|
+
- No session fixation, no token leakage in URLs/logs
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
- Use safe deserialization libraries
|
|
110
|
-
- Implement integrity checks
|
|
111
|
+
### A08 Software & Data Integrity Failures (incl. insecure deserialization)
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
- Never deserialize untrusted data with unsafe loaders; validate + integrity-check
|
|
114
|
+
- Verify integrity of updates/CI artifacts (signatures, SRI, pinned digests)
|
|
115
|
+
- Guard the CI/CD supply chain — no untrusted code with elevated tokens
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
- Monitor security advisories
|
|
116
|
-
- Use dependency scanning tools
|
|
117
|
+
### A09 Security Logging & Monitoring Failures
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
- Log security events; monitor for suspicious activity; alert
|
|
120
|
+
- Protect log integrity; never log secrets/PII
|
|
121
|
+
- Ensure exploitation attempts are detectable and auditable
|
|
119
122
|
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
123
|
+
### A10 Server-Side Request Forgery (SSRF)
|
|
124
|
+
|
|
125
|
+
- Validate + allowlist outbound destinations; parse (don't substring) URLs
|
|
126
|
+
- Block internal metadata endpoints and private ranges
|
|
127
|
+
- Enforce egress controls; treat any user-supplied URL/host as hostile
|
|
124
128
|
|
|
125
129
|
## Rate Limiting
|
|
126
130
|
|