baldart 5.4.1 → 5.6.0

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/VERSION +1 -1
  3. package/framework/.claude/agents/CHANGELOG.md +16 -0
  4. package/framework/.claude/agents/prd-card-writer.md +37 -3
  5. package/framework/.claude/skills/new/CHANGELOG.md +33 -0
  6. package/framework/.claude/skills/new/SKILL.md +11 -5
  7. package/framework/.claude/skills/new/references/commit.md +12 -8
  8. package/framework/.claude/skills/new/references/completeness.md +10 -0
  9. package/framework/.claude/skills/new/references/final-review.md +23 -0
  10. package/framework/.claude/skills/new/references/implement.md +14 -4
  11. package/framework/.claude/skills/new/references/merge-cleanup.md +35 -14
  12. package/framework/.claude/skills/new/references/setup.md +1 -1
  13. package/framework/.claude/skills/new2/CHANGELOG.md +50 -0
  14. package/framework/.claude/skills/new2/SKILL.md +101 -17
  15. package/framework/.claude/skills/prd/CHANGELOG.md +36 -0
  16. package/framework/.claude/skills/prd/SKILL.md +17 -5
  17. package/framework/.claude/skills/prd/assets/state-template.md +8 -1
  18. package/framework/.claude/skills/prd/references/audit-phase.md +13 -1
  19. package/framework/.claude/skills/prd/references/backlog-phase.md +20 -0
  20. package/framework/.claude/skills/prd/references/discovery-phase.md +35 -0
  21. package/framework/.claude/skills/prd/references/ui-design-phase.md +28 -0
  22. package/framework/.claude/skills/prd/references/validation-phase.md +45 -1
  23. package/framework/.claude/skills/worktree-manager/CHANGELOG.md +13 -0
  24. package/framework/.claude/skills/worktree-manager/SKILL.md +11 -1
  25. package/framework/.claude/skills/worktree-manager/scripts/merge-worktree.sh +82 -6
  26. package/framework/.claude/workflows/new-final-review.js +9 -7
  27. package/framework/.claude/workflows/new2-resolve.js +29 -1
  28. package/framework/.claude/workflows/new2.js +153 -17
  29. package/framework/agents/card-schema.md +18 -1
  30. package/framework/agents/research-protocol.md +6 -1
  31. package/framework/scripts/stamp-holistic-audit.js +53 -10
  32. package/package.json +1 -1
@@ -22,7 +22,17 @@
22
22
  * Usage:
23
23
  * node framework/scripts/stamp-holistic-audit.js \
24
24
  * --at "2026-07-02T10:00:00Z" --commit "<sha-or-empty>" \
25
- * --set "FEAT-0070-01,FEAT-0070-02" <card1.yml> [<card2.yml> ...]
25
+ * --set "FEAT-0070-01,FEAT-0070-02" \
26
+ * [--audit-report /tmp/prd-audit-<slug>.md] <card1.yml> [<card2.yml> ...]
27
+ *
28
+ * EVIDENCE GATE (v5.6.0 — the FEAT-0068 phantom stamp): the stamp is only worth
29
+ * anything if the audit team actually ran. `--audit-report <path>` names the
30
+ * audit-phase report artifact; the script reads its `agents_run:` line (falling
31
+ * back to counting `agent:`/`###` sections) and writes `agents_run: [...]` +
32
+ * `findings_count` into the stamp. WITHOUT the flag — or when the file does not
33
+ * exist / names no agents — the script writes `status: "skipped"` and NO
34
+ * `agents_run`, which `/new` (implement.md Phase 1 step 4 P1) treats as ABSENT:
35
+ * the plan-auditor runs. A stamp can no longer claim an audit that never happened.
26
36
  *
27
37
  * Exit 0 = every non-epic card carries the stamp afterwards; exit 1 = a write or
28
38
  * parse failed (per-file errors printed). Zero-dep, node-core only.
@@ -61,19 +71,49 @@ function stripExistingBlock(text) {
61
71
  return lines.slice(0, start).concat(lines.slice(end)).join('\n');
62
72
  }
63
73
 
64
- function buildBlock(at, commit, set, childIndent) {
74
+ /**
75
+ * v5.6.0 — extract audit evidence from the audit-phase report artifact.
76
+ * Returns { agentsRun: string[], findingsCount: number|null } or null when the
77
+ * report is absent/unreadable/empty of agents (→ the stamp degrades to skipped).
78
+ */
79
+ function readAuditEvidence(reportPath) {
80
+ if (!reportPath) return null;
81
+ let text;
82
+ try { text = fs.readFileSync(reportPath, 'utf8'); } catch (_) { return null; }
83
+ // primary: an explicit `agents_run: a, b, c` (or YAML list) line in the report
84
+ const m = text.match(/^agents_run:\s*\[?([^\]\n]+)\]?/m);
85
+ let agents = m ? m[1].split(',').map((s) => s.trim().replace(/^["']|["']$/g, '')).filter(Boolean) : [];
86
+ if (!agents.length) {
87
+ // fallback: agent names cited as report sections/spawn lines
88
+ const KNOWN = ['plan-auditor', 'codebase-architect', 'security-reviewer', 'api-perf-cost-auditor', 'qa-sentinel', 'code-reviewer', 'doc-reviewer'];
89
+ agents = KNOWN.filter((a) => new RegExp(`\\b${a}\\b`).test(text));
90
+ }
91
+ if (!agents.length) return null;
92
+ const fc = text.match(/^findings(?:_count)?:\s*(\d+)/m);
93
+ return { agentsRun: Array.from(new Set(agents)), findingsCount: fc ? Number(fc[1]) : null };
94
+ }
95
+
96
+ function buildBlock(at, commit, set, childIndent, evidence) {
65
97
  const pad = ' '.repeat(childIndent);
66
98
  const inner = ' '.repeat(childIndent + 2);
67
99
  const ids = set.map((s) => `"${s}"`).join(', ');
68
- return [
100
+ const lines = [
69
101
  `${pad}holistic_audit:`,
70
102
  `${inner}audited_at: "${at}"`,
71
103
  `${inner}audited_commit: "${commit}"`,
72
104
  `${inner}audited_set: [${ids}]`,
73
- ].join('\n');
105
+ ];
106
+ if (evidence) {
107
+ lines.push(`${inner}agents_run: [${evidence.agentsRun.map((a) => `"${a}"`).join(', ')}]`);
108
+ if (evidence.findingsCount !== null) lines.push(`${inner}findings_count: ${evidence.findingsCount}`);
109
+ } else {
110
+ // no audit artifact → the stamp is explicit about it; /new P1 treats this as ABSENT
111
+ lines.push(`${inner}status: "skipped"`);
112
+ }
113
+ return lines.join('\n');
74
114
  }
75
115
 
76
- function stampFile(file, at, commit, set) {
116
+ function stampFile(file, at, commit, set, evidence) {
77
117
  let text = fs.readFileSync(file, 'utf8');
78
118
  let card;
79
119
  try { card = parseCardYaml(text); } catch (e) { return { file, status: 'error', detail: `YAML parse error — ${e.message}` }; }
@@ -94,11 +134,11 @@ function stampFile(file, at, commit, set) {
94
134
  if (li > 0) childIndent = li;
95
135
  break;
96
136
  }
97
- lines.splice(metaIdx + 1, 0, buildBlock(at, commit, set, childIndent));
137
+ lines.splice(metaIdx + 1, 0, buildBlock(at, commit, set, childIndent, evidence));
98
138
  text = lines.join('\n');
99
139
  } else {
100
140
  if (!text.endsWith('\n')) text += '\n';
101
- text += `\nmetadata:\n${buildBlock(at, commit, set, 2)}\n`;
141
+ text += `\nmetadata:\n${buildBlock(at, commit, set, 2, evidence)}\n`;
102
142
  }
103
143
  fs.writeFileSync(file, text);
104
144
  // verify the write landed (the 5b "re-read one card" contract, made universal)
@@ -113,20 +153,23 @@ function main(argv) {
113
153
  const at = argValue(args, '--at');
114
154
  const commit = argValue(args, '--commit'); // may legitimately be "" — must still be PASSED
115
155
  const setRaw = argValue(args, '--set');
156
+ const auditReport = argValue(args, '--audit-report'); // v5.6.0 evidence gate (optional flag, binding semantics)
116
157
  const files = [];
117
158
  for (let i = 0; i < args.length; i++) {
118
- if (args[i] === '--at' || args[i] === '--commit' || args[i] === '--set') { i++; continue; }
159
+ if (args[i] === '--at' || args[i] === '--commit' || args[i] === '--set' || args[i] === '--audit-report') { i++; continue; }
119
160
  files.push(args[i]);
120
161
  }
121
162
  if (!at || commit === null || setRaw === null || !files.length) {
122
- process.stderr.write('Usage: stamp-holistic-audit.js --at <iso8601> --commit <sha-or-empty> --set "ID1,ID2" <card.yml> [...]\n');
163
+ process.stderr.write('Usage: stamp-holistic-audit.js --at <iso8601> --commit <sha-or-empty> --set "ID1,ID2" [--audit-report <path>] <card.yml> [...]\n');
123
164
  return 2;
124
165
  }
125
166
  const set = setRaw.split(',').map((s) => s.trim()).filter(Boolean).sort();
167
+ const evidence = readAuditEvidence(auditReport);
168
+ if (!evidence) process.stdout.write(`⚠ no audit evidence (${auditReport ? 'report unreadable/agent-less: ' + auditReport : '--audit-report not passed'}) → stamping status:"skipped" (no agents_run; /new will RUN the plan-auditor)\n`);
126
169
  let failed = 0, stamped = 0, already = 0, epics = 0;
127
170
  for (const f of files) {
128
171
  let r;
129
- try { r = stampFile(f, at, commit, set); } catch (e) { r = { file: f, status: 'error', detail: e.message }; }
172
+ try { r = stampFile(f, at, commit, set, evidence); } catch (e) { r = { file: f, status: 'error', detail: e.message }; }
130
173
  if (r.status === 'error') { failed++; process.stdout.write(`✖ ${f}: ${r.detail}\n`); }
131
174
  else if (r.status === 'stamped') { stamped++; process.stdout.write(`✓ ${f}: stamped\n`); }
132
175
  else if (r.status === 'already-stamped') { already++; process.stdout.write(`· ${f}: already stamped\n`); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "5.4.1",
3
+ "version": "5.6.0",
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"