baldart 4.53.1 → 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,24 @@ 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
+
17
+ ## [4.53.2] - 2026-06-18
18
+
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).
20
+
21
+ ### Fixed
22
+
23
+ - **`framework/.claude/skills/new/references/completeness.md`** — Phase 2.5 gains a "Grep-verification discipline" MUST note governing every grep check in the phase (step 0 binary-outcome branches, step 4 symbol verification, the `data_fields` checks): `rg -F` for code-shaped literals, one pattern per call; `0 matches` ≠ absent; a 0-match contradicting the coder report → re-confirm at the cited `file:line` before classifying Missing/Partial or spawning a gap-fix agent.
24
+ - **`framework/.claude/skills/new/references/team-mode.md`** — the post-completion (D.1+) on-disk spot-check step now cites that discipline, so a quote-broken AC/symbol grep in team mode cannot trigger a needless fix-coder.
25
+
8
26
  ## [4.53.1] - 2026-06-18
9
27
 
10
28
  **Review workflows no longer spawn fixers for verified-but-no-action findings, and a `finding_id` collision that turned applied fixes into false residuals is closed.** Diagnosed from a real `/new FEAT-0035` team-mode run where the per-wave review cluster spawned `security-reviewer` (Sonnet) + `coder` (Opus) for ~110k tokens to apply ~3 LOW fixes and **no-op ~8 non-actionable findings** — finders (simplify / security / codex / api-perf / doc) emit *verified observations that need NO change* (`minimal_fix_direction` = "No fix required" / "acceptable as-is" / "verified non-issue") as findings; these survive the false-positive check (they are TRUE, not false positives), are `preValidated` → skip Verify → classified `VERIFIED` → flood the fix partitions. The system conflated **VERIFIED with actionable**. Fix (two fail-safe layers): the `FINDING` schema gains an optional `requires_action` flag, every finder prompt is told to set it `false` (or simply not emit) a no-change observation, and a deterministic `isActionable()` gate — honoured for MED/LOW only, a **BLOCKER/HIGH is always actionable** so a cross-wave merge-ordering HIGH still surfaces as residual — segregates no-action findings into a recorded/counted bucket (`summary.noAction`, `decision=skipped reason=no-action`) that **never reaches a writer and is never silently dropped**. A regex backstop on the fix direction covers a finder that omits the flag. **Second defect (root cause of an observed empty-coder re-spawn):** finders number `finding_id` (`F###`) independently, so a security `F003` and a migration `F003` collide; the Fix-phase bookkeeping (`appliedIds`/`unresolvedIds`/`codeResidual`/`bucket`) keys on `finding_id` with flat Sets, so one finder's `unresolved` id dragged another finder's *applied* same-id fix into residual → the skill then re-spawned a coder that found nothing to do. Fixed by making `finding_id` globally unique at the fan-in (prefix `source` + index). Verified with a deterministic test that extracts the real gate from source and runs it over the actual FEAT-0035 findings (4 no-action LOW suppressed incl. a mid-prose "no fix required", 3 real LOW + the cross-wave HIGH kept) plus a collision regression (bug reproduces pre-fix, gone post-fix). Applied to **both** review workflows + their prose-SSOT (so the inline fallback benefits) + (opt-in) the finder agents' system prompts. **PATCH** (economy/quality refinement of existing workflows; no new agent/skill/command, no install-layout change, no `baldart.config.yml` key).
package/VERSION CHANGED
@@ -1 +1 @@
1
- 4.53.1
1
+ 4.53.3
@@ -6,6 +6,10 @@
6
6
 
7
7
  Before triggering any review, you MUST verify that the coder agent implemented **every requirement and acceptance criterion** in the backlog card. This is a blocking gate — do NOT proceed to Phase 3 with unimplemented items.
8
8
 
9
+ > **Grep-verification discipline (MUST — applies to every grep check in this phase: step 0, step 4, the `data_fields` checks).** The completeness checks below confirm an item by grepping the changed files for a token. Two failure modes turn a *false* "not found" into a *false* "Missing/Partial":
10
+ > 1. **Quote-broken pattern.** Code-shaped tokens contain shell/regex metacharacters — a TS union literal `mode:'catalog'|'picker'`, an object key, anything with `'` `"` `|` `[]` `?` `(`. Embedding such a pattern in an `echo "=== … ===" && grep …` block or an unquoted `-E` arg breaks the shell/regex parse → the grep matches **nothing even though the code is there**. **Search literals with `rg -F '<exact string>'` (fixed-string, no regex), single-quote the WHOLE pattern, and run ONE pattern per `rg` call** — never fold the search token into a quoted header/echo composition.
11
+ > 2. **`0 matches` ≠ absent.** A zero-match result is ambiguous (true miss vs. broken pattern / wrong file / wrong escaping). Do NOT classify an item `Missing`/`Partial` on a 0-match alone. **If the 0-match CONTRADICTS the coder completion report's claim that the item is implemented, treat it as a likely broken grep — re-confirm by reading the report's cited `file:line` (or a corrected `rg -F`) BEFORE flagging or spawning a gap-fix agent.** Never re-spawn a coder on a 0-match alone. When unsure, a targeted `Read` at the report's `file:line` is more reliable than a hand-rolled grep.
12
+
9
13
  **Step-by-step**:
10
14
 
11
15
  0. **Conditional requirements pre-scan** (BLOCKING — before building the main checklist):
@@ -152,6 +152,8 @@ For each completed agent:
152
152
 
153
153
  After ALL agents in the group complete successfully:
154
154
 
155
+ > **On-disk spot-checks follow the grep-verification discipline** (`references/completeness.md` § "Grep-verification discipline"): when you grep the worktree to confirm a coder implemented an AC / symbol / field, search code-shaped tokens with `rg -F '<exact string>'` (one pattern per call, fully single-quoted — never inside an `echo "===…" && grep` block), and treat a `0-match` that CONTRADICTS the coder's completion report as a likely broken grep → re-confirm by reading the cited `file:line` BEFORE alarming or re-spawning. A false "AC not implemented" from a quote-broken grep must not trigger a needless fix-coder.
156
+
155
157
  1. **D.1 — Build verification (group)** — Run `npm run build` (when `has_toolchain`, the configured `toolchain.commands.build` verbatim — § "Toolchain gates") in the worktree to verify combined changes compile (redirect to `/tmp/build-group.txt` per § "Context economy"; surface only exit code + bounded extract on failure). If build fails, identify which card's changes broke it (from `git diff --name-only` per card), spawn a targeted fix-coder for those files only.
156
158
 
157
159
  1.5. **D.1.5 — Effective per-card review profile (compute ONCE; drives D.2 + D.4b)** — For EACH card in the group, compute its **effective codex profile** with the SAME deterministic rule the sequential Phase 3.7 Step C uses, so the two paths never disagree:
@@ -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.1",
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"