@boyingliu01/xp-gate 0.27.1 → 0.27.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/adapter-common.sh +0 -0
- package/adapters/cpp.sh +0 -0
- package/adapters/dart.sh +0 -0
- package/adapters/flutter.sh +0 -0
- package/adapters/gate-7.sh +22 -4
- package/adapters/go.sh +0 -0
- package/adapters/iac.sh +0 -0
- package/adapters/java.sh +68 -6
- package/adapters/kotlin.sh +0 -0
- package/adapters/objectivec.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
- package/adapters/powershell.sh +0 -0
- package/adapters/python.sh +0 -0
- package/adapters/shell.sh +0 -0
- package/adapters/swift.sh +0 -0
- package/adapters/typescript.sh +0 -0
- package/bin/xp-gate.js +0 -0
- package/gate-10.sh +0 -0
- package/gate-3.sh +0 -0
- package/gate-4.sh +0 -0
- package/gate-7.sh +22 -4
- package/gate-8.sh +0 -0
- package/gate-9.sh +0 -0
- package/hooks/adapter-common.sh +0 -0
- package/hooks/gate-10.sh +0 -0
- package/hooks/gate-3.sh +0 -0
- package/hooks/gate-4.sh +0 -0
- package/hooks/gate-7.sh +22 -4
- package/hooks/gate-8.sh +0 -0
- package/hooks/gate-9.sh +0 -0
- package/hooks/post-merge +0 -0
- package/hooks/pre-commit +0 -0
- package/hooks/pre-push +0 -0
- package/hooks/sprint-gate.sh +0 -0
- package/lib/__tests__/doctor-delphi.test.js +244 -244
- package/lib/__tests__/sprint-audit.test.js +207 -207
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/sprint-audit.js +235 -235
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
- package/plugins/claude-code/bin/xp-gate-check +0 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/grill-with-docs/SKILL.md +1 -2
- package/plugins/claude-code/skills/grilling/SKILL.md +22 -5
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/lib/command.d.ts +32 -0
- package/plugins/dsh/lib/command.d.ts.map +1 -0
- package/plugins/dsh/lib/command.js +81 -0
- package/plugins/dsh/lib/delphi-config.d.ts +75 -0
- package/plugins/dsh/lib/delphi-config.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-config.js +183 -0
- package/plugins/dsh/lib/delphi-run.d.ts +24 -0
- package/plugins/dsh/lib/delphi-run.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi-run.js +92 -0
- package/plugins/dsh/lib/delphi.d.ts +88 -0
- package/plugins/dsh/lib/delphi.d.ts.map +1 -0
- package/plugins/dsh/lib/delphi.js +161 -0
- package/plugins/dsh/lib/dialect.d.ts +24 -0
- package/plugins/dsh/lib/dialect.d.ts.map +1 -0
- package/plugins/dsh/lib/dialect.js +36 -0
- package/plugins/dsh/lib/gate-runner.d.ts +18 -0
- package/plugins/dsh/lib/gate-runner.d.ts.map +1 -0
- package/plugins/dsh/lib/gate-runner.js +46 -0
- package/plugins/dsh/lib/index.d.ts +5 -0
- package/plugins/dsh/lib/index.d.ts.map +1 -0
- package/plugins/dsh/lib/index.js +162 -0
- package/plugins/dsh/node_modules/.vite/vitest/results.json +1 -0
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +1 -2
- package/plugins/dsh/skills/grilling/SKILL.md +22 -5
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/src/delphi-config-global-fallback.test.ts +87 -0
- package/plugins/dsh/src/delphi-config.ts +67 -28
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/grill-with-docs/SKILL.md +1 -2
- package/plugins/opencode/skills/grilling/SKILL.md +22 -5
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
- package/plugins/qoder/bin/xp-gate-check +0 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/scripts/delphi-external-review.cjs +26 -0
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/grill-with-docs/SKILL.md +1 -2
- package/skills/grilling/SKILL.md +22 -5
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
- package/sprint-gate.sh +0 -0
package/lib/sprint-audit.js
CHANGED
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* xp-gate sprint-audit — Layer 2: Final sprint completeness audit.
|
|
3
|
-
*
|
|
4
|
-
* Checks phase_history coverage, time records, artifact records,
|
|
5
|
-
* and state consistency at Phase 6 CLOSE.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* xp-gate sprint-audit [--dir <path>] [--json]
|
|
9
|
-
*
|
|
10
|
-
* Verdict rules:
|
|
11
|
-
* 6/6 completed + 0 errors → PASS (exit 0)
|
|
12
|
-
* >= 4/6 completed + 0 errors → PASS_WITH_WARNINGS (exit 0)
|
|
13
|
-
* < 4/6 completed or any errors → FAIL (exit 1)
|
|
14
|
-
* No sprint-state.json → SKIP (exit 0)
|
|
15
|
-
*
|
|
16
|
-
* @module sprint-audit
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
'use strict';
|
|
20
|
-
|
|
21
|
-
const fs = require('fs');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const { PHASE_NAMES } = require('./phase-transition');
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Run the sprint audit.
|
|
27
|
-
* @param {string[]} args - CLI arguments
|
|
28
|
-
* @returns {Promise<number>} Exit code
|
|
29
|
-
*/
|
|
30
|
-
async function handleSprintAudit(args = []) {
|
|
31
|
-
if (args.includes('--help')) {
|
|
32
|
-
console.log('Usage: xp-gate sprint-audit [options]');
|
|
33
|
-
console.log('');
|
|
34
|
-
console.log('Options:');
|
|
35
|
-
console.log(' --dir <path> Project directory (default: cwd)');
|
|
36
|
-
console.log(' --json Output as JSON instead of human-readable');
|
|
37
|
-
console.log('');
|
|
38
|
-
console.log('Runs final sprint completeness audit at Phase 6 CLOSE.');
|
|
39
|
-
console.log('Checks phase coverage, time records, artifact records,');
|
|
40
|
-
console.log('and state consistency.');
|
|
41
|
-
return 0;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const dirIdx = args.indexOf('--dir');
|
|
45
|
-
const projectDir = dirIdx >= 0 ? args[dirIdx + 1] : process.cwd();
|
|
46
|
-
const jsonFlag = args.includes('--json');
|
|
47
|
-
|
|
48
|
-
const stateFile = path.join(projectDir, '.sprint-state', 'sprint-state.json');
|
|
49
|
-
|
|
50
|
-
// No sprint-state.json → SKIP
|
|
51
|
-
if (!fs.existsSync(stateFile)) {
|
|
52
|
-
if (jsonFlag) {
|
|
53
|
-
console.log(JSON.stringify({ verdict: 'SKIP', reason: 'No sprint-state.json found' }));
|
|
54
|
-
} else {
|
|
55
|
-
console.log('⏭️ No sprint-state.json found — skipping audit.');
|
|
56
|
-
}
|
|
57
|
-
return 0;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let state;
|
|
61
|
-
try {
|
|
62
|
-
state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
63
|
-
} catch (err) {
|
|
64
|
-
const result = {
|
|
65
|
-
verdict: 'FAIL',
|
|
66
|
-
errors: [`Failed to parse sprint-state.json: ${err.message}`],
|
|
67
|
-
};
|
|
68
|
-
if (jsonFlag) {
|
|
69
|
-
console.log(JSON.stringify(result, null, 2));
|
|
70
|
-
} else {
|
|
71
|
-
console.error(`❌ ${result.errors[0]}`);
|
|
72
|
-
}
|
|
73
|
-
return 1;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ── Run checks ──
|
|
77
|
-
const warnings = [];
|
|
78
|
-
const errors = [];
|
|
79
|
-
const phaseHistory = Array.isArray(state.phase_history) ? state.phase_history : [];
|
|
80
|
-
|
|
81
|
-
// Build lookup
|
|
82
|
-
const historyLookup = {};
|
|
83
|
-
for (const entry of phaseHistory) {
|
|
84
|
-
historyLookup[entry.phase] = entry;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Check 1: Phase coverage
|
|
88
|
-
const completedPhases = [];
|
|
89
|
-
const missingPhases = [];
|
|
90
|
-
const incompletePhases = [];
|
|
91
|
-
|
|
92
|
-
for (let i = 1; i <= 6; i++) {
|
|
93
|
-
const entry = historyLookup[i];
|
|
94
|
-
if (!entry) {
|
|
95
|
-
missingPhases.push(i);
|
|
96
|
-
} else if (entry.status === 'completed' || entry.status === 'skipped') {
|
|
97
|
-
completedPhases.push(i);
|
|
98
|
-
} else {
|
|
99
|
-
incompletePhases.push({ phase: i, name: PHASE_NAMES[i] || `Phase ${i}`, status: entry.status });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Check 2: Time records for completed phases
|
|
104
|
-
for (const phaseNum of completedPhases) {
|
|
105
|
-
const entry = historyLookup[phaseNum];
|
|
106
|
-
if (entry.status === 'completed' && (entry.duration_seconds == null || entry.duration_seconds === 0)) {
|
|
107
|
-
warnings.push(`Phase ${phaseNum} (${PHASE_NAMES[phaseNum]}) completed but has no duration recorded`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Check 3: Artifact records (phase-{N}-summary.md)
|
|
112
|
-
const outputsDir = path.join(projectDir, '.sprint-state', 'phase-outputs');
|
|
113
|
-
for (const phaseNum of completedPhases) {
|
|
114
|
-
if (historyLookup[phaseNum].status === 'completed') {
|
|
115
|
-
const summaryFile = path.join(outputsDir, `phase-${phaseNum}-summary.md`);
|
|
116
|
-
if (!fs.existsSync(summaryFile)) {
|
|
117
|
-
warnings.push(`Phase ${phaseNum} summary missing: .sprint-state/phase-outputs/phase-${phaseNum}-summary.md`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Check 4: State consistency — phase field vs phase_history last entry
|
|
123
|
-
if (phaseHistory.length > 0) {
|
|
124
|
-
const lastEntry = phaseHistory[phaseHistory.length - 1];
|
|
125
|
-
if (state.phase !== undefined && state.phase !== lastEntry.phase) {
|
|
126
|
-
errors.push(
|
|
127
|
-
`State inconsistency: sprint-state.phase=${state.phase} but phase_history last entry is phase ${lastEntry.phase}`
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ── Determine verdict ──
|
|
133
|
-
const completedCount = completedPhases.length;
|
|
134
|
-
let verdict;
|
|
135
|
-
if (errors.length > 0 || completedCount < 4) {
|
|
136
|
-
verdict = 'FAIL';
|
|
137
|
-
} else if (completedCount >= 6 && errors.length === 0) {
|
|
138
|
-
verdict = 'PASS';
|
|
139
|
-
} else {
|
|
140
|
-
verdict = 'PASS_WITH_WARNINGS';
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ── Build result ──
|
|
144
|
-
const result = {
|
|
145
|
-
sprint_id: state.id || 'unknown',
|
|
146
|
-
branch: state.isolation?.branch || 'unknown',
|
|
147
|
-
coverage: {
|
|
148
|
-
completed: completedCount,
|
|
149
|
-
total: 6,
|
|
150
|
-
pct: Math.round((completedCount / 6) * 100),
|
|
151
|
-
},
|
|
152
|
-
missing_phases: missingPhases.map(p => ({ phase: p, name: PHASE_NAMES[p] || `Phase ${p}` })),
|
|
153
|
-
incomplete_phases: incompletePhases,
|
|
154
|
-
warnings,
|
|
155
|
-
errors,
|
|
156
|
-
verdict,
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// ── Persist report (latest overwrites — expected behavior) ──
|
|
160
|
-
const reportPath = path.join(projectDir, '.sprint-state', 'audit-report.json');
|
|
161
|
-
try {
|
|
162
|
-
fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), 'utf8');
|
|
163
|
-
} catch (err) {
|
|
164
|
-
// Report write failure is WARNING, not BLOCK
|
|
165
|
-
warnings.push(`Failed to write audit report: ${err.message}`);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// ── Output ──
|
|
169
|
-
if (jsonFlag) {
|
|
170
|
-
console.log(JSON.stringify(result, null, 2));
|
|
171
|
-
} else {
|
|
172
|
-
printHumanReadable(result, state);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Exit code: FAIL → 1, others → 0
|
|
176
|
-
return verdict === 'FAIL' ? 1 : 0;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Print human-readable audit report.
|
|
181
|
-
* @param {object} result - Audit result object
|
|
182
|
-
* @param {object} state - Sprint state object
|
|
183
|
-
*/
|
|
184
|
-
function printHumanReadable(result, state) {
|
|
185
|
-
const lines = [];
|
|
186
|
-
lines.push('');
|
|
187
|
-
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
188
|
-
lines.push(` Sprint Audit Report — ${result.sprint_id}`);
|
|
189
|
-
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
190
|
-
lines.push('');
|
|
191
|
-
lines.push(` Branch: ${result.branch}`);
|
|
192
|
-
lines.push(` Phase Coverage: ${result.coverage.completed}/${result.coverage.total} (${result.coverage.pct}%)`);
|
|
193
|
-
lines.push('');
|
|
194
|
-
lines.push(' Phase History:');
|
|
195
|
-
|
|
196
|
-
for (let i = 1; i <= 6; i++) {
|
|
197
|
-
const entry = result.incomplete_phases.find(p => p.phase === i);
|
|
198
|
-
const missing = result.missing_phases.find(p => p.phase === i);
|
|
199
|
-
const name = (PHASE_NAMES[i] || `Phase ${i}`).padEnd(10);
|
|
200
|
-
|
|
201
|
-
if (missing) {
|
|
202
|
-
lines.push(` ❌ Phase ${i}/6 ${name} — not recorded`);
|
|
203
|
-
} else if (entry) {
|
|
204
|
-
lines.push(` ⚠️ Phase ${i}/6 ${name} ${entry.status} ← not yet completed`);
|
|
205
|
-
} else {
|
|
206
|
-
lines.push(` ✅ Phase ${i}/6 ${name} completed`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (result.warnings.length > 0) {
|
|
211
|
-
lines.push('');
|
|
212
|
-
lines.push(' Warnings:');
|
|
213
|
-
for (const w of result.warnings) {
|
|
214
|
-
lines.push(` ⚠️ ${w}`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (result.errors.length > 0) {
|
|
219
|
-
lines.push('');
|
|
220
|
-
lines.push(' Errors:');
|
|
221
|
-
for (const e of result.errors) {
|
|
222
|
-
lines.push(` ❌ ${e}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
lines.push('');
|
|
227
|
-
const issueCount = result.warnings.length + result.errors.length;
|
|
228
|
-
lines.push(` Overall: ${result.warnings.length} warning(s), ${result.errors.length} error(s)`);
|
|
229
|
-
lines.push(` Verdict: ${result.verdict}`);
|
|
230
|
-
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
231
|
-
|
|
232
|
-
console.log(lines.join('\n'));
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
module.exports = { handleSprintAudit };
|
|
1
|
+
/**
|
|
2
|
+
* xp-gate sprint-audit — Layer 2: Final sprint completeness audit.
|
|
3
|
+
*
|
|
4
|
+
* Checks phase_history coverage, time records, artifact records,
|
|
5
|
+
* and state consistency at Phase 6 CLOSE.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* xp-gate sprint-audit [--dir <path>] [--json]
|
|
9
|
+
*
|
|
10
|
+
* Verdict rules:
|
|
11
|
+
* 6/6 completed + 0 errors → PASS (exit 0)
|
|
12
|
+
* >= 4/6 completed + 0 errors → PASS_WITH_WARNINGS (exit 0)
|
|
13
|
+
* < 4/6 completed or any errors → FAIL (exit 1)
|
|
14
|
+
* No sprint-state.json → SKIP (exit 0)
|
|
15
|
+
*
|
|
16
|
+
* @module sprint-audit
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { PHASE_NAMES } = require('./phase-transition');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Run the sprint audit.
|
|
27
|
+
* @param {string[]} args - CLI arguments
|
|
28
|
+
* @returns {Promise<number>} Exit code
|
|
29
|
+
*/
|
|
30
|
+
async function handleSprintAudit(args = []) {
|
|
31
|
+
if (args.includes('--help')) {
|
|
32
|
+
console.log('Usage: xp-gate sprint-audit [options]');
|
|
33
|
+
console.log('');
|
|
34
|
+
console.log('Options:');
|
|
35
|
+
console.log(' --dir <path> Project directory (default: cwd)');
|
|
36
|
+
console.log(' --json Output as JSON instead of human-readable');
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log('Runs final sprint completeness audit at Phase 6 CLOSE.');
|
|
39
|
+
console.log('Checks phase coverage, time records, artifact records,');
|
|
40
|
+
console.log('and state consistency.');
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const dirIdx = args.indexOf('--dir');
|
|
45
|
+
const projectDir = dirIdx >= 0 ? args[dirIdx + 1] : process.cwd();
|
|
46
|
+
const jsonFlag = args.includes('--json');
|
|
47
|
+
|
|
48
|
+
const stateFile = path.join(projectDir, '.sprint-state', 'sprint-state.json');
|
|
49
|
+
|
|
50
|
+
// No sprint-state.json → SKIP
|
|
51
|
+
if (!fs.existsSync(stateFile)) {
|
|
52
|
+
if (jsonFlag) {
|
|
53
|
+
console.log(JSON.stringify({ verdict: 'SKIP', reason: 'No sprint-state.json found' }));
|
|
54
|
+
} else {
|
|
55
|
+
console.log('⏭️ No sprint-state.json found — skipping audit.');
|
|
56
|
+
}
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let state;
|
|
61
|
+
try {
|
|
62
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
63
|
+
} catch (err) {
|
|
64
|
+
const result = {
|
|
65
|
+
verdict: 'FAIL',
|
|
66
|
+
errors: [`Failed to parse sprint-state.json: ${err.message}`],
|
|
67
|
+
};
|
|
68
|
+
if (jsonFlag) {
|
|
69
|
+
console.log(JSON.stringify(result, null, 2));
|
|
70
|
+
} else {
|
|
71
|
+
console.error(`❌ ${result.errors[0]}`);
|
|
72
|
+
}
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── Run checks ──
|
|
77
|
+
const warnings = [];
|
|
78
|
+
const errors = [];
|
|
79
|
+
const phaseHistory = Array.isArray(state.phase_history) ? state.phase_history : [];
|
|
80
|
+
|
|
81
|
+
// Build lookup
|
|
82
|
+
const historyLookup = {};
|
|
83
|
+
for (const entry of phaseHistory) {
|
|
84
|
+
historyLookup[entry.phase] = entry;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check 1: Phase coverage
|
|
88
|
+
const completedPhases = [];
|
|
89
|
+
const missingPhases = [];
|
|
90
|
+
const incompletePhases = [];
|
|
91
|
+
|
|
92
|
+
for (let i = 1; i <= 6; i++) {
|
|
93
|
+
const entry = historyLookup[i];
|
|
94
|
+
if (!entry) {
|
|
95
|
+
missingPhases.push(i);
|
|
96
|
+
} else if (entry.status === 'completed' || entry.status === 'skipped') {
|
|
97
|
+
completedPhases.push(i);
|
|
98
|
+
} else {
|
|
99
|
+
incompletePhases.push({ phase: i, name: PHASE_NAMES[i] || `Phase ${i}`, status: entry.status });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check 2: Time records for completed phases
|
|
104
|
+
for (const phaseNum of completedPhases) {
|
|
105
|
+
const entry = historyLookup[phaseNum];
|
|
106
|
+
if (entry.status === 'completed' && (entry.duration_seconds == null || entry.duration_seconds === 0)) {
|
|
107
|
+
warnings.push(`Phase ${phaseNum} (${PHASE_NAMES[phaseNum]}) completed but has no duration recorded`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Check 3: Artifact records (phase-{N}-summary.md)
|
|
112
|
+
const outputsDir = path.join(projectDir, '.sprint-state', 'phase-outputs');
|
|
113
|
+
for (const phaseNum of completedPhases) {
|
|
114
|
+
if (historyLookup[phaseNum].status === 'completed') {
|
|
115
|
+
const summaryFile = path.join(outputsDir, `phase-${phaseNum}-summary.md`);
|
|
116
|
+
if (!fs.existsSync(summaryFile)) {
|
|
117
|
+
warnings.push(`Phase ${phaseNum} summary missing: .sprint-state/phase-outputs/phase-${phaseNum}-summary.md`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check 4: State consistency — phase field vs phase_history last entry
|
|
123
|
+
if (phaseHistory.length > 0) {
|
|
124
|
+
const lastEntry = phaseHistory[phaseHistory.length - 1];
|
|
125
|
+
if (state.phase !== undefined && state.phase !== lastEntry.phase) {
|
|
126
|
+
errors.push(
|
|
127
|
+
`State inconsistency: sprint-state.phase=${state.phase} but phase_history last entry is phase ${lastEntry.phase}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── Determine verdict ──
|
|
133
|
+
const completedCount = completedPhases.length;
|
|
134
|
+
let verdict;
|
|
135
|
+
if (errors.length > 0 || completedCount < 4) {
|
|
136
|
+
verdict = 'FAIL';
|
|
137
|
+
} else if (completedCount >= 6 && errors.length === 0) {
|
|
138
|
+
verdict = 'PASS';
|
|
139
|
+
} else {
|
|
140
|
+
verdict = 'PASS_WITH_WARNINGS';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── Build result ──
|
|
144
|
+
const result = {
|
|
145
|
+
sprint_id: state.id || 'unknown',
|
|
146
|
+
branch: state.isolation?.branch || 'unknown',
|
|
147
|
+
coverage: {
|
|
148
|
+
completed: completedCount,
|
|
149
|
+
total: 6,
|
|
150
|
+
pct: Math.round((completedCount / 6) * 100),
|
|
151
|
+
},
|
|
152
|
+
missing_phases: missingPhases.map(p => ({ phase: p, name: PHASE_NAMES[p] || `Phase ${p}` })),
|
|
153
|
+
incomplete_phases: incompletePhases,
|
|
154
|
+
warnings,
|
|
155
|
+
errors,
|
|
156
|
+
verdict,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// ── Persist report (latest overwrites — expected behavior) ──
|
|
160
|
+
const reportPath = path.join(projectDir, '.sprint-state', 'audit-report.json');
|
|
161
|
+
try {
|
|
162
|
+
fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), 'utf8');
|
|
163
|
+
} catch (err) {
|
|
164
|
+
// Report write failure is WARNING, not BLOCK
|
|
165
|
+
warnings.push(`Failed to write audit report: ${err.message}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── Output ──
|
|
169
|
+
if (jsonFlag) {
|
|
170
|
+
console.log(JSON.stringify(result, null, 2));
|
|
171
|
+
} else {
|
|
172
|
+
printHumanReadable(result, state);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Exit code: FAIL → 1, others → 0
|
|
176
|
+
return verdict === 'FAIL' ? 1 : 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Print human-readable audit report.
|
|
181
|
+
* @param {object} result - Audit result object
|
|
182
|
+
* @param {object} state - Sprint state object
|
|
183
|
+
*/
|
|
184
|
+
function printHumanReadable(result, state) {
|
|
185
|
+
const lines = [];
|
|
186
|
+
lines.push('');
|
|
187
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
188
|
+
lines.push(` Sprint Audit Report — ${result.sprint_id}`);
|
|
189
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
190
|
+
lines.push('');
|
|
191
|
+
lines.push(` Branch: ${result.branch}`);
|
|
192
|
+
lines.push(` Phase Coverage: ${result.coverage.completed}/${result.coverage.total} (${result.coverage.pct}%)`);
|
|
193
|
+
lines.push('');
|
|
194
|
+
lines.push(' Phase History:');
|
|
195
|
+
|
|
196
|
+
for (let i = 1; i <= 6; i++) {
|
|
197
|
+
const entry = result.incomplete_phases.find(p => p.phase === i);
|
|
198
|
+
const missing = result.missing_phases.find(p => p.phase === i);
|
|
199
|
+
const name = (PHASE_NAMES[i] || `Phase ${i}`).padEnd(10);
|
|
200
|
+
|
|
201
|
+
if (missing) {
|
|
202
|
+
lines.push(` ❌ Phase ${i}/6 ${name} — not recorded`);
|
|
203
|
+
} else if (entry) {
|
|
204
|
+
lines.push(` ⚠️ Phase ${i}/6 ${name} ${entry.status} ← not yet completed`);
|
|
205
|
+
} else {
|
|
206
|
+
lines.push(` ✅ Phase ${i}/6 ${name} completed`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (result.warnings.length > 0) {
|
|
211
|
+
lines.push('');
|
|
212
|
+
lines.push(' Warnings:');
|
|
213
|
+
for (const w of result.warnings) {
|
|
214
|
+
lines.push(` ⚠️ ${w}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (result.errors.length > 0) {
|
|
219
|
+
lines.push('');
|
|
220
|
+
lines.push(' Errors:');
|
|
221
|
+
for (const e of result.errors) {
|
|
222
|
+
lines.push(` ❌ ${e}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
lines.push('');
|
|
227
|
+
const issueCount = result.warnings.length + result.errors.length;
|
|
228
|
+
lines.push(` Overall: ${result.warnings.length} warning(s), ${result.errors.length} error(s)`);
|
|
229
|
+
lines.push(` Verdict: ${result.verdict}`);
|
|
230
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
231
|
+
|
|
232
|
+
console.log(lines.join('\n'));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = { handleSprintAudit };
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-17
|
|
4
|
+
**Commit:** b207e882
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.3.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Mock layering policy enforcement — Gate M3 of pre-push hook. Ensures integration tests use real implementations for internal dependencies, mock external dependencies, and annotate pending mocks with removal plans. Combines project scope scanning, mock decision engine, and per-file validation into a single pipeline.
|
package/mutation/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MUTATION KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-17
|
|
4
|
+
**Commit:** b207e882
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.3.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**Gate M** (incremental mutation testing) + **Gate M2** helpers (test-layer detection used by `src/mock-policy/`). Pre-push quality gate. TypeScript-only; uses Stryker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.3",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
File without changes
|
|
File without changes
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-17
|
|
4
|
+
**Commit:** b207e882
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.3.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: grill-with-docs
|
|
3
|
-
version: 1.0.0
|
|
4
3
|
description: A relentless interview to sharpen a plan or design, which also creates docs (ADR's and glossary) as we go.
|
|
5
4
|
disable-model-invocation: true
|
|
6
5
|
---
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Call the Skill tool twice, for "grilling" and "domain-modeling".
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: grilling
|
|
3
|
-
version: 1.0.0
|
|
4
3
|
description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Interview
|
|
6
|
+
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled: the questions you can ask _now_ without guessing at answers you haven't heard yet. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait for the user's answers before the next round.
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
Format a round like so:
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
```
|
|
13
|
+
❓ **Q1** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
|
|
14
|
+
|
|
15
|
+
➡️ <your recommended answer>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
❓ **Q2** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
|
|
20
|
+
|
|
21
|
+
➡️ <your recommended answer>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Word each question so "yes" accepts your recommended answer.
|
|
25
|
+
|
|
26
|
+
Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one.
|
|
27
|
+
|
|
28
|
+
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait.
|
|
29
|
+
|
|
30
|
+
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-17
|
|
4
|
+
**Commit:** b207e882
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.3.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-17
|
|
4
|
+
**Commit:** b207e882
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.27.
|
|
6
|
+
**Version:** 0.27.3.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ShellDialect } from "./dialect.js";
|
|
2
|
+
export { type ShellDialect, detectDialect, shellQuote, shq } from "./dialect.js";
|
|
3
|
+
/** Shown when the global `xp-gate` CLI is not on PATH. */
|
|
4
|
+
export declare const FALLBACK_MESSAGE = "[XP-Gate] xp-gate CLI not installed. Install it with: npm install -g @boyingliu01/xp-gate";
|
|
5
|
+
/**
|
|
6
|
+
* Allowlist of xp-gate gate aliases that are safe to invoke standalone
|
|
7
|
+
* (non-preCommitOnly gates + common aliases). Anything outside this set is
|
|
8
|
+
* rejected before reaching the shell.
|
|
9
|
+
*/
|
|
10
|
+
export declare const GATE_WHITELIST: readonly ["duplicates", "complexity", "principles", "arch", "architecture", "iac", "secrets", "sast"];
|
|
11
|
+
export type GateId = (typeof GATE_WHITELIST)[number];
|
|
12
|
+
export declare function isGateAllowed(gate: string): boolean;
|
|
13
|
+
export interface BuildCommandOptions {
|
|
14
|
+
subcommand: "check" | "principles" | "arch";
|
|
15
|
+
target?: string;
|
|
16
|
+
gates?: readonly string[];
|
|
17
|
+
config?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Executor dialect. Defaults to `"posix"` so existing callers keep working;
|
|
20
|
+
* callers running under DSH on Windows must pass `"powershell"`.
|
|
21
|
+
*/
|
|
22
|
+
dialect?: ShellDialect;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build a single guarded shell command: runs `xp-gate …` when the global CLI
|
|
26
|
+
* is present, otherwise prints the graceful-degradation hint. A single
|
|
27
|
+
* presence check avoids a separate probe/run race.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildCommand(options: BuildCommandOptions): string;
|
|
30
|
+
/** Resolve a model-supplied path against the session cwd; absolutes pass through. */
|
|
31
|
+
export declare function resolveTarget(rawPath: string, cwd: string): string;
|
|
32
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,cAAc,CAAA;AAI5D,OAAO,EAAE,KAAK,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAEhF,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,8FACgE,CAAA;AAE7F;;;;GAIG;AACH,eAAO,MAAM,cAAc,uGASjB,CAAA;AAEV,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,GAAG,YAAY,GAAG,MAAM,CAAA;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB;AA+BD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAmBjE;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElE"}
|