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.
- package/CHANGELOG.md +86 -0
- package/VERSION +1 -1
- package/framework/.claude/agents/CHANGELOG.md +16 -0
- package/framework/.claude/agents/prd-card-writer.md +37 -3
- package/framework/.claude/skills/new/CHANGELOG.md +33 -0
- package/framework/.claude/skills/new/SKILL.md +11 -5
- package/framework/.claude/skills/new/references/commit.md +12 -8
- package/framework/.claude/skills/new/references/completeness.md +10 -0
- package/framework/.claude/skills/new/references/final-review.md +23 -0
- package/framework/.claude/skills/new/references/implement.md +14 -4
- package/framework/.claude/skills/new/references/merge-cleanup.md +35 -14
- package/framework/.claude/skills/new/references/setup.md +1 -1
- package/framework/.claude/skills/new2/CHANGELOG.md +50 -0
- package/framework/.claude/skills/new2/SKILL.md +101 -17
- package/framework/.claude/skills/prd/CHANGELOG.md +36 -0
- package/framework/.claude/skills/prd/SKILL.md +17 -5
- package/framework/.claude/skills/prd/assets/state-template.md +8 -1
- package/framework/.claude/skills/prd/references/audit-phase.md +13 -1
- package/framework/.claude/skills/prd/references/backlog-phase.md +20 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +35 -0
- package/framework/.claude/skills/prd/references/ui-design-phase.md +28 -0
- package/framework/.claude/skills/prd/references/validation-phase.md +45 -1
- package/framework/.claude/skills/worktree-manager/CHANGELOG.md +13 -0
- package/framework/.claude/skills/worktree-manager/SKILL.md +11 -1
- package/framework/.claude/skills/worktree-manager/scripts/merge-worktree.sh +82 -6
- package/framework/.claude/workflows/new-final-review.js +9 -7
- package/framework/.claude/workflows/new2-resolve.js +29 -1
- package/framework/.claude/workflows/new2.js +153 -17
- package/framework/agents/card-schema.md +18 -1
- package/framework/agents/research-protocol.md +6 -1
- package/framework/scripts/stamp-holistic-audit.js +53 -10
- 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"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
]
|
|
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`); }
|