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.
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`
|
|
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 committed — find 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
|
-
`
|
|
204
|
-
`
|
|
205
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
`
|
|
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 merge — no 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
|
-
`
|
|
161
|
-
`
|
|
162
|
-
`
|
|
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
|
-
|
|
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) {
|