baldart 4.53.2 → 4.53.3

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 CHANGED
@@ -5,6 +5,15 @@ All notable changes to BALDART will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.53.3] - 2026-06-18
9
+
10
+ **The per-wave/Final Codex review wrapper is now a thin relay on a cheaper model, not an Opus agent that re-investigates Codex's findings.** Observed on a real `/new FEAT-0035` run: the `codex` discovery agent burned ~127k tokens / 26 tool calls — it launched the Codex companion (correct) but then **re-grepped/sed'd the source to "mechanically confirm" Codex's findings** before returning, despite the design already trusting them (`preValidated`, FP-checked by Codex). Root cause: the wrapper consumed Codex's **freeform `task` output** (prose), which forced a smart+expensive model to parse 6 fields per finding out of prose — and it over-reached into self-investigation. Fix: instruct Codex to emit its findings as strict JSON between explicit `<<<FINDINGS_JSON>>>` / `<<<END_FINDINGS_JSON>>>` sentinels (verified on two real companion runs: output round-trips cleanly through `JSON.parse` with all our fields incl. `domain` + `requires_action`, and the sentinels make extraction deterministic despite the companion's `[codex]` trace lines + the truncated "Assistant message captured:" echo — the truncated copy lacks the closing sentinel so the last-complete-pair awk skips it). The wrapper is now a **pure relay**: write the task to a temp file (no shell-quote breakage — same discipline as v4.53.2), launch Codex in the background, poll for the END sentinel, extract via a fixed awk, `JSON.parse`, return — **no re-grep, no re-verify**. Model dropped from the inherited Opus to **`haiku` per-wave** (a poll misfire degrades safely to the `code-reviewer` fallback, and the Final re-runs Codex batch-wide) and **`sonnet` at the Final** (last cross-model gate before merge — no downstream Codex backstop). The review *mechanism* (Codex `task` + `--cwd` worktree + the changed-file list) is unchanged from production — only the output format, the wrapper's scope, and the model tier change. **PATCH** (cost/economy refinement of existing workflows; no new capability, no schema/config key, no install change).
11
+
12
+ ### Fixed
13
+
14
+ - **`framework/.claude/workflows/new-card-review.js`** — `codexPrompt` rewritten as a thin relay (write task → background launch → poll END sentinel → deterministic awk extract → `JSON.parse` → return; explicitly forbids self-investigation); Codex instructed to emit sentinel-delimited strict JSON findings; the `codex` discovery thunk runs on `model: 'haiku'`.
15
+ - **`framework/.claude/workflows/new-final-review.js`** — same relay rewrite + sentinel-JSON contract; the Final `codex` thunk runs on `model: 'sonnet'` (last cross-model gate, no downstream backstop).
16
+
8
17
  ## [4.53.2] - 2026-06-18
9
18
 
10
19
  **`/new` completeness/AC verification no longer treats a quote-broken grep as proof an AC is unimplemented.** Diagnosed from a real `/new FEAT-0035` team-mode L1 run: the orchestrator spot-checked AC-3 by grepping `data/products.ts` for the `mode:'catalog'|'picker'` token, the pattern's embedded single-quotes + pipe broke the shell/regex parse inside the `echo "=== … ===" && grep` block → **0 matches even though `mode` was fully implemented** (`ListProductsMode` type + `mode??'catalog'` + picker branch all present) → a false "AC-3 not implemented" alarm. It self-recovered (the orchestrator noticed the grep contradicted the coder's completion report and re-grepped correctly), but a false "AC absent" can otherwise trigger a needless gap-fix coder re-spawn — the same waste class as v4.53.1. Two compounding causes, both addressed: (1) grep-as-AC-evidence over **code-shaped tokens** is brittle (the verification phase is prose-mandated to "grep changed files for the implementation"), and (2) `0 matches` was conflated with "absent". Added a **Grep-verification discipline** to the completeness SSOT: search literals with `rg -F` (fixed-string, one fully-single-quoted pattern per call — never folded into an `echo "===…" && grep` composition), and a `0-match` that CONTRADICTS the coder's structured completion report is a likely broken grep → re-confirm by Reading the cited `file:line` BEFORE flagging or re-spawning (never re-spawn a coder on a 0-match alone). **PATCH** (verification-guidance hygiene to existing prose; no code/behaviour change in the workflows, no `baldart.config.yml` key).
package/VERSION CHANGED
@@ -1 +1 @@
1
- 4.53.2
1
+ 4.53.3
@@ -191,18 +191,36 @@ log(codexResolved
191
191
  ? `Codex companion resolved deterministically: ${codexScriptPath}`
192
192
  : 'Codex companion NOT found (deterministic pre-flight) — primary code review via code-reviewer fallback.')
193
193
 
194
- const codexPrompt =
195
- `Run a deep, cross-model code review over this wave's diff, following the review protocol summarized in ${protocolRef} (Phase 3.7). The code is already written and committed — find bugs, regressions, security issues, and quality problems.\n\n` +
196
- `The Codex companion script is ALREADY CONFIRMED PRESENT at:\n ${codexScriptPath}\n` +
197
- `Launch it in the BACKGROUND and poll for completion do NOT run it synchronously (a sync run would hit the Bash tool timeout):\n` +
198
- ` • REVIEW_FILE=a unique /tmp file (e.g. /tmp/codexreview-wave-${cards[0].cardId}-$$.md)\n` +
199
- ` • node "${codexScriptPath}" task "<your review instructions>" > "$REVIEW_FILE" 2>&1 — run this with run_in_background:true (the launching call returns immediately).\n` +
200
- ` • Then POLL $REVIEW_FILE (BashOutput / repeated reads) until it holds a terminal result, up to a full 10-minute window.\n` +
201
- `Return codexAvailable:false ONLY if $REVIEW_FILE ends up containing "CODEX_NOT_FOUND" or stays empty after the FULL 10-minute windowNEVER because a single Bash call returned slowly.\n\n` +
194
+ // Codex companion review. The WRAPPER is a thin relay (model: haiku — see the thunk): it launches
195
+ // Codex, waits, and extracts Codex's JSON findings with a deterministic awk. It does NOT review or
196
+ // re-verify code itself (Codex's findings are authoritative + FP-validated) the old wrapper burned
197
+ // ~100k+ tokens re-grepping to "confirm" findings the design already trusts. Codex emits its findings
198
+ // between explicit sentinels so the extraction is unambiguous despite the companion's `[codex]` trace
199
+ // lines and the truncated "Assistant message captured:" echo (verified on real companion runs).
200
+ const codexReviewTask =
201
+ `Run a deep code review over this wave's committed diff. The code is already written and committedfind bugs, regressions, security issues, and quality problems, per the protocol in ${protocolRef} (Phase 3.7).\n\n` +
202
202
  `${waveBrief}\n\n${baselineBrief}\n\n` +
203
- `For each finding return: finding_id, title, severity (BLOCKER|HIGH|MEDIUM|LOW), confidence (0-100), evidence (exact file:line + code quote), minimal_fix_direction, and domain (doc|security|migration|code|perf|test). ` +
204
- `Run the mandatory false-positive check on every finding and suppress the unconvincing ones (your findings are treated as already FP-validated). ` +
205
- `Then the actionability check: an observation you have VERIFIED needs NO change anywhere (the fix direction would be "no fix required" / "acceptable as-is" / "verified non-issue") is not work — set requires_action:false on it (it is recorded but never sent to a fixer), or simply do not emit it. Emit requires_action true/omitted ONLY when a concrete edit is needed. Set codexAvailable:true when the review ran.`
203
+ `Run the mandatory false-positive check on every finding and suppress the unconvincing ones. Actionability: set requires_action:false on a VERIFIED observation that needs NO change (a cleared concern MED/LOW only; a BLOCKER/HIGH is always requires_action:true).\n\n` +
204
+ `Emit your findings as your FINAL message, between these EXACT sentinels and nothing else after the opening sentinel:\n` +
205
+ `<<<FINDINGS_JSON>>>\n` +
206
+ `[{"finding_id":"<CARD-ID>-F###","title":"...","severity":"BLOCKER|HIGH|MEDIUM|LOW","confidence":<0-100>,"evidence":"exact file:line + code quote","minimal_fix_direction":"...","domain":"doc|security|migration|code|perf|test","requires_action":true}]\n` +
207
+ `<<<END_FINDINGS_JSON>>>\n` +
208
+ `Use an empty array [] between the sentinels if there are no real bugs.`
209
+ const codexTaskFile = `/tmp/codextask-${cards[0].cardId}-$$.txt`
210
+ const codexReviewFile = `/tmp/codexreview-wave-${cards[0].cardId}-$$.md`
211
+ const codexPrompt =
212
+ `You are a THIN RELAY around the Codex companion — do NOT review or investigate code yourself, do NOT grep/sed/Read source to "confirm" findings (Codex's findings are authoritative and already FP-validated; re-verifying them is wasted work). Your ONLY job: launch Codex, wait, extract its JSON, return it.\n\n` +
213
+ `The companion script is ALREADY CONFIRMED PRESENT at:\n ${codexScriptPath}\n\n` +
214
+ `Run EXACTLY these steps and nothing else:\n` +
215
+ ` 1. Write the task text (everything after "TASK PROMPT:" at the end of this message) to ${codexTaskFile} using the Write tool — do NOT inline it into the shell command (avoids quote breakage).\n` +
216
+ ` 2. Launch Codex in the BACKGROUND (run_in_background:true — a sync run hits the Bash timeout):\n` +
217
+ ` node "${codexScriptPath}" task "$(cat ${codexTaskFile})" --cwd "${a.worktreePath || '.'}" > ${codexReviewFile} 2>&1\n` +
218
+ ` 3. POLL ${codexReviewFile} (BashOutput / repeated reads) until it contains "<<<END_FINDINGS_JSON>>>" (terminal) OR "CODEX_NOT_FOUND" OR the full 10-minute window has elapsed.\n` +
219
+ ` 4. Extract the findings with this EXACT command (deterministic — skips the [codex] trace + truncated echo, takes the LAST complete sentinel pair):\n` +
220
+ ` awk '/<<<FINDINGS_JSON>>>/{c=1;b="";next}/<<<END_FINDINGS_JSON>>>/{c=0;last=b;next}c{b=b $0 ORS}END{printf "%s",last}' ${codexReviewFile}\n` +
221
+ ` Parse that output as JSON and return it verbatim as \`findings\` (its keys already match the schema). Do NOT re-verify, re-grep, or Read any source file.\n\n` +
222
+ `Set codexAvailable:false ONLY if ${codexReviewFile} contains "CODEX_NOT_FOUND" or has no "<<<END_FINDINGS_JSON>>>" after the FULL 10-minute window — NEVER because a single poll returned slowly. Otherwise codexAvailable:true.\n\n` +
223
+ `TASK PROMPT:\n${codexReviewTask}`
206
224
 
207
225
  const tcGateLines = [
208
226
  ['lint', tcCmds.lint], ['typecheck', tcCmds.typecheck], ['test', tcCmds.test],
@@ -242,7 +260,9 @@ for (const c of cards) {
242
260
  }
243
261
  }
244
262
  if (codexResolved) {
245
- findThunks.push(() => agent(codexPrompt, { label: 'codex', phase: 'Discovery', schema: CODEX_SCHEMA }).then((r) => ({ kind: 'codex', card: null, r })))
263
+ // model: haiku the wrapper is a deterministic relay (launch + poll + awk-extract); a poll misfire
264
+ // degrades safely to the code-reviewer fallback below, and the Final review re-runs Codex batch-wide.
265
+ findThunks.push(() => agent(codexPrompt, { label: 'codex', phase: 'Discovery', model: 'haiku', schema: CODEX_SCHEMA }).then((r) => ({ kind: 'codex', card: null, r })))
246
266
  }
247
267
  if (maxQaTier === 'full') {
248
268
  findThunks.push(() => agent(qaPrompt, { label: 'qa-sentinel', phase: 'Discovery', agentType: 'qa-sentinel', schema: GATES_SCHEMA }).then((r) => ({ kind: 'qa', card: null, r })))
@@ -148,18 +148,36 @@ log(codexResolved
148
148
  ? `Codex companion resolved deterministically: ${codexScriptPath}`
149
149
  : 'Codex companion NOT found (deterministic pre-flight) — primary review via code-reviewer fallback.')
150
150
 
151
- const codexPrompt =
152
- `Run a deep, cross-model code review over this batch diff, following the /codexreview protocol summarized in ${protocolRef} (Step F.3). The code is already written and committed — find bugs, regressions, security issues, and quality problems.\n\n` +
153
- `The Codex companion script is ALREADY CONFIRMED PRESENT at:\n ${codexScriptPath}\n` +
154
- `Launch it in the BACKGROUND and poll for completion — do NOT run it synchronously (a sync run would hit the Bash tool timeout and is the exact bug this design fixes):\n` +
155
- ` • REVIEW_FILE=a unique /tmp file (e.g. /tmp/codexreview-batch-${a.firstCardId || 'batch'}-$$.md)\n` +
156
- ` • node "${codexScriptPath}" task "<your review instructions>" > "$REVIEW_FILE" 2>&1 run this with run_in_background:true (the launching call returns immediately).\n` +
157
- ` • Then POLL $REVIEW_FILE (BashOutput / repeated reads) until it holds a terminal result, up to a full 10-minute window.\n` +
158
- `Return codexAvailable:false ONLY if $REVIEW_FILE ends up containing "CODEX_NOT_FOUND" or stays empty after the FULL 10-minute window NEVER because a single Bash call returned slowly. The script's existence is already verified, so a premature false here is a bug, not a capability gap.\n\n` +
151
+ // Codex companion review. The WRAPPER is a thin relay (model: sonnet — see the thunk): it launches
152
+ // Codex, waits, and extracts Codex's JSON findings with a deterministic awk; it does NOT review or
153
+ // re-verify code itself (Codex's findings are authoritative + FP-validated). Codex emits findings
154
+ // between explicit sentinels so the extraction is unambiguous despite the companion's `[codex]` trace
155
+ // and the truncated "Assistant message captured:" echo. Sonnet (not haiku) here because the Final is
156
+ // the last cross-model gate before mergeno downstream Codex pass backstops a wrapper misfire.
157
+ const codexReviewTask =
158
+ `Run a deep code review over this batch diff, per the /codexreview protocol in ${protocolRef} (Step F.3). The code is already written and committed find bugs, regressions, security issues, and quality problems.\n\n` +
159
159
  `${scopeBrief}\n\n${baselineBrief}\n\n` +
160
- `For each finding return: finding_id, title, severity (BLOCKER|HIGH|MEDIUM|LOW), confidence (0-100), evidence (exact file:line + code quote), minimal_fix_direction, and domain (doc|security|migration|code|perf|test). ` +
161
- `Run the mandatory false-positive check on every finding and suppress the unconvincing ones (your findings are treated as already FP-validated). ` +
162
- `Then the actionability check: an observation you have VERIFIED needs NO change anywhere (the fix direction would be "no fix required" / "acceptable as-is" / "verified non-issue") is not work — set requires_action:false on it (it is recorded but never sent to a fixer), or simply do not emit it. Emit requires_action true/omitted ONLY when a concrete edit is needed. Set codexAvailable:true when the review ran.`
160
+ `Run the mandatory false-positive check on every finding and suppress the unconvincing ones. Actionability: set requires_action:false on a VERIFIED observation that needs NO change (a cleared concern MED/LOW only; a BLOCKER/HIGH is always requires_action:true).\n\n` +
161
+ `Emit your findings as your FINAL message, between these EXACT sentinels and nothing else after the opening sentinel:\n` +
162
+ `<<<FINDINGS_JSON>>>\n` +
163
+ `[{"finding_id":"<CARD-ID>-F###","title":"...","severity":"BLOCKER|HIGH|MEDIUM|LOW","confidence":<0-100>,"evidence":"exact file:line + code quote","minimal_fix_direction":"...","domain":"doc|security|migration|code|perf|test","requires_action":true}]\n` +
164
+ `<<<END_FINDINGS_JSON>>>\n` +
165
+ `Use an empty array [] between the sentinels if there are no real bugs.`
166
+ const codexTaskFile = `/tmp/codextask-batch-${a.firstCardId || 'batch'}-$$.txt`
167
+ const codexReviewFile = `/tmp/codexreview-batch-${a.firstCardId || 'batch'}-$$.md`
168
+ const codexPrompt =
169
+ `You are a THIN RELAY around the Codex companion — do NOT review or investigate code yourself, do NOT grep/sed/Read source to "confirm" findings (Codex's findings are authoritative and already FP-validated). Your ONLY job: launch Codex, wait, extract its JSON, return it.\n\n` +
170
+ `The companion script is ALREADY CONFIRMED PRESENT at:\n ${codexScriptPath}\n\n` +
171
+ `Run EXACTLY these steps and nothing else:\n` +
172
+ ` 1. Write the task text (everything after "TASK PROMPT:" at the end of this message) to ${codexTaskFile} using the Write tool — do NOT inline it into the shell command (avoids quote breakage).\n` +
173
+ ` 2. Launch Codex in the BACKGROUND (run_in_background:true — a sync run hits the Bash timeout):\n` +
174
+ ` node "${codexScriptPath}" task "$(cat ${codexTaskFile})" --cwd "${a.worktreePath || '.'}" > ${codexReviewFile} 2>&1\n` +
175
+ ` 3. POLL ${codexReviewFile} (BashOutput / repeated reads) until it contains "<<<END_FINDINGS_JSON>>>" (terminal) OR "CODEX_NOT_FOUND" OR the full 10-minute window has elapsed.\n` +
176
+ ` 4. Extract the findings with this EXACT command (deterministic — skips the [codex] trace + truncated echo, takes the LAST complete sentinel pair):\n` +
177
+ ` awk '/<<<FINDINGS_JSON>>>/{c=1;b="";next}/<<<END_FINDINGS_JSON>>>/{c=0;last=b;next}c{b=b $0 ORS}END{printf "%s",last}' ${codexReviewFile}\n` +
178
+ ` Parse that output as JSON and return it verbatim as \`findings\` (its keys already match the schema). Do NOT re-verify, re-grep, or Read any source file.\n\n` +
179
+ `Set codexAvailable:false ONLY if ${codexReviewFile} contains "CODEX_NOT_FOUND" or has no "<<<END_FINDINGS_JSON>>>" after the FULL 10-minute window — NEVER because a single poll returned slowly. Otherwise codexAvailable:true.\n\n` +
180
+ `TASK PROMPT:\n${codexReviewTask}`
163
181
 
164
182
  const docPrompt =
165
183
  `Cross-card documentation + SSOT-registry review over the batch diff, per ${protocolRef} Step F.3 (doc-reviewer row). Check doc consistency, ssot-registry completeness, and invariants across the changed files.\n\n${scopeBrief}\n\n${baselineBrief}\n\n` +
@@ -201,7 +219,9 @@ if (!slimDoc) {
201
219
  }
202
220
  // Codex thunk runs ONLY when the pre-flight resolved the companion (else: no wasted agent).
203
221
  if (codexResolved) {
204
- reviewThunks.unshift(() => agent(codexPrompt, { label: 'codex', phase: 'Review', schema: CODEX_SCHEMA }).then((r) => ({ kind: 'codex', r })))
222
+ // model: sonnet thin relay (launch + poll + awk-extract), no self-investigation; sonnet (not haiku)
223
+ // because this is the last cross-model gate before merge (no downstream Codex pass backstops a misfire).
224
+ reviewThunks.unshift(() => agent(codexPrompt, { label: 'codex', phase: 'Review', model: 'sonnet', schema: CODEX_SCHEMA }).then((r) => ({ kind: 'codex', r })))
205
225
  }
206
226
  // api-perf-cost-auditor: skipped when no API/data files OR when it already ran per-card (slimApi).
207
227
  if (!slimApi && a.hasApiDataFiles !== false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "4.53.2",
3
+ "version": "4.53.3",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"