baldart 4.52.4 → 4.53.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.
@@ -54,6 +54,7 @@ const FINDING = {
54
54
  evidence: { type: 'string', description: 'exact file:line + code quote' },
55
55
  minimal_fix_direction: { type: 'string' },
56
56
  domain: { enum: ['doc', 'security', 'migration', 'code', 'perf', 'test'], description: 'Domain-Override routing bucket' },
57
+ requires_action: { type: 'boolean', description: 'false = a VERIFIED observation that needs NO change anywhere (concern cleared / safe-as-is / advisory) — recorded for audit, NEVER routed to a writer. Omit (⇒ actionable) when a concrete edit is needed. If a change is needed but not by you (out of scope), keep true and say so in minimal_fix_direction (it surfaces as residual).' },
57
58
  },
58
59
  }
59
60
  const FINDINGS_SCHEMA = {
@@ -157,16 +158,19 @@ const codexPrompt =
157
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` +
158
159
  `${scopeBrief}\n\n${baselineBrief}\n\n` +
159
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). ` +
160
- `Run the mandatory false-positive check on every finding and suppress the unconvincing ones (your findings are treated as already FP-validated). Set codexAvailable:true when the review ran.`
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.`
161
163
 
162
164
  const docPrompt =
163
165
  `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` +
164
- `You OWN the doc domain end-to-end: run the mandatory false-positive check on every finding yourself and SUPPRESS the unconvincing ones — your surviving findings are treated as already validated and are NOT re-judged by another agent (a generic code-reviewer judging prose would be cross-domain). Flag only a finding you genuinely cannot resolve as confidence < 80.\n\n` +
166
+ `You OWN the doc domain end-to-end: run the mandatory false-positive check on every finding yourself and SUPPRESS the unconvincing ones — your surviving findings are treated as already validated and are NOT re-judged by another agent (a generic code-reviewer judging prose would be cross-domain). ` +
167
+ `Then the actionability check: an observation you VERIFIED needs NO change (the fix direction would be "no fix required" / "already accurate") is not work — set requires_action:false (recorded, never sent to a fixer), or do not emit it. Flag only a finding you genuinely cannot resolve as confidence < 80.\n\n` +
165
168
  `Return findings (domain almost always "doc"). Use the finding schema fields.`
166
169
 
167
170
  const apiPrompt =
168
171
  `API / data-model / performance / cost defect review over the batch diff, per ${protocolRef} Step F.3 (api-perf-cost-auditor row). Look for unbounded reads, N+1, missing pagination, contract drift, and cost regressions.\n\n${scopeBrief}\n\n${baselineBrief}\n\n` +
169
- `You OWN the api/perf/data domain end-to-end: run the mandatory false-positive check on every finding yourself and SUPPRESS the unconvincing ones — your surviving findings are treated as already validated and are NOT re-judged by another agent. Flag only a finding you genuinely cannot resolve as confidence < 80.\n\n` +
172
+ `You OWN the api/perf/data domain end-to-end: run the mandatory false-positive check on every finding yourself and SUPPRESS the unconvincing ones — your surviving findings are treated as already validated and are NOT re-judged by another agent. ` +
173
+ `Then the actionability check: an observation you VERIFIED needs NO change (the fix direction would be "no fix required" / "acceptable as-is" / "optional") is not work — set requires_action:false (recorded, never sent to a fixer), or do not emit it. Flag only a finding you genuinely cannot resolve as confidence < 80.\n\n` +
170
174
  `Return findings with domain in {code, perf, migration, security}. Use the finding schema fields.`
171
175
 
172
176
  const qaPrompt =
@@ -238,7 +242,7 @@ for (const item of reviewResults) {
238
242
  if (!codexRan) {
239
243
  codexEngine = 'code-reviewer (fallback)'
240
244
  const fb = await agent(
241
- `Codex was unavailable for the batch final review. Run the FULL code review yourself over the batch diff, per ${protocolRef} Step F.3.\n\n${scopeBrief}\n\n${baselineBrief}\n\nReturn findings using the schema fields, with a self false-positive check applied.`,
245
+ `Codex was unavailable for the batch final review. Run the FULL code review yourself over the batch diff, per ${protocolRef} Step F.3.\n\n${scopeBrief}\n\n${baselineBrief}\n\nReturn findings using the schema fields, with a self false-positive check applied. An observation you VERIFIED needs NO change (fix direction "no fix required" / "acceptable as-is") is not work — set requires_action:false (recorded, never sent to a fixer), or do not emit it.`,
242
246
  { label: 'code-reviewer (fallback)', phase: 'Review', agentType: 'code-reviewer', schema: FINDINGS_SCHEMA }
243
247
  )
244
248
  // the fallback code-reviewer IS the primary code-review engine here and applied its own FP
@@ -247,6 +251,12 @@ if (!codexRan) {
247
251
  if (fb && Array.isArray(fb.findings)) raw.push(...fb.findings.map((f) => ({ ...f, source: 'code-reviewer', preValidated: true })))
248
252
  }
249
253
 
254
+ // Globally-unique finding_id at the fan-in (parity with new-card-review.js). Finders number F###
255
+ // INDEPENDENTLY → a Codex F003 and a doc F003 collide; downstream telemetry/routing (new2 ledger,
256
+ // the skill's F.5 partition) keys on finding_id, so a collision conflates them. Prefix source +
257
+ // running index → uniqueness without losing the human-readable id.
258
+ raw.forEach((f, i) => { f.finding_id = `${f.source || 'src'}#${i}:${f.finding_id || ('F' + i)}` })
259
+
250
260
  // ───────────────────────────────────────────────────────────────────────────
251
261
  // Phase Verify (F.4) — specialist-owned validation (NOT a generic code-reviewer re-judge).
252
262
  // Each domain specialist owns its lane end-to-end: it FP-checks its OWN findings in the
@@ -292,20 +302,31 @@ async function verifyFinding(f) {
292
302
  return { ...f, classification: (v && v.classification) || 'NEEDS_MANUAL_CONFIRMATION' }
293
303
  }
294
304
 
295
- // ---- Result: drop FALSE_POSITIVE, keep VERIFIED + NEEDS_MANUAL (F.4 step 9) --
296
- const findings = classified.filter((f) => f.classification !== 'FALSE_POSITIVE')
305
+ // ---- Result: drop FALSE_POSITIVE, segregate no-action, keep actionable VERIFIED + NEEDS_MANUAL --
306
+ // Actionability gate (parity with new-card-review.js): a VERIFIED finding needing NO change must NOT
307
+ // reach the skill's F.5 writer partition. Honoured ONLY for MED/LOW — BLOCKER/HIGH always actionable.
308
+ // No silent drop: no-action findings are returned separately (noActionFindings) + counted, so the
309
+ // skill can log them; only the actionable set flows into `findings` (what F.5 routes to writers).
310
+ const NO_ACTION_RE = /(^|[.\s(])(no\s+(fix|change|action)\s+(is\s+)?(required|needed)|none\s+(needed|required)|not\s+a\s+(defect|bug|correctness\s+bug)|acceptable\s+as[-\s]is|verified\s+(safe|non[-\s]issue)|no\s+action\s+(needed|required))([.\s,;)]|$)/i
311
+ const isNoAction = (f) => f.classification === 'VERIFIED' && (f.severity !== 'BLOCKER' && f.severity !== 'HIGH') &&
312
+ (f.requires_action === false || NO_ACTION_RE.test(String(f.minimal_fix_direction || '')))
313
+ const noActionFindings = classified.filter(isNoAction)
314
+ const noActionSet = new Set(noActionFindings)
315
+ const findings = classified.filter((f) => f.classification !== 'FALSE_POSITIVE' && !noActionSet.has(f))
316
+ for (const f of noActionFindings) log(`Final review: no-action finding recorded (not routed to a writer): [${f.finding_id}] ${f.severity} ${f.domain} — ${f.title} (${f.requires_action === false ? 'requires_action:false' : 'no-action-inferred'})`)
297
317
  const summary = {
298
318
  total: raw.length,
299
319
  verified: classified.filter((f) => f.classification === 'VERIFIED').length,
320
+ noAction: noActionFindings.length,
300
321
  falsePositive: classified.filter((f) => f.classification === 'FALSE_POSITIVE').length,
301
322
  needsManual: classified.filter((f) => f.classification === 'NEEDS_MANUAL_CONFIRMATION').length,
302
323
  blockers: classified.filter((f) => f.classification === 'VERIFIED' && f.severity === 'BLOCKER').length,
303
324
  highs: classified.filter((f) => f.classification === 'VERIFIED' && f.severity === 'HIGH').length,
304
325
  failingGates: gateTable.filter((g) => g.status === 'FAIL').map((g) => g.gate),
305
326
  }
306
- log(`Final review done: ${summary.verified} verified (${summary.blockers} BLOCKER / ${summary.highs} HIGH), ${summary.needsManual} needs-manual, ${summary.failingGates.length} failing gate(s). Engine: ${codexEngine}.`)
327
+ log(`Final review done: ${summary.verified} verified (${summary.blockers} BLOCKER / ${summary.highs} HIGH), ${summary.noAction} no-action (recorded), ${summary.needsManual} needs-manual, ${summary.failingGates.length} failing gate(s). Engine: ${codexEngine}.`)
307
328
 
308
- return { codexEngine, findings, gateTable, summary }
329
+ return { codexEngine, findings, noActionFindings, gateTable, summary }
309
330
 
310
331
  function emptySummary() {
311
332
  return { total: 0, verified: 0, falsePositive: 0, needsManual: 0, blockers: 0, highs: 0, failingGates: [] }
@@ -268,7 +268,7 @@ try {
268
268
  `ROLE BOUNDARY (specialization integrity): you are the OPS/GIT agent. You NEVER edit source or doc files — any needed content change belongs to the coder specialist; report it instead.\n\n` +
269
269
  `DETERMINISTIC GATE POLICIES (NO user prompts):\n` +
270
270
  `• G1 dirty-tree (main repo ${MAIN}): partition framework-managed noise exactly as setup.md step 3 ($METRICS=${METRICS}, .baldart/generated|state.json|skill-conflicts.json — NOT overlays/). Genuine user work → auto-stash 'baldart-new2-${firstCard}' (main checkout) and record the label. Never commit/abort/prompt.\n` +
271
- `• Worktree (setup.md step 4): create ONE code worktree off ${TRUNK}; install deps; assign a port; run the baseline (tsc+lint+build). Copy the gitignored env artifacts in stack.env_files (resolved: ${JSON.stringify(ENV_FILES)}) FILES via \`cp\` (plain cp, dereferences symlinks; never cp -P), DIRS via \`cp -r\`; a missing FILE WARN (never abort). Do NOT add .env.example (tracked already in the checkout) and do NOT bulk-copy untracked files from the main repo (avoids stray backlog cards in the worktree). Use the git-authoritative idempotency pre-check. E2: baseline FAILS do NOT fix it yourself (role boundary — the coder specialist repairs it); return baseline:'fail' + a baselineLog precise enough for a coder to act (failing command, error excerpt, suspect files). Wrap the build in \`timeout 600 <build-cmd>\` (10 min); if killed, return baseline:'timeout' + the partial log. On baseline PASS, VERIFY the worktree on disk and return EVIDENCE (not just a flag): set worktreeVerified:true ONLY after running \`git -C ${MAIN} worktree list --porcelain\` (the worktree path MUST appear in the output) AND \`test -d <wt>/node_modules\` AND confirming the branch; put the LITERAL stdout into worktreeEvidence{ worktreeListPorcelain, artifactsLs:\`ls -la <wt>/node_modules <wt>/.next 2>/dev/null | head\`, baselineLogTail }. The workflow string-matches this evidence NEVER report worktreeVerified:true without actually running the commands.\n` +
271
+ `• Worktree (setup.md step 4): the ENTIRE build sequence (worktree add install env copy port registry baseline) is the SSOT script \`scripts/setup-worktree.sh\` the SAME script /nw and classic /new run. Do NOT hand-roll the bash (a re-authored copy is the duplication CLAUDE.md forbids, and the script cannot fabricate a baseline or stall). Resolve the branch from the lead/parent card's git_strategy.branch (fallback feat/<PARENT-ID|lead>-<slug>) + a kebab slug, then derive the git-authoritative idempotency pre-check (existing worktree for the branch reset the half-built orphan before running, never re-create into a fail-loud collision). Resolve the script: \`SETUP_SH="$(ls .framework/framework/.claude/skills/worktree-manager/scripts/setup-worktree.sh .claude/skills/worktree-manager/scripts/setup-worktree.sh 2>/dev/null | head -1)"\`. Run it (foregroundyou ARE the background context here): \`bash "$SETUP_SH" --main ${MAIN} --trunk ${TRUNK} --branch <branch> --slug <slug> --cards "${cardIds.join(',')}" --group-parent "<PARENT|>" --env-files "${ENV_FILES.join(',')}" --manifest /tmp/new2-wt-${firstCard}.txt\`. It installs non-interactively, copies stack.env_files (FILES plain cp / DIRS cp -R / missing FILE → WARN, never abort; never .env.example, never bulk-copy untracked), allocates a locked port, writes the registry, and runs the baseline under timeout 600 (tsc/lint report-but-continue, build STOP-on-fail). Read the manifest block (status/baseline/worktree_path/port/created_at/baseline_log): map baseline → your baseline field (pass|fail|timeout) and baseline_log baselineLog. E2: a \`baseline:fail\`/\`status:error\` build break is NOT yours to fix (role boundary — the coder repairs it); just return it precisely. Then, INDEPENDENTLY of the manifest, VERIFY the worktree on disk and return EVIDENCE (the workflow E2.5 gate string-matches it, never the script's word): set worktreeVerified:true ONLY after running \`git -C ${MAIN} worktree list --porcelain\` (the worktree path MUST appear) AND \`test -d <wt>/node_modules\` AND confirming the branch; put the LITERAL stdout into worktreeEvidence{ worktreeListPorcelain, artifactsLs:\`ls -la <wt>/node_modules <wt>/.next 2>/dev/null | head\`, baselineLogTail }. If the script is ABSENT (older subtree), fall back to running the sequence inline per setup.md step 4 prose. NEVER report worktreeVerified:true without actually running the verify commands.\n` +
272
272
  codexResolveBullet +
273
273
  g3Bullet +
274
274
  `• G4 card-field validation (setup.md 1b/1c): card missing requirements/acceptance_criteria/files_likely_touched → EXCLUDE (excluded[] + reason). Never HALT for one bad card.\n` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "4.52.4",
3
+ "version": "4.53.1",
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"