@boyingliu01/xp-gate 0.14.31 → 0.15.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 (76) hide show
  1. package/adapter-common.sh +0 -0
  2. package/adapters/cpp.sh +0 -0
  3. package/adapters/dart.sh +0 -0
  4. package/adapters/flutter.sh +0 -0
  5. package/adapters/go.sh +0 -0
  6. package/adapters/iac.sh +0 -0
  7. package/adapters/java.sh +33 -4
  8. package/adapters/kotlin.sh +0 -0
  9. package/adapters/objectivec.sh +0 -0
  10. package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
  11. package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
  12. package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
  13. package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
  14. package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
  15. package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
  16. package/adapters/powershell.sh +0 -0
  17. package/adapters/python.sh +0 -0
  18. package/adapters/shell.sh +0 -0
  19. package/adapters/swift.sh +0 -0
  20. package/adapters/typescript.sh +0 -0
  21. package/bin/xp-gate.js +0 -0
  22. package/gate-3.sh +0 -0
  23. package/gate-4.sh +0 -0
  24. package/gate-7.sh +0 -0
  25. package/gate-8.sh +0 -0
  26. package/gate-9.sh +22 -48
  27. package/hooks/adapter-common.sh +0 -0
  28. package/hooks/gate-3.sh +0 -0
  29. package/hooks/gate-4.sh +0 -0
  30. package/hooks/gate-7.sh +0 -0
  31. package/hooks/gate-8.sh +0 -0
  32. package/hooks/gate-9.sh +22 -48
  33. package/hooks/pre-commit +102 -44
  34. package/hooks/pre-push +1 -33
  35. package/lib/__tests__/doctor.test.js +34 -31
  36. package/lib/__tests__/phase-transition.test.js +272 -272
  37. package/lib/__tests__/sprint-audit.test.js +207 -207
  38. package/lib/detect-deps.js +12 -0
  39. package/lib/phase-transition.js +249 -249
  40. package/lib/sprint-audit.js +235 -235
  41. package/mock-policy/AGENTS.md +3 -3
  42. package/mutation/AGENTS.md +3 -3
  43. package/package.json +1 -1
  44. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  45. package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
  46. package/plugins/claude-code/bin/xp-gate-check +0 -0
  47. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  48. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  49. package/plugins/claude-code/skills/sprint-flow/SKILL.md +1 -0
  50. package/plugins/claude-code/skills/sprint-flow/references/phase-3-build.md +71 -0
  51. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  52. package/plugins/opencode/package.json +1 -1
  53. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  54. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  55. package/plugins/opencode/skills/sprint-flow/SKILL.md +1 -0
  56. package/plugins/opencode/skills/sprint-flow/references/phase-3-build.md +71 -0
  57. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  58. package/plugins/qoder/agents/delphi-architecture.md +48 -48
  59. package/plugins/qoder/agents/delphi-feasibility.md +48 -48
  60. package/plugins/qoder/agents/delphi-technical.md +48 -48
  61. package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
  62. package/plugins/qoder/bin/xp-gate-check +0 -0
  63. package/plugins/qoder/plugin.json +1 -1
  64. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  65. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  66. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  67. package/principles/AGENTS.md +3 -3
  68. package/principles/adapters/python.ts +39 -6
  69. package/scripts/__tests__/sync-package-content.test.js +36 -17
  70. package/scripts/delphi-external-review.cjs +432 -432
  71. package/scripts/sync-package-content.js +5 -4
  72. package/skills/delphi-review/AGENTS.md +3 -3
  73. package/skills/sprint-flow/AGENTS.md +3 -3
  74. package/skills/sprint-flow/SKILL.md +1 -0
  75. package/skills/sprint-flow/references/phase-3-build.md +71 -0
  76. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -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 };
@@ -1,9 +1,9 @@
1
1
  # SRC/MOCK-POLICY KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-07-22
4
- **Commit:** 18b636a
3
+ **Generated:** 2026-07-23
4
+ **Commit:** 1d581aa
5
5
  **Branch:** main
6
- **Version:** 0.14.31.0
6
+ **Version:** 0.15.0.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.
@@ -1,9 +1,9 @@
1
1
  # SRC/MUTATION KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-07-22
4
- **Commit:** 18b636a
3
+ **Generated:** 2026-07-23
4
+ **Commit:** 1d581aa
5
5
  **Branch:** main
6
- **Version:** 0.14.31.0
6
+ **Version:** 0.15.0.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": "@boyingliu01/xp-gate",
3
- "version": "0.14.31",
3
+ "version": "0.15.0",
4
4
  "description": "AI-driven development workflow: 6 quality gates + Delphi review + Sprint Flow",
5
5
  "bin": {
6
6
  "xp-gate": "./bin/xp-gate.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.14.31",
3
+ "version": "0.15.0",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 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-07-22
4
- **Commit:** 18b636a
3
+ **Generated:** 2026-07-23
4
+ **Commit:** 1d581aa
5
5
  **Branch:** main
6
- **Version:** 0.14.31.0
6
+ **Version:** 0.15.0.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-07-22
4
- **Commit:** 18b636a
3
+ **Generated:** 2026-07-23
4
+ **Commit:** 1d581aa
5
5
  **Branch:** main
6
- **Version:** 0.14.31.0
6
+ **Version:** 0.15.0.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.
@@ -269,6 +269,7 @@ next_phase_context: [key info for next phase]
269
269
  | Clean up CLOSE without backing up sprint-state first | `.sprint-state/` is inside worktree; it's lost on worktree removal. CLOSE step 1 MUST back up to repo-tracked path first |
270
270
  | Keep adding random changes after verification failures | Max 3 fix cycles; if still failing, BLOCK and request user decision |
271
271
  | Enter next phase without generating Phase Summary | Every phase MUST write `.sprint-state/phase-outputs/phase-{N}-summary.md` and pass transition gate |
272
+ | Complete implementation change without running verification | After every file edit/refactor/feature, MUST run `npm test` + `npm run lint` + `npx tsc --noEmit` and record the structured verification event (see `phase-3-build.md` §In-Session Verification) |
272
273
 
273
274
  ---
274
275
 
@@ -144,6 +144,77 @@ This enforcement is instructional (LLM follows SKILL.md instructions), not progr
144
144
 
145
145
  ---
146
146
 
147
+ ## In-Session Verification (MANDATORY — v0.14.31+)
148
+
149
+ **Purpose**: Ensure every implementation change is immediately verified and recorded, enabling Better Loop Harness (and other observability tools) to capture normalized change→validation event pairs.
150
+
151
+ **Problem it solves**: Without explicit in-session verification, Agents may edit files and move on without running tests or linters. This creates unverified changes that are invisible to post-hoc review tools, breaking the traceability chain between "what changed" and "was it validated".
152
+
153
+ ### Mandatory Verification Protocol
154
+
155
+ After **every** implementation change (file edit, refactor, new feature, bug fix), the Agent MUST execute the following sequence BEFORE claiming the change is complete:
156
+
157
+ 1. **Run tests**: `npm test` or `npx vitest run` (or project-specific test command)
158
+ 2. **Run linter**: `npm run lint` or `npx eslint .` (or project-specific lint command)
159
+ 3. **Type-check** (TypeScript projects): `npx tsc --noEmit`
160
+ 4. **Record the result**: Append a structured verification event (see format below)
161
+
162
+ ### Verification Event Format
163
+
164
+ Each verification event MUST be recorded as a structured JSON object. Agents should output this to stdout or append to `.sprint-state/phase-outputs/verification-events.jsonl`:
165
+
166
+ ```json
167
+ {
168
+ "timestamp": "2026-07-22T10:30:00Z",
169
+ "change": {
170
+ "type": "edit|refactor|feature|bugfix",
171
+ "files": ["src/lib/auth.ts", "src/lib/auth.test.ts"],
172
+ "description": "Implement validateToken() for expired token handling"
173
+ },
174
+ "verification": {
175
+ "tests": { "command": "npx vitest run src/lib/auth.test.ts", "status": "pass|fail", "summary": "4 passed, 0 failed" },
176
+ "lint": { "command": "npm run lint", "status": "pass|fail|warn", "summary": "0 errors, 2 warnings" },
177
+ "typecheck": { "command": "npx tsc --noEmit", "status": "pass|fail|skip", "summary": "No type errors" }
178
+ },
179
+ "outcome": "verified|failed|partial",
180
+ "req": "REQ-003"
181
+ }
182
+ ```
183
+
184
+ ### Integration with ralph-loop
185
+
186
+ When using ralph-loop mode (default BUILD strategy), each REQ iteration MUST include verification as the final step before marking the REQ complete:
187
+
188
+ ```
189
+ ralph-loop iteration:
190
+ 1. [TDD-RED] Write failing test
191
+ 2. [TDD-GREEN] Implement minimal code to pass
192
+ 3. [TDD-REFACTOR] Clean up while keeping tests green
193
+ 4. [VERIFY] Run tests + lint + typecheck ← MANDATORY
194
+ 5. [RECORD] Append verification event ← MANDATORY
195
+ 6. Mark REQ complete and continue to next
196
+ ```
197
+
198
+ ### Anti-Patterns
199
+
200
+ | ❌ Error | ✅ Correct |
201
+ |----------|----------|
202
+ | Edit file → claim done → move to next task | Edit file → run tests + lint → record result → claim done |
203
+ | "Tests should pass" (assertion without evidence) | "Tests passed: 4 passed, 0 failed (npx vitest run)" |
204
+ | Run tests only at end of entire BUILD phase | Run tests after every individual change/REQ |
205
+ | Skip type-check because "I didn't touch types" | Always run `tsc --noEmit` on TypeScript projects |
206
+
207
+ ### Harness Integration
208
+
209
+ Better Loop Harness captures change and verification events by inspecting session history. The structured verification events above serve as the canonical signal that:
210
+ - A change occurred (the `change` field)
211
+ - The change was validated (the `verification` field)
212
+ - The outcome is attributable (the `outcome` field ties result to specific change)
213
+
214
+ Without these events, the Harness can only infer changes from file diffs and has no verification signal, resulting in lower "改动验证" (Change Verification) dimension scores.
215
+
216
+ ---
217
+
147
218
  ## TDD 强制执行
148
219
 
149
220
  ### Gate 5a-BLOCK: 新增文件测试强制
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-07-22
4
- **Commit:** 18b636a
3
+ **Generated:** 2026-07-23
4
+ **Commit:** 1d581aa
5
5
  **Branch:** main
6
- **Version:** 0.14.31.0
6
+ **Version:** 0.15.0.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boyingliu01/opencode-plugin",
3
- "version": "0.14.31",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "XP-Gate quality gates + AI workflow skills + Sprint Flow TUI sidebar for OpenCode",