baldart 4.99.0 → 5.0.1
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 +114 -0
- package/README.md +1 -0
- package/VERSION +1 -1
- package/framework/.claude/agents/CHANGELOG.md +105 -0
- package/framework/.claude/agents/REGISTRY.md +3 -1
- package/framework/.claude/agents/code-reviewer.md +84 -400
- package/framework/.claude/agents/codebase-architect.md +71 -356
- package/framework/.claude/agents/coder.md +130 -291
- package/framework/.claude/agents/doc-reviewer.md +127 -450
- package/framework/.claude/agents/plan-auditor.md +137 -436
- package/framework/.claude/agents/prd-card-writer.md +1 -1
- package/framework/.claude/agents/qa-sentinel.md +59 -313
- package/framework/.claude/agents/skill-improver.md +60 -4
- package/framework/.claude/agents/ui-expert.md +104 -591
- package/framework/.claude/commands/codexreview.md +26 -4
- package/framework/.claude/hooks/agent-discovery-gate.js +2 -2
- package/framework/.claude/hooks/agent-discovery-info.sh +1 -0
- package/framework/.claude/skills/new/CHANGELOG.md +39 -0
- package/framework/.claude/skills/new/SKILL.md +1 -1
- package/framework/.claude/skills/new/references/codex-gate.md +25 -11
- package/framework/.claude/skills/new/references/implement.md +49 -5
- package/framework/.claude/skills/new/references/review-cycle.md +20 -5
- package/framework/.claude/skills/new/scripts/build-review-bundle.mjs +112 -0
- package/framework/.claude/skills/new/scripts/doc-invariants.mjs +166 -0
- package/framework/.claude/skills/new2/CHANGELOG.md +13 -0
- package/framework/.claude/skills/new2/SKILL.md +5 -1
- package/framework/.claude/workflows/new-card-review.js +16 -2
- package/framework/.claude/workflows/new2-resolve.js +11 -4
- package/framework/.claude/workflows/new2.js +51 -3
- package/framework/agents/agent-operating-protocol.md +152 -0
- package/framework/agents/coding-standards.md +2 -2
- package/framework/agents/doc-audit-protocol.md +232 -0
- package/framework/agents/index.md +4 -0
- package/framework/agents/review-protocol.md +207 -0
- package/framework/docs/UPGRADE-3.12-UI-COHERENCE.md +1 -1
- package/framework/routines/finding-mine.routine.yml +56 -0
- package/framework/routines/index.yml +11 -0
- package/package.json +1 -1
- package/src/commands/configure.js +15 -0
- package/src/commands/doctor.js +69 -2
- package/src/utils/agent-slots.js +109 -0
- package/src/utils/overlay-merger.js +17 -8
- package/src/utils/symlinks.js +93 -33
|
@@ -48,11 +48,19 @@ For the (single) card in scope, check for `/tmp/codexreview-lean-<CARD-ID>.json`
|
|
|
48
48
|
"profile": "light" | "full",
|
|
49
49
|
"arch_baseline_path": "/tmp/arch-baseline-<CARD-ID>.md",
|
|
50
50
|
"diff_summary_path": "<completion-report or git-diff path>",
|
|
51
|
+
"review_bundle_path": "/tmp/review-bundle-<CARD-ID>.json",
|
|
51
52
|
"skip_doc_reviewer": true,
|
|
52
|
-
"skip_api_perf_auditor": true
|
|
53
|
+
"skip_api_perf_auditor": true,
|
|
54
|
+
"specialist_dispatch": {
|
|
55
|
+
"suppress": ["doc-reviewer", "plan-auditor", "api-perf-cost-auditor", "security-reviewer"],
|
|
56
|
+
"owner": "/new phases 3 / 3.5 / 3.7 + Final F.3"
|
|
57
|
+
}
|
|
53
58
|
}
|
|
54
59
|
```
|
|
55
60
|
|
|
61
|
+
(`review_bundle_path` and `specialist_dispatch` since v5.0.1 — producer: `codex-gate.md` § lean
|
|
62
|
+
contract. Both OPTIONAL: absent on pre-5.0 contracts; unknown keys are ignored.)
|
|
63
|
+
|
|
56
64
|
Apply:
|
|
57
65
|
- `arch_baseline_path` readable → **skip Step 1** (do NOT re-spawn `codebase-architect`); load the
|
|
58
66
|
baseline from that file and ALSO attach the `diff_summary_path` contents to every review agent so
|
|
@@ -77,6 +85,17 @@ For the (single) card in scope, check for `/tmp/codexreview-lean-<CARD-ID>.json`
|
|
|
77
85
|
its own findings as before. Any high-risk diff trigger escalates the card to `full` (both finders)
|
|
78
86
|
per the Review Profile Selector; the Final-review FULL gate re-runs Codex batch-wide regardless. *(B1)*
|
|
79
87
|
- `profile: "full"` (or missing) → full agent set incl. Codex adversarial, minus #3 if `skip_doc_reviewer`.
|
|
88
|
+
- `review_bundle_path` present AND readable (since v5.0.1) → append to EVERY Step-2 reviewer prompt
|
|
89
|
+
the binding anti-re-read phrase: *"Review bundle: `<review_bundle_path>` — Read it FIRST. It lists
|
|
90
|
+
the diff, the changed files, the arch baseline and the completion report. Do NOT re-Read source
|
|
91
|
+
files to rebuild context you can get from those paths; for any file in `hot_files`, Read ONLY the
|
|
92
|
+
symbol ranges listed."* Absent/unreadable → no phrase (reviewers derive scope as before). *(A4)*
|
|
93
|
+
- `specialist_dispatch` present (since v5.0.1) → append its verbatim JSON to EVERY Step-2 reviewer
|
|
94
|
+
prompt, prefixed: *"ORCHESTRATED run — do NOT spawn any of the suppressed specialists yourself
|
|
95
|
+
(owner: `<owner>`); record what you would have routed as findings tagged
|
|
96
|
+
`dispatch_deferred: <agent>`."* (SSOT semantics: `agents/review-protocol.md § SECTION:
|
|
97
|
+
specialist-spawn`; the block WINS over the legacy `skip_*` flags where both are present.)
|
|
98
|
+
Absent → the reviewers' own spawn matrices apply (standalone behavior, unchanged). *(A5)*
|
|
80
99
|
|
|
81
100
|
**Invariant**: lean mode NEVER suppresses the run itself — at minimum a **finder + the Step 3 FP-gate**
|
|
82
101
|
execute on every card. At `light` the finder is **Codex** (or `code-reviewer` on the Codex-unavailable
|
|
@@ -85,8 +104,9 @@ malformed/unreadable, fall back to **full mode** and note it. **Consume-once**:
|
|
|
85
104
|
`/tmp/codexreview-lean-<CARD-ID>.json` immediately after parsing it, so a later standalone
|
|
86
105
|
`/codexreview <CARD-ID>` on the same card never inherits a stale lean contract from a prior `/new` run.
|
|
87
106
|
Record the resolved mode in the Step 4 report `Method` section, e.g.
|
|
88
|
-
`lean: profile=light, architect=reused, doc-reviewer=skipped(Phase 3), codex=finder(light), code-reviewer=fp-gate, cove=skipped`
|
|
89
|
-
(or `codex=unavailable→code-reviewer finder(light)` when the fallback fired
|
|
107
|
+
`lean: profile=light, architect=reused, doc-reviewer=skipped(Phase 3), codex=finder(light), code-reviewer=fp-gate, cove=skipped, bundle=forwarded, dispatch=suppressed`
|
|
108
|
+
(or `codex=unavailable→code-reviewer finder(light)` when the fallback fired; `bundle=absent` /
|
|
109
|
+
`dispatch=none` on pre-5.0 contracts).
|
|
90
110
|
|
|
91
111
|
---
|
|
92
112
|
|
|
@@ -175,7 +195,9 @@ Launch these agents in parallel for each card:
|
|
|
175
195
|
> the finder instead (the Step 3 FP-gate validates its findings). At `full` with `skip_api_perf_auditor`,
|
|
176
196
|
> launch #1 + #4 (Codex) (+ #3 unless `skip_doc_reviewer`). At `full` with no skips (and in full mode
|
|
177
197
|
> with no contract file), launch all four. The standalone `/codexreview <CARD-ID>` invocation (no
|
|
178
|
-
> contract) is always full with the complete set.
|
|
198
|
+
> contract) is always full with the complete set. When the contract carried `review_bundle_path` /
|
|
199
|
+
> `specialist_dispatch` (Step -0.5 A4/A5), every prompt built here carries the anti-re-read phrase
|
|
200
|
+
> and the suppression block respectively.
|
|
179
201
|
|
|
180
202
|
**Codex invocation rules (agent #4):**
|
|
181
203
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*
|
|
26
26
|
* Source of truth for the list of BALDART agents: the filesystem
|
|
27
27
|
* directory next to this file (`.framework/framework/.claude/agents/`).
|
|
28
|
-
* Every `<name>.md` (except `REGISTRY.md`) is a BALDART agent. No
|
|
28
|
+
* Every `<name>.md` (except `REGISTRY.md` and `CHANGELOG.md`) is a BALDART agent. No
|
|
29
29
|
* separate manifest is shipped — the directory IS the manifest.
|
|
30
30
|
*
|
|
31
31
|
* Fail-safe contract: this hook MUST NOT block legitimate work. If
|
|
@@ -68,7 +68,7 @@ function loadBaldartAgents() {
|
|
|
68
68
|
try {
|
|
69
69
|
if (!fs.existsSync(agentsDir)) return null;
|
|
70
70
|
const names = fs.readdirSync(agentsDir)
|
|
71
|
-
.filter((f) => f.endsWith('.md') && f !== 'REGISTRY.md')
|
|
71
|
+
.filter((f) => f.endsWith('.md') && f !== 'REGISTRY.md' && f !== 'CHANGELOG.md')
|
|
72
72
|
.map((f) => f.replace(/\.md$/, ''));
|
|
73
73
|
return new Set(names);
|
|
74
74
|
} catch (_) {
|
|
@@ -99,6 +99,7 @@ while IFS= read -r f; do
|
|
|
99
99
|
[ -z "$f" ] && continue
|
|
100
100
|
name="$(basename "$f" .md)"
|
|
101
101
|
[ "$name" = "REGISTRY" ] && continue
|
|
102
|
+
[ "$name" = "CHANGELOG" ] && continue
|
|
102
103
|
target="$CONSUMER_AGENTS_DIR/${name}.${CONSUMER_AGENT_EXT}"
|
|
103
104
|
# `test -e` returns false for broken symlinks too — exactly what we want.
|
|
104
105
|
if [ ! -e "$target" ]; then
|
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 2.0.1 — 2026-07-03
|
|
6
|
+
|
|
7
|
+
Adversarial-verification fixes on the v5.0.0 pipeline wave (framework v5.0.1):
|
|
8
|
+
|
|
9
|
+
- **implement.md 11b→11c (HIGH)**: the clean branch of the file-diff gate now proceeds to 11c
|
|
10
|
+
(review bundle + doc-invariants) — previously it jumped straight to Phase 2.5, leaving the
|
|
11
|
+
bundle/PASS-fast machinery inert on the happy path. 11c now ends with an explicit "proceed
|
|
12
|
+
to Phase 2.5".
|
|
13
|
+
- **implement.md 7d**: explicit branch for `[CAP-HANDOFF]` marker present but checkpoint file
|
|
14
|
+
missing (rebuild `items_remaining` from the completion report, else `[CAP-EXHAUSTED]`); the
|
|
15
|
+
continuation briefing cites the review bundle only "when it already exists".
|
|
16
|
+
- **implement.md 6a**: delegated `/ui-implement` cards explicitly still run steps 7c–11c
|
|
17
|
+
(i18n fill, gates, ownership gate, bundle) — the delegation replaces only the build + 2.6.
|
|
18
|
+
- **review-cycle.md**: Phase 2.55 pre-5.0 diff fallback is now `git diff "$TRUNK...HEAD"`
|
|
19
|
+
(a bare `git diff` post-commit is vacuous); the 2.5x delegation passes `reviewBundlePath`
|
|
20
|
+
to `new-card-review.js`; the deferral KEEP list re-includes `balanced`; the PASS-fast
|
|
21
|
+
Own-Doc Closure claim corrected (modified-primitive obligations ride the serializer gate +
|
|
22
|
+
Final F.3, R1/R3 fire on new files only); the QA briefing marks bundle `gate_logs` as
|
|
23
|
+
CONTEXT ONLY (own-tier gates always re-run).
|
|
24
|
+
- **scripts/build-review-bundle.mjs**: the diff/files_changed are now SCOPED to `--may-edit`
|
|
25
|
+
when passed — in a sequential multi-card batch the unscoped `trunk...HEAD` accumulated
|
|
26
|
+
prior cards' commits into card N's bundle/docinv.
|
|
27
|
+
- **scripts/doc-invariants.mjs**: `card_declares_docs` evaluates `documentation_impact` and
|
|
28
|
+
`canonical_docs` independently (a populated `canonical_docs` with `documentation_impact: []`
|
|
29
|
+
no longer PASS-fasts).
|
|
30
|
+
- Companion consumer fix (command, not this skill): `/codexreview` Step -0.5 now parses +
|
|
31
|
+
forwards `review_bundle_path` and `specialist_dispatch` from the lean contract.
|
|
32
|
+
|
|
33
|
+
## 2.0.0 — 2026-07-03
|
|
34
|
+
|
|
35
|
+
- **BALDART 5.0 pipeline wave (framework v5.0.0).** Six additive mechanisms, all with pre-5.0 graceful degradation:
|
|
36
|
+
- **Review bundle (implement.md step 11c)**: `scripts/build-review-bundle.mjs` materializes the deterministic per-card handoff (`/tmp/review-bundle-<ID>.json` — diff, files_changed, arch-baseline pointer + parsed Hot-File Map, completion report, ownership, gate logs). Every reviewer briefing carries the binding anti-re-read phrase; the coder also persists its completion report to `/tmp/completion-<ID>.md`. The Phase 2.55 / 3.7 diff dumps become consumers, not producers.
|
|
37
|
+
- **doc-invariants PASS-fast (Phase 3)**: `scripts/doc-invariants.mjs` classifies the diff against the doc-reviewer invariant table (9 rules, advisory rows excluded); `pass_fast_eligible` + non-`deep` profile → per-card doc-reviewer spawn skipped (measured 84/127 PASS runs), Final F.3 backstop unchanged.
|
|
38
|
+
- **Cap-and-handoff (step 7 briefing + step 7d)**: soft ~80 / hard 100 tool calls, checkpoint JSON + `[CAP-HANDOFF]` marker, fresh continuation respawn (max 2), then unmet items flow through the normal gap path.
|
|
39
|
+
- **Explicit i18n fill pass (step 7c)**: the buried step-9 sub-bullet promoted to a numbered step — deterministic new-key detection, `i18n-translator` spawned BY NAME (closes the general-purpose leakage), parity failures never retry the coder.
|
|
40
|
+
- **Specialist dispatch suppression (codex-gate lean contract)**: new `specialist_dispatch` block (suppress list + owner) — reviewers consume it instead of nested-spawning duplicates; legacy `skip_*` flags kept for back-compat. SSOT: `agents/review-protocol.md § specialist-spawn`.
|
|
41
|
+
- **Fix-pass model routing A/B (steps 9 / 3.5 / 3.7)**: fix-pass `coder` spawns get `model: sonnet` best-effort (initial builds untouched, specialists untouched); overlay kill-switch `## [OVERRIDE] Fix-pass model`; `model=` recorded in Fix Application Log rows for recurrence telemetry.
|
|
42
|
+
- Codex parity: scripts portable (.mjs), prose additive, bundle/docinv degrade to the pre-5.0 inline behaviour when the scripts are absent. All six policies guarded by `scripts/check-new-parity.mjs`.
|
|
43
|
+
|
|
5
44
|
## 1.5.0 — 2026-07-03
|
|
6
45
|
|
|
7
46
|
- **Epic-closure gate (framework v4.99.0)**: `commit.md` step 28 gains sub-step **c-bis** — when the card being marked DONE declares a `parent:` epic, a deterministic count of non-DONE siblings decides whether the PARENT epic's ssot-registry entry (and its own YAML, if present) is flipped to DONE in the same commit. Closes the recurring consumer gap where epics stayed `IN_PROGRESS` after their last child closed (3 occurrences across two audit windows, caught only by the nightly). Mirrored in `new2.js`'s mechanical commit prompt (step 4b).
|
|
@@ -99,25 +99,39 @@ For EVERY other card (no conditional skip — the gate ALWAYS runs; only its DEP
|
|
|
99
99
|
the Codex-unavailable fallback). To revert per-card Codex off `light`, restore the v3.38.0 `light`
|
|
100
100
|
branch of `/codexreview` Step -0.5 (`code-reviewer` finder only).
|
|
101
101
|
|
|
102
|
-
Then
|
|
102
|
+
Then ensure the card diff is on disk (implement.md step 11c's review bundle already dumped
|
|
103
|
+
`/tmp/diff-<CARD-ID>.txt` — this gate is a CONSUMER; only on a pre-5.0 install without the
|
|
104
|
+
bundle script, dump it now) and write the lean contract `/codexreview` consumes:
|
|
103
105
|
```bash
|
|
104
106
|
cd <worktree-path>
|
|
105
107
|
# $TRUNK = the trunk branch resolved in Phase 0 (git.trunk_branch)
|
|
106
|
-
git diff "$TRUNK...HEAD" -- <card files from ownership map> > /tmp/diff-<CARD-ID>.txt 2>/dev/null \
|
|
108
|
+
[ -s /tmp/diff-<CARD-ID>.txt ] || git diff "$TRUNK...HEAD" -- <card files from ownership map> > /tmp/diff-<CARD-ID>.txt 2>/dev/null \
|
|
107
109
|
|| git diff HEAD~1..HEAD > /tmp/diff-<CARD-ID>.txt
|
|
108
110
|
```
|
|
109
111
|
Write `/tmp/codexreview-lean-<CARD-ID>.json`:
|
|
110
112
|
```json
|
|
111
113
|
{ "profile": "light|full", "arch_baseline_path": "/tmp/arch-baseline-<CARD-ID>.md",
|
|
112
|
-
"diff_summary_path": "/tmp/diff-<CARD-ID>.txt",
|
|
113
|
-
"
|
|
114
|
+
"diff_summary_path": "/tmp/diff-<CARD-ID>.txt",
|
|
115
|
+
"review_bundle_path": "/tmp/review-bundle-<CARD-ID>.json",
|
|
116
|
+
"skip_doc_reviewer": true, "skip_api_perf_auditor": true,
|
|
117
|
+
"specialist_dispatch": {
|
|
118
|
+
"suppress": ["doc-reviewer", "plan-auditor", "api-perf-cost-auditor", "security-reviewer"],
|
|
119
|
+
"owner": "/new phases 3 / 3.5 / 3.7 + Final F.3" } }
|
|
114
120
|
```
|
|
115
|
-
`
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
**`specialist_dispatch` (v5.0.0 — the generalized suppression block, SSOT semantics in
|
|
122
|
+
`agents/review-protocol.md § SECTION: specialist-spawn`)**: in an orchestrated run the
|
|
123
|
+
ORCHESTRATOR owns specialist routing — `/new` runs doc-review (Phase 3), qa (3.5), security
|
|
124
|
+
fixes (3.7 writer partition) and the batch-wide api-perf pass (Final F.3) itself, so a reviewer
|
|
125
|
+
that nested-spawned any of the suppressed agents would duplicate a review, untracked and
|
|
126
|
+
invisible to the tracker. Reviewers record what they would have routed as findings tagged
|
|
127
|
+
`dispatch_deferred: <agent>`. The legacy `skip_doc_reviewer`/`skip_api_perf_auditor` flags stay
|
|
128
|
+
for back-compat (pre-5.0 readers); the block WINS where both are present. `review_bundle_path`
|
|
129
|
+
points the reviewer at the deterministic scope handoff (Read it FIRST; never re-derive; honor
|
|
130
|
+
`hot_files` ranges). Standalone `/codexreview` invocations (no lean contract) carry no block —
|
|
131
|
+
the reviewers' own spawn matrices apply as before.
|
|
132
|
+
`skip_doc_reviewer` rationale: Phase 3 already ran `doc-reviewer` on this diff (spec/docs-drift→bug
|
|
133
|
+
lens since v3.35.0). `skip_api_perf_auditor` rationale (since v4.7.0): the per-card api-perf pass is
|
|
134
|
+
deferred to the Final Review F.3 batch-wide sweep.
|
|
121
135
|
|
|
122
136
|
1. **Log gate invocation** in the tracker under `## Pre-Merge Codex Review`:
|
|
123
137
|
|
|
@@ -151,7 +165,7 @@ For EVERY other card (no conditional skip — the gate ALWAYS runs; only its DEP
|
|
|
151
165
|
|
|
152
166
|
4. **Apply fix sub-loop** (mirror of Phase 3.5 retry pattern):
|
|
153
167
|
- If 0 BLOCKER and 0 HIGH → log `verdict: PASS — proceeding to Phase 4` in tracker. Done. (MEDIUM/LOW findings are advisory at this per-card gate; they are not silently lost — the post-batch **Final-review FULL gate** applies every VERIFIED finding ≥ MEDIUM. Log the MEDIUM count in the tracker so it is visible.)
|
|
154
|
-
- If 1+ BLOCKER OR 1+ HIGH → spawn the **domain writer** with the report path + list of VERIFIED bugs (canonical writer map v4.26.1 — see SKILL.md § "Domain-Override Domains"): **`security`-domain findings** (touching `paths.high_risk_modules` or RLS-policy SQL — the same `security` match rule) → **`security-reviewer`** in write mode (it owns the security-invariant contract a coder lacks; never route a security fix to `coder`); **all other findings** (`correctness`/code/perf/`other`) → **`coder
|
|
168
|
+
- If 1+ BLOCKER OR 1+ HIGH → spawn the **domain writer** with the report path + list of VERIFIED bugs (canonical writer map v4.26.1 — see SKILL.md § "Domain-Override Domains"): **`security`-domain findings** (touching `paths.high_risk_modules` or RLS-policy SQL — the same `security` match rule) → **`security-reviewer`** in write mode (it owns the security-invariant contract a coder lacks; never route a security fix to `coder`); **all other findings** (`correctness`/code/perf/`other`) → **`coder`** — **fix-pass model routing (v5.0.0 A/B, best-effort): spawn the fix-pass coder with `model: sonnet`** when the runtime supports it (overlay `## [OVERRIDE] Fix-pass model` wins; the security-reviewer partition is NOT downgraded); append `model=<sonnet|opus>` to the Fix Application Log rows. Run security-reviewer first, then coder (skip either if its partition is empty). **At `full` profile** the report contains Codex-suggested inline patches: pass them and have the coder **apply the suggested patches** with the right system prompt (project conventions, naming, testing patterns) — it does NOT re-do the analysis or re-grep (since v3.28.3), BUT it MUST first confirm each patch still applies against the current file state (prior fix-loop iterations may have shifted line offsets); if a patch no longer applies cleanly, the coder re-locates the target by content and applies the equivalent edit rather than a stale-offset verbatim paste. **At `light` profile** (since v4.18.0) the findings come from **Codex** (the sole finder) — the report carries Codex's `minimal_fix_direction`; brief the coder to apply it (treat it like the `full`-profile Codex fix direction). **On the Codex-unavailable fallback** the `light` findings come from `code-reviewer` instead — brief the coder to apply the `code-reviewer` fix direction (no Codex patches to paste). After coder fixes, **re-write the lean contract `/tmp/codexreview-lean-<CARD-ID>.json` (it is consumed-once and deleted by `/codexreview`)** and re-invoke `/codexreview` via the Skill tool with `args: <CARD-ID>` (NOT a bare prose mention — the card ID MUST be passed so the retry reviews THIS card, not an inferred one). Repeat **max 2 times**.
|
|
155
169
|
- If still BLOCKER/HIGH after 2 retries → log in `## Issues & Flags` and **ask the user** whether to proceed, escalate, or stop. The Phase 4 commit MUST NOT happen until the Pre-Merge Codex Review verdict is PASS or user explicitly overrides. **When AUTONOMOUS, apply § AUTONOMOUS RESOLUTION RULE (SKILL.md): category=blocker; recommended=route the residual BLOCKER/HIGH to the writer (security-reviewer/coder) and re-verify; if still unresolved → follow-up card (NEVER force the verdict to PASS / force-commit).**
|
|
156
170
|
- **Telemetry** — for EVERY codex finding processed (verified BLOCKER, verified HIGH, or false-positive-filtered), append one row to `## Fix Application Log`: `3.7 | codex-<security|correctness|other> | est_lines=<n> | decision=<security-reviewer|coder|skipped> | applied_by=<security-reviewer|coder|-> | severity=<BLOCKER|HIGH|FALSE-POSITIVE> | retry=<n>`. (`security`-domain fixes are applied by `security-reviewer`, all others by `coder`.) Classify domain: `security` for findings touching RLS / auth / permissions / payments; `correctness` for logic / data integrity / race conditions; `other` for everything else.
|
|
157
171
|
|
|
@@ -62,7 +62,10 @@
|
|
|
62
62
|
in programmatic mode (contract: ui-implement/references/integration.md).
|
|
63
63
|
Consume its COMPACT JSON (status mirrors /e2e-review — review-cycle.md Phase 2.6
|
|
64
64
|
gating table). SKIP step 6b (owner_agent router), the step-7 coarse briefing,
|
|
65
|
-
AND Phase 2.6 (the skill already ran /e2e-review).
|
|
65
|
+
AND Phase 2.6 (the skill already ran /e2e-review). Steps 7c–11c still run HERE
|
|
66
|
+
for the delegated card (i18n fill, gates 8–9, file diff gate 11b, review bundle
|
|
67
|
+
11c) — the delegation replaces ONLY the build steps and Phase 2.6, never the
|
|
68
|
+
gates or the bundle. Then resume the per-card
|
|
66
69
|
review cluster (Phase 2.55 Simplify → Phase 3 doc → 3.5 QA → 3.7 Codex) per the
|
|
67
70
|
card's review_profile — for a MIXED UI+logic card /new KEEPS that cluster
|
|
68
71
|
(integration.md § "What /new KEEPS"); it is fidelity-only that the skill owns.
|
|
@@ -409,6 +412,20 @@
|
|
|
409
412
|
this — this briefing is an explicit reminder for UI-touching cards.
|
|
410
413
|
- No hardcoded hex/shadow/border values in component styling — canonical tokens only.
|
|
411
414
|
|
|
415
|
+
### Tool-call budget & checkpoint (cap-and-handoff, v5.0.0)
|
|
416
|
+
Call budget for this card: soft ~80 tool calls, hard 100. The measured failure mode
|
|
417
|
+
this prevents: runs that ballooned to 200–440 calls on a degrading context (a single
|
|
418
|
+
such run cost 93M tokens — more than 30 median cards). When you cross the SOFT cap —
|
|
419
|
+
or notice you are re-reading the same files — do NOT push through:
|
|
420
|
+
(1) commit the WIP (`[<CARD-ID>] WIP checkpoint` — never a stash),
|
|
421
|
+
(2) write `/tmp/checkpoint-<CARD-ID>.json` per `coder.md § Tool-Call Budget & Checkpoint`
|
|
422
|
+
({ card, wip_commit, items_done, items_remaining[{id,text,next_step}],
|
|
423
|
+
files_in_progress, gates_last_status, decisions, warnings }),
|
|
424
|
+
(3) return a `partial` completion report with the checkpoint path and the marker
|
|
425
|
+
`[CAP-HANDOFF n=<attempt>]` in your final message.
|
|
426
|
+
At the HARD cap stop even mid-task. A fresh continuation spawn finishes the card on a
|
|
427
|
+
clean context — cheaper and safer than 100 more calls on a bloated one.
|
|
428
|
+
|
|
412
429
|
### Batch Lessons (from prior cards in this batch)
|
|
413
430
|
[If tracker `## Lessons Learned` has entries, paste them here.
|
|
414
431
|
If empty or first card: "First card — no lessons yet."]
|
|
@@ -437,6 +454,9 @@
|
|
|
437
454
|
When implementation is done, output this block in EXACTLY this format.
|
|
438
455
|
This report is NOT optional — if you omit it, the orchestrator will flag
|
|
439
456
|
your implementation as incomplete and spawn a fix agent.
|
|
457
|
+
**ALSO write the identical block to `/tmp/completion-<CARD-ID>.md` before returning**
|
|
458
|
+
(v5.0.0 — downstream reviewers read it from disk via the review bundle; the
|
|
459
|
+
orchestrator persists it from your return as a backstop if the file is missing).
|
|
440
460
|
|
|
441
461
|
```completion-report
|
|
442
462
|
card: <CARD-ID>
|
|
@@ -479,6 +499,20 @@
|
|
|
479
499
|
- **Grounding (cheap `grep`, like the Phase-1 plan-auditor grounding):** every cited source symbol/class MUST appear in `mockups/_src/` (`grep -rF "<symbol>" <prd_dir>/<slug>/mockups/_src/`), and every cited registry primitive MUST exist in the registry (`grep` `${paths.design_system}/INDEX.md` / `components/`). A symbol that is not in the source, or a primitive that is not in the registry, means a fabricated declaration → reject + re-spawn (do NOT pass it to the gates). Log `design-source: grounded` or `design-source: rejected (<reason>)` in the tracker.
|
|
480
500
|
- This does NOT re-verify pixels — that stays with `visual-fidelity-verifier` (Phase 2.6). It only proves the source was read and the reuse-mapping targets are real. When the card has no design source, skip this step entirely.
|
|
481
501
|
|
|
502
|
+
7c. **i18n fill pass (explicit, deterministic — v5.0.0, promoted from the step-9 sub-bullet).** When `features.has_i18n: true` AND this card's diff added new source-locale keys — detect deterministically, never by intuition:
|
|
503
|
+
```bash
|
|
504
|
+
cd <worktree-path>
|
|
505
|
+
# $SRC_LOCALE = the source-language locale file path (from i18n config / the project's locales root)
|
|
506
|
+
git diff "$TRUNK...HEAD" -- "$SRC_LOCALE" 2>/dev/null | grep -c '^+.*:' || echo 0
|
|
507
|
+
```
|
|
508
|
+
If > 0 new keys → spawn **`i18n-translator`** BY NAME (never `general-purpose`, never a generic agent — the context-aware fill is its exclusive domain per `agents/i18n-protocol.md`), passing: the registry path (`${paths.i18n_registry}`), the diff path, and the instruction "fill the missing target locales for the new source keys — source stays untouched". This runs AFTER the coder commit and BEFORE the step-8 gates, so locale-parity holds first-try (the fail-then-fix round-trip measured ~2M tokens on FEAT-0042). Log `i18n-fill: RAN (<n> keys)` or `i18n-fill: SKIP (no new source keys)` in the tracker. A locale-parity failure later in step 8/9 despite this pass → route back to `i18n-translator`, NEVER retry the coder on it (the coder is source-only by design).
|
|
509
|
+
|
|
510
|
+
7d. **Cap-and-handoff continuation loop (v5.0.0).** If the implementer returned `partial` with a `[CAP-HANDOFF]` marker and `/tmp/checkpoint-<CARD-ID>.json` exists:
|
|
511
|
+
- Respawn a FRESH instance of the SAME owner agent with a minimal continuation briefing: the arch-baseline path (+ the review-bundle path only when it already exists — it is normally built later, at 11c) + the checkpoint path + "continue from `items_remaining`; the checkpoint + baseline are your context — do NOT re-explore, do NOT re-read files already listed as done". Same card, same worktree, same ownership map.
|
|
512
|
+
- **Max 2 continuations.** Still `partial` after the 2nd → do NOT keep respawning: log `[CAP-EXHAUSTED]` in `## Issues & Flags`, treat the unfinished items as `not_implemented` rows, and route through the normal gap path (AC-Closure Gate / follow-up card — the card is NOT auto-DONE). Log each continuation in the tracker: `cap-handoff: CONTINUED n=<i> (items_remaining=<k>)`.
|
|
513
|
+
- `[CAP-HANDOFF]` marker present but the checkpoint file MISSING → do NOT respawn blind: rebuild `items_remaining` from the completion report's `not_implemented` rows and persist it to `/tmp/checkpoint-<CARD-ID>.json` yourself, then run the continuation as above (it counts toward the max-2). If the completion report is also missing, treat as `[CAP-EXHAUSTED]` directly (gap path) — never guess what remains.
|
|
514
|
+
- No `[CAP-HANDOFF]` marker → this step is a no-op.
|
|
515
|
+
|
|
482
516
|
8. **Run the verification gates and CAPTURE their output to disk** (so step 9 can pass it to a fix agent) — **redirect, never `tee`/stream inline** (per § "Context economy" → Gate-output discipline). Each is its own gate:
|
|
483
517
|
When `features.has_toolchain: true`, substitute each command below with `toolchain.commands.{lint,typecheck,test,build}` run verbatim (§ "Toolchain gates"; defaults shown are the fallback when a key is unset).
|
|
484
518
|
```bash
|
|
@@ -514,9 +548,9 @@
|
|
|
514
548
|
```
|
|
515
549
|
The `echo "<gate>:$?"` lines surface only the **exit code** inline — the full log stays on disk. When `stack.language` does NOT include `typescript`, skip the `tsc` line (no equivalent gate). Capturing to `/tmp/*-<CARD-ID>.txt` guarantees the failing output is available for step 9. On a non-zero exit, read a **bounded** extract only (`tail -n 30 /tmp/<gate>-<CARD-ID>.txt`), never the whole log — do NOT run the gates without redirect-to-file capture.
|
|
516
550
|
9. **If any check fails**: categorize the error (`lint | TypeScript | test | build | i18n | ds-gate`), log it in the tracker as `retry-cause: <category>`. An `i18n` failure means a hardcoded user-facing string — the fix is to externalize it via `t()` + register the key (coder STEP 9), never to weaken the gate. A `ds-gate` failure (`DS_CLOSED_SET_VIOLATION`) means this card introduced a NEW canonical component in a CLOSED role family — the fix is to **reuse the family's canonical member** (named in the gate message), never to weaken the gate or add the duplicate; if the requirement genuinely needs a new member, that is a governance decision (escalate as a blocking finding for a `/prd` reconciliation + ADR, do not self-approve). Because the gate is **diff-scoped to this card's changed files**, an `i18n` failure is unambiguously a string **this card** introduced — it is never pre-existing baseline debt in an untouched file (so do NOT spend a retry triaging "is this mine?"; it is). The Final review re-runs the gate over the whole batch diff as the merge backstop.
|
|
517
|
-
- **i18n locale-PARITY ≠ anti-hardcoded (do NOT retry the coder on it).** The `i18n` category above is the *anti-hardcoded* gate. A separate **locale-parity** failure — the project's OWN parity test/build complaining that a new key exists in the source locale but not in the others — surfaces under `test`/`build`, and is **NOT a `coder` retry**: the `coder` writes the source locale only **by design** (`coder.md` STEP 9.
|
|
551
|
+
- **i18n locale-PARITY ≠ anti-hardcoded (do NOT retry the coder on it).** The `i18n` category above is the *anti-hardcoded* gate. A separate **locale-parity** failure — the project's OWN parity test/build complaining that a new key exists in the source locale but not in the others — surfaces under `test`/`build`, and is **NOT a `coder` retry**: the `coder` writes the source locale only **by design** (`coder.md` STEP 9.5), so retrying it just loops. Route it to **`i18n-translator`** (by name), then re-run the gate. The proactive fill already ran at **step 7c** — a parity failure here means 7c was skipped or missed keys; fix via 7c's spawn, never via the coder.
|
|
518
552
|
**Code-recovery check (MUST do before rewriting)**: before spawning a fix agent or rewriting code, check whether lint-staged or a pre-commit hook removed code that is actually needed (e.g. a field "unused" at commit time but consumed by later code). Inspect the captured diff and `git diff`/`git log -p` for the worktree branch and restore the needed code by an explicit file write. **Do NOT `git stash pop` inside the worktree** — `refs/stash` is globally shared across worktrees (`$GIT_COMMON_DIR`), so a stash created or popped in a worktree can corrupt another worktree's state (see Phase 4 WORKTREE COMMIT RULE). If you need to set work aside in a worktree, make a clearly-labelled WIP commit and record its hash in the tracker for later squash, never a stash.
|
|
519
|
-
When spawning a fix agent: scope it exclusively to this card's Edit-allowed files (from `## File Ownership Map`) — it MUST NOT touch files owned by other cards. Pass the fix agent: the **path** to the captured gate log (`/tmp/<gate>-<CARD-ID>.txt` — it Reads the log itself; do NOT inline-paste the full log into its prompt), the error category, and the explicit list of files it may edit. Do NOT ask the user — just fix and re-run. Fix the code, not the tests (unless the test itself is wrong). Repeat up to **3 times**. **Stuck-loop guard**: compare the failing error's fingerprint (file:line + message) between retries; if the SAME error reproduces on 2 consecutive retries, the fix is not converging (often a constraint outside the card's ownership map) — stop early, log `[STUCK-LOOP] <error>` in `## Issues & Flags`, and escalate to the user rather than burning the 3rd retry on an identical failure.
|
|
553
|
+
When spawning a fix agent: scope it exclusively to this card's Edit-allowed files (from `## File Ownership Map`) — it MUST NOT touch files owned by other cards. Pass the fix agent: the **path** to the captured gate log (`/tmp/<gate>-<CARD-ID>.txt` — it Reads the log itself; do NOT inline-paste the full log into its prompt), the error category, and the explicit list of files it may edit. **Fix-pass model routing (v5.0.0 A/B, best-effort)**: spawn the fix-pass `coder` with `model: sonnet` when the runtime supports a model param (the initial build stays on the agent's default opus; a fix-pass applies a scoped, well-specified correction). Consumer kill-switch: an overlay section `## [OVERRIDE] Fix-pass model` in `.baldart/overlays/new.md` naming `fix_pass_model: opus` wins. Append `model=<sonnet|opus>` to the finding's `## Fix Application Log` row so the telemetry can compare recurrence per model. Do NOT ask the user — just fix and re-run. Fix the code, not the tests (unless the test itself is wrong). Repeat up to **3 times**. **Stuck-loop guard**: compare the failing error's fingerprint (file:line + message) between retries; if the SAME error reproduces on 2 consecutive retries, the fix is not converging (often a constraint outside the card's ownership map) — stop early, log `[STUCK-LOOP] <error>` in `## Issues & Flags`, and escalate to the user rather than burning the 3rd retry on an identical failure.
|
|
520
554
|
10. If still failing after 3 retries (or on a stuck loop), log the failure in `## Issues & Flags` and ask the user before continuing.
|
|
521
555
|
11. **Update tracker**: phase = "2-implement DONE", log files changed (short list), retry count, retry causes (e.g., `"2 retries: TypeScript x1, lint x1"`), and test results (new + existing test count, pass/fail).
|
|
522
556
|
11b. **File diff gate** — verify the coder only touched its allowed files. The coder commits its work (per `coder.md`), so a bare `git diff --name-only HEAD` is vacuous (clean tree). Detect what the card's commits actually changed against the trunk, with an uncommitted-fallback:
|
|
@@ -526,7 +560,7 @@
|
|
|
526
560
|
```
|
|
527
561
|
(`$TRUNK` is the trunk branch resolved in Phase 0; if the worktree branched from a different base, the `HEAD~1..HEAD` fallback covers the last commit.)
|
|
528
562
|
Compare against this card's allowed files in `## File Ownership Map`.
|
|
529
|
-
- **All within allowed set** → proceed to
|
|
563
|
+
- **All within allowed set** → proceed to 11c (the bundle build runs on EVERY card, not only after a violation).
|
|
530
564
|
- **Any file outside allowed set** → log the violation in `## Issues & Flags` (`"unauthorized file: <path>"`), then run the **deterministic file-scoped revert SCRIPT** (a file-scoped revert is mechanical git plumbing, not code authoring — a script cannot fabricate or over-reach, and it runs IDENTICALLY on Claude and Codex with no dependency on a `model` param or a `general-purpose` agent Codex lacks). Resolve + run it (opt-in-with-fallback):
|
|
531
565
|
```bash
|
|
532
566
|
REVERT_SH="$(ls .framework/framework/.claude/skills/new/scripts/revert-files.sh .claude/skills/new/scripts/revert-files.sh 2>/dev/null | head -1)"
|
|
@@ -537,4 +571,14 @@
|
|
|
537
571
|
for f in <unauthorized-files...>; do git cat-file -e "$TRUNK:$f" 2>/dev/null && git checkout "$TRUNK" -- "$f" || git rm -f --quiet "$f"; done
|
|
538
572
|
fi
|
|
539
573
|
```
|
|
540
|
-
Re-run build + lint to confirm clean state after revert. Update tracker with the revert outcome (from the script's JSON: `reverted`/`removed`/`refused`), then proceed to
|
|
574
|
+
Re-run build + lint to confirm clean state after revert. Update tracker with the revert outcome (from the script's JSON: `reverted`/`removed`/`refused`), then proceed to 11c.
|
|
575
|
+
11c. **Build the review bundle (v5.0.0 — the anti-re-read handoff for the whole review chain).** With the diff now FINAL (post 11b), materialize the deterministic per-card bundle every downstream reviewer reads FIRST:
|
|
576
|
+
```bash
|
|
577
|
+
BUNDLE_MJS="$(ls .framework/framework/.claude/skills/new/scripts/build-review-bundle.mjs .claude/skills/new/scripts/build-review-bundle.mjs 2>/dev/null | head -1)"
|
|
578
|
+
[ -n "$BUNDLE_MJS" ] && node "$BUNDLE_MJS" --card <CARD-ID> --worktree <worktree-path> --trunk "$TRUNK" \
|
|
579
|
+
--card-yaml <card yaml path> --may-edit "<comma-separated allowed files from ## File Ownership Map>"
|
|
580
|
+
# Also classify the doc invariants NOW (consumed by Phase 3's PASS-fast gate):
|
|
581
|
+
DOCINV_MJS="$(ls .framework/framework/.claude/skills/new/scripts/doc-invariants.mjs .claude/skills/new/scripts/doc-invariants.mjs 2>/dev/null | head -1)"
|
|
582
|
+
[ -n "$DOCINV_MJS" ] && node "$DOCINV_MJS" --diff /tmp/diff-<CARD-ID>.txt --card <card yaml path> --config baldart.config.yml
|
|
583
|
+
```
|
|
584
|
+
The bundle (`/tmp/review-bundle-<CARD-ID>.json`) unifies the diff dump (it writes `/tmp/diff-<CARD-ID>.txt` if missing — Phase 2.55 step 2 and the Phase 3.7 dump become consumers, not producers), the arch-baseline pointer + parsed Hot-File Map, the completion report path (backstop: if the implementer did not write `/tmp/completion-<CARD-ID>.md`, persist its returned `completion-report` block there YOURSELF before running the script), the ownership map, and the gate-log paths. **Every reviewer briefing from here on carries the binding phrase**: *"Review bundle: `/tmp/review-bundle-<CARD-ID>.json` — Read it FIRST. It lists the diff, the changed files, the arch baseline and the completion report. Do NOT re-Read source files to rebuild context you can get from those paths; for any file in `hot_files`, Read ONLY the symbol ranges listed."* Scripts missing (pre-5.0 install) → skip both, log `review-bundle: unavailable (pre-5.0 scripts)` — the reviewers derive scope themselves as before (graceful degradation, Codex-portable either way). Then proceed to Phase 2.5.
|
|
@@ -47,12 +47,13 @@ so it surfaces in telemetry.
|
|
|
47
47
|
- `scopeFiles` ← this card's committed diff (`git diff --name-only "$TRUNK...HEAD"`, fallback `HEAD~1..HEAD`). **Diffs to disk (context economy, v4.43.0):** write the card's full diff to `/tmp/diff-<CARD-ID>.txt` once and derive BOTH the Step-A grep (above) and this `--name-only` list from it — never read the raw diff inline (mirrors the team-mode D.1.5 batching discipline, so the two paths stay symmetric).
|
|
48
48
|
- `editableFiles` ← this card's **File Ownership Map** entries (the coder's write scope; `setup.md` step 3b).
|
|
49
49
|
- `archBaselinePath` ← `/tmp/arch-baseline-<CARD-ID>.md` (persisted at `implement.md` step 5b).
|
|
50
|
+
- `reviewBundlePath` ← `/tmp/review-bundle-<CARD-ID>.json` when it exists (implement.md step 11c), else omit — the workflow threads it into every agent brief as the anti-re-read phrase (v5.0.1; absent → briefs unchanged, pre-5.0 behavior).
|
|
50
51
|
- `hasSecurityFiles` ← any `scopeFiles` path matches `paths.high_risk_modules`.
|
|
51
52
|
- `runSimplify` ← `true` (a non-trivial card always simplifies).
|
|
52
53
|
|
|
53
54
|
```
|
|
54
55
|
Workflow({ name: 'new-card-review', args: {
|
|
55
|
-
cards: [{ cardId, cardPath, scopeFiles, editableFiles, qaTier, hasSecurityFiles, runSimplify, archBaselinePath }],
|
|
56
|
+
cards: [{ cardId, cardPath, scopeFiles, editableFiles, qaTier, hasSecurityFiles, runSimplify, archBaselinePath, reviewBundlePath }],
|
|
56
57
|
worktreePath, baseBranch, config, codexScriptPath // from Phase 0 / tracker
|
|
57
58
|
}})
|
|
58
59
|
```
|
|
@@ -135,8 +136,8 @@ After completeness is verified, clean up the implementation before it reaches re
|
|
|
135
136
|
**Step-by-step**:
|
|
136
137
|
|
|
137
138
|
1. **Enter Phase 2.55 (simplify)** — **no standalone tracker write** for phase entry (§ "Context Tracking" allowlist; the tracker records only `2.55-simplify DONE` at step 6, co-emitted). Proceed directly to step 2.
|
|
138
|
-
2.
|
|
139
|
-
3. Launch **three agents in parallel** (single message). Each receives the **path** `/tmp/diff-<CARD-ID>.txt` and Reads the diff itself — do NOT paste the full diff into the prompts:
|
|
139
|
+
2. Ensure the card diff is on disk — implement.md step 11c's review bundle already wrote `/tmp/diff-<CARD-ID>.txt` (this phase is a CONSUMER, not a producer). Only if the bundle step did not run (pre-5.0 install): `git diff "$TRUNK...HEAD" > /tmp/diff-<CARD-ID>.txt 2>/dev/null || git diff HEAD~1..HEAD > /tmp/diff-<CARD-ID>.txt` in the worktree — the coder already committed, so a bare `git diff` would be vacuous (**redirect, not inline** — per § "Context economy" → Diffs to disk).
|
|
140
|
+
3. Launch **three agents in parallel** (single message). Each receives the **path** `/tmp/diff-<CARD-ID>.txt` and Reads the diff itself — do NOT paste the full diff into the prompts. When `/tmp/review-bundle-<CARD-ID>.json` exists, each briefing ALSO carries the binding anti-re-read phrase (implement.md step 11c): *"Review bundle: `/tmp/review-bundle-<CARD-ID>.json` — Read it FIRST; do NOT re-Read source files to rebuild context you can get from its paths; for `hot_files`, Read ONLY the listed symbol ranges."*
|
|
140
141
|
|
|
141
142
|
- **Reuse agent** — search the codebase for existing utilities/helpers that could replace newly written code. Flag duplicated functionality, inline logic that could use existing utils.
|
|
142
143
|
- **Quality agent** — flag redundant state, parameter sprawl, copy-paste with slight variation, leaky abstractions, stringly-typed code where constants/enums exist, unnecessary JSX nesting, unnecessary comments (WHAT comments, narration, task references).
|
|
@@ -304,7 +305,9 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
|
|
|
304
305
|
|
|
305
306
|
> **Note**: Code review is NOT performed in this phase — it is handled by the **mandatory unconditional `/codexreview` gate in Phase 3.7**, which runs per-card BEFORE the Phase 4 commit. The post-batch `/codexreview` in the Final review remains as a final FULL-diff sweep over the entire batch (since v3.37.0 — the guaranteed full-depth merge gate). This Phase 3 focuses exclusively on documentation sync, which must happen per-card (tied to the specific commit).
|
|
306
307
|
|
|
307
|
-
> **Deferral gate (since v4.7.0 — enumerated, `review_profile`-driven)**: SKIP per-card doc-review and defer it to the **Final Review F.3 doc-reviewer** (which runs over the entire batch) when the card is **`review_profile == light` AND its committed diff touches NO documentation file** (no `.md`, no path under `${paths.references_dir}`, no data-model/ssot/api doc). These are small code changes with no doc surface of their own; their only doc concern is drift, which the batch-wide Final doc-reviewer catches. Log `doc-review: DEFERRED to Final FULL gate (light, no doc files in diff)` and proceed to Phase 3.5. **KEEP per-card doc-review** for: `
|
|
308
|
+
> **Deferral gate (since v4.7.0 — enumerated, `review_profile`-driven)**: SKIP per-card doc-review and defer it to the **Final Review F.3 doc-reviewer** (which runs over the entire batch) when the card is **`review_profile == light` AND its committed diff touches NO documentation file** (no `.md`, no path under `${paths.references_dir}`, no data-model/ssot/api doc). These are small code changes with no doc surface of their own; their only doc concern is drift, which the batch-wide Final doc-reviewer catches. Log `doc-review: DEFERRED to Final FULL gate (light, no doc files in diff)` and proceed to Phase 3.5. **KEEP per-card doc-review** for: `deep` cards, `balanced` cards (they may still PASS-fast via the deterministic gate below — that gate, not this deferral, is their only skip lane), ANY card whose diff touches a doc file (the change IS doc-relevant), and trivial cards (doc-review is their primary gate — § "Trivial-card fast-lane", unchanged). Never defer "for time" — the only enumerated reasons are `light + non-doc diff` and the deterministic PASS-fast below.
|
|
309
|
+
>
|
|
310
|
+
> **Deterministic PASS-fast (v5.0.0 — measured: 84/127 per-card doc reviews returned PASS)**: when implement.md step 11c produced `/tmp/docinv-<CARD-ID>.json` AND it says `pass_fast_eligible: true` (zero non-advisory invariant rows triggered + no doc file in the diff + the card declares no `documentation_impact`/`canonical_docs`) AND the card is NOT `deep` → log `doc-review: PASS-fast (doc-invariants: 0 blocking rows) — Final doc-reviewer backstop stands` and **skip the per-card spawn** (proceed to Phase 3.5). No gate is removed: the batch-Final F.3 doc-reviewer still sees this card's diff. Own-Doc Closure: NEW-file obligations are definitionally empty (no invariant row fired, no docs declared); obligations on MODIFIED primitives/routes are covered by the automatic spec resync (`DS_COMPONENT_STALE` serializer gate, v4.70.0) plus the Final F.3 backstop — the invariant table's R1/R3 fire on new files only, by design. `deep` cards always spawn. Missing docinv JSON → no PASS-fast (fall through to the normal path).
|
|
308
311
|
|
|
309
312
|
12. **Enter Phase 3 (doc-review)** — **no standalone tracker write** for phase entry (§ "Context Tracking" allowlist; the tracker records only `3-doc-review DONE` at step 17, co-emitted). Proceed directly to the next step.
|
|
310
313
|
13. Build a **Doc Sync Context** block. The coder committed in Phase 2, so detect the card's changed files against the trunk (a bare `HEAD` diff would be empty post-commit):
|
|
@@ -340,6 +343,14 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
|
|
|
340
343
|
Doc Sync Context:
|
|
341
344
|
[paste the Doc Sync Context block built above]
|
|
342
345
|
|
|
346
|
+
Deterministic invariant classification (v5.0.0 — when /tmp/docinv-<CARD-ID>.json exists):
|
|
347
|
+
Read /tmp/docinv-<CARD-ID>.json FIRST — its `triggered` rows ARE the drift check:
|
|
348
|
+
Read ONLY the `target_doc` of each triggered row (manual pattern-matching over the
|
|
349
|
+
checklist above applies ONLY when the JSON is missing).
|
|
350
|
+
Review bundle: /tmp/review-bundle-<CARD-ID>.json — Read it FIRST. Do NOT re-Read source
|
|
351
|
+
files to rebuild context you can get from its paths; for `hot_files`, Read ONLY the
|
|
352
|
+
listed symbol ranges.
|
|
353
|
+
|
|
343
354
|
Instructions for doc-reviewer:
|
|
344
355
|
- **WORKTREE ISOLATION (per AGENTS.md § Worktree isolation):** every doc you Edit/Write MUST be UNDER the worktree root <ABSOLUTE_WORKTREE_PATH>. NEVER write a path under the main repo <ABSOLUTE_MAIN_REPO> — it is checked out live by other sessions; `cd` alone does not stop an absolute main-repo path. Edit the worktree-rooted copy of every doc.
|
|
345
356
|
- You OWN the `doc` domain end-to-end: audit AND apply the fixes in THIS invocation.
|
|
@@ -385,6 +396,10 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
|
|
|
385
396
|
- Worktree path: <worktree-path> — `cd` into it before running any gate
|
|
386
397
|
- Branch: <branch-name>
|
|
387
398
|
- Changed files: <list from implementation phase>
|
|
399
|
+
- Review bundle: /tmp/review-bundle-<CARD-ID>.json (when present) — its `files_changed`
|
|
400
|
+
is authoritative for scope (do not re-derive); its `gate_logs` are CONTEXT ONLY
|
|
401
|
+
(Phase-2 logs, possibly stale after Simplify/doc mutations) — the gates your tier
|
|
402
|
+
owns you execute yourself, never cite a bundle log as your own run
|
|
388
403
|
- QA profile: [deep | balanced-escalated]
|
|
389
404
|
|
|
390
405
|
Tier contract: deep → FULL suite. A BALANCED card only reaches this step when risk drift
|
|
@@ -404,7 +419,7 @@ skill's Phase 1 falls back to deriving Gherkin scenarios from
|
|
|
404
419
|
|
|
405
420
|
23. **Read qa-sentinel's output.** Verify the findings file was written to `/qa/<CARD-ID>.md`. **If the file is absent** (qa-sentinel returned a verdict but did not write it): re-invoke qa-sentinel once asking it to (re-)write the file; if still absent, treat the verdict from the return message as authoritative, log `[QA] findings file missing — used return verdict` in `## Issues & Flags`, and pass the captured gate output (not a missing path) to any fix coder.
|
|
406
421
|
24. **If QA verdict is FAIL** (one or more gates failed):
|
|
407
|
-
- Spawn the **coder** agent to fix the FAILING GATES (pass it the findings file path + the failing-gate output — NOT a severity-ranked finding list; qa-sentinel does not emit one). Do NOT ask the user.
|
|
422
|
+
- Spawn the **coder** agent to fix the FAILING GATES (pass it the findings file path + the failing-gate output — NOT a severity-ranked finding list; qa-sentinel does not emit one). Do NOT ask the user. **Fix-pass model routing (v5.0.0 A/B, best-effort)**: spawn it with `model: sonnet` when the runtime supports it (overlay `## [OVERRIDE] Fix-pass model` wins — see implement.md step 9); append `model=<sonnet|opus>` to the Fix Application Log rows.
|
|
408
423
|
- After coder fixes, re-invoke `qa-sentinel` in the same mode to re-validate. Repeat up to **2 times**.
|
|
409
424
|
- If still FAIL after 2 retries: log in `## Issues & Flags` and **ask the user** whether to proceed or stop. **When AUTONOMOUS, apply § AUTONOMOUS RESOLUTION RULE (SKILL.md): category=blocker; recommended=none safe (a failing QA gate is a merge blocker) → materialize a follow-up card and stop this card's commit (NEVER force-proceed past a FAIL verdict).**
|
|
410
425
|
- The commit in Phase 4 MUST NOT happen until QA verdict is PASS (or user explicitly overrides).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* build-review-bundle.mjs — deterministic per-card review bundle (v5.0.0).
|
|
4
|
+
*
|
|
5
|
+
* Materializes ONE JSON file every downstream reviewer reads FIRST, so the
|
|
6
|
+
* chain stops re-deriving scope the orchestrator already has (measured: the
|
|
7
|
+
* same hot files re-read at every link — data.ts 363× coder / 111× reviewer).
|
|
8
|
+
* Pure derivation from git + files already on disk — NEVER an LLM summary
|
|
9
|
+
* (a summarized code artifact would be a second SSOT that drifts).
|
|
10
|
+
*
|
|
11
|
+
* Usage (run from anywhere; --worktree is the card's worktree):
|
|
12
|
+
* node build-review-bundle.mjs --card FEAT-0001-02 --worktree <path> --trunk main \
|
|
13
|
+
* [--card-yaml backlog/FEAT-0001-02.yml] [--may-edit "src/a.ts,src/b.ts"] [--out /tmp/review-bundle-<ID>.json]
|
|
14
|
+
*
|
|
15
|
+
* Output: /tmp/review-bundle-<CARD-ID>.json + a one-line summary on stdout.
|
|
16
|
+
* Exit 0 always (fail-open: missing pieces are null — a reviewer treats a null
|
|
17
|
+
* as "derive this one yourself", never as a hard failure).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import fs from 'fs';
|
|
21
|
+
import { execFileSync } from 'child_process';
|
|
22
|
+
|
|
23
|
+
const args = {};
|
|
24
|
+
for (let i = 2; i < process.argv.length; i++) {
|
|
25
|
+
const k = process.argv[i];
|
|
26
|
+
if (k.startsWith('--')) args[k.slice(2)] = process.argv[i + 1], i++;
|
|
27
|
+
}
|
|
28
|
+
const card = args.card;
|
|
29
|
+
const worktree = args.worktree || process.cwd();
|
|
30
|
+
const trunk = args.trunk || 'main';
|
|
31
|
+
if (!card) { console.error('usage: build-review-bundle.mjs --card <CARD-ID> --worktree <path> --trunk <branch>'); process.exit(0); }
|
|
32
|
+
|
|
33
|
+
const out = args.out || `/tmp/review-bundle-${card}.json`;
|
|
34
|
+
const exists = (p) => { try { fs.accessSync(p); return true; } catch { return false; } };
|
|
35
|
+
const git = (argv) => {
|
|
36
|
+
try { return execFileSync('git', ['-C', worktree, ...argv], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim(); }
|
|
37
|
+
catch { return null; }
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Ownership scope (v5.0.1): in a sequential multi-card batch the worktree's
|
|
41
|
+
// `trunk...HEAD` accumulates PRIOR cards' commits — an unscoped diff would make
|
|
42
|
+
// card N's bundle/docinv classify other cards' files. When --may-edit is passed
|
|
43
|
+
// (implement.md step 11c always passes it), scope the diff to this card's
|
|
44
|
+
// allowed files; standalone invocations without it keep the full diff.
|
|
45
|
+
const mayEdit = args['may-edit'] ? args['may-edit'].split(',').map((s) => s.trim()).filter(Boolean) : null;
|
|
46
|
+
const scopeArgs = mayEdit && mayEdit.length ? ['--', ...mayEdit] : [];
|
|
47
|
+
|
|
48
|
+
// diff: reuse the per-card dump when present, else write it now (same command
|
|
49
|
+
// as implement.md step 8 / codex-gate — this script UNIFIES the dumps).
|
|
50
|
+
const diffPath = `/tmp/diff-${card}.txt`;
|
|
51
|
+
if (!exists(diffPath)) {
|
|
52
|
+
const diff = git(['diff', `${trunk}...HEAD`, ...scopeArgs]) ?? git(['diff', 'HEAD~1..HEAD', ...scopeArgs]);
|
|
53
|
+
if (diff != null) fs.writeFileSync(diffPath, diff + '\n');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// files changed (trunk-relative, uncommitted fallback; card-scoped when possible)
|
|
57
|
+
const filesChanged = (git(['diff', '--name-only', `${trunk}...HEAD`, ...scopeArgs]) ?? git(['diff', '--name-only', 'HEAD~1..HEAD', ...scopeArgs]) ?? '')
|
|
58
|
+
.split('\n').filter(Boolean);
|
|
59
|
+
|
|
60
|
+
// arch baseline + Hot-File Map (deterministic parse of the architect's block:
|
|
61
|
+
// `path (N lines): symbol — L<start>–L<end>`)
|
|
62
|
+
const baselinePath = `/tmp/arch-baseline-${card}.md`;
|
|
63
|
+
let hotFiles = [];
|
|
64
|
+
if (exists(baselinePath)) {
|
|
65
|
+
const base = fs.readFileSync(baselinePath, 'utf8');
|
|
66
|
+
const section = base.split(/^##\s*Hot-File Map\s*$/m)[1];
|
|
67
|
+
if (section) {
|
|
68
|
+
const body = section.split(/^##\s/m)[0];
|
|
69
|
+
// \s* BEFORE the bullet: a match anchored at string start would otherwise
|
|
70
|
+
// eat the leading newline via the later \s* and swallow the bullet into
|
|
71
|
+
// the filename capture.
|
|
72
|
+
const rowRe = /^\s*[-*]?\s*(.+?)\s*\((\d+)\s*lines?\):\s*(.+?)\s*[—-]\s*L(\d+)\s*[–-]\s*L?(\d+)\s*$/gm;
|
|
73
|
+
const byFile = new Map();
|
|
74
|
+
let m;
|
|
75
|
+
while ((m = rowRe.exec(body))) {
|
|
76
|
+
const [, file, lines, symbol, start, end] = m;
|
|
77
|
+
if (!byFile.has(file)) byFile.set(file, { file, lines: Number(lines), symbols: [] });
|
|
78
|
+
byFile.get(file).symbols.push({ name: symbol.trim(), range: [Number(start), Number(end)] });
|
|
79
|
+
}
|
|
80
|
+
hotFiles = [...byFile.values()];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// gate logs written by implement.md step 8 (paths only — reviewers Read on demand)
|
|
85
|
+
const gateLogs = {};
|
|
86
|
+
for (const gate of ['lint', 'tsc', 'test', 'build', 'i18n', 'dsgate']) {
|
|
87
|
+
const p = `/tmp/${gate}-${card}.txt`;
|
|
88
|
+
if (exists(p)) gateLogs[gate] = p;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const completionPath = `/tmp/completion-${card}.md`;
|
|
92
|
+
const docinvPath = `/tmp/docinv-${card}.json`;
|
|
93
|
+
|
|
94
|
+
const bundle = {
|
|
95
|
+
card,
|
|
96
|
+
trunk,
|
|
97
|
+
worktree,
|
|
98
|
+
generated_at_commit: git(['rev-parse', '--short', 'HEAD']),
|
|
99
|
+
card_yaml_path: args['card-yaml'] || null,
|
|
100
|
+
diff_path: exists(diffPath) ? diffPath : null,
|
|
101
|
+
files_changed: filesChanged,
|
|
102
|
+
arch_baseline_path: exists(baselinePath) ? baselinePath : null,
|
|
103
|
+
hot_files: hotFiles,
|
|
104
|
+
completion_report_path: exists(completionPath) ? completionPath : null,
|
|
105
|
+
doc_invariants_path: exists(docinvPath) ? docinvPath : null,
|
|
106
|
+
ownership_may_edit: mayEdit,
|
|
107
|
+
gate_logs: gateLogs,
|
|
108
|
+
generated_by: 'build-review-bundle.mjs v5.0.1',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
fs.writeFileSync(out, JSON.stringify(bundle, null, 2) + '\n');
|
|
112
|
+
console.log(`review-bundle: ${out} (files=${filesChanged.length} hot=${hotFiles.length} baseline=${bundle.arch_baseline_path ? 'yes' : 'MISSING'} completion=${bundle.completion_report_path ? 'yes' : 'missing'})`);
|