@boyingliu01/xp-gate 0.30.1 → 0.30.2
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/bin/xp-gate.js +8 -0
- package/gate-3.sh +30 -6
- package/hooks/gate-3.sh +30 -6
- package/lib/__tests__/crap-cli.test.js +76 -0
- package/lib/__tests__/crap.test.js +137 -0
- package/lib/__tests__/retro-gate-m.test.js +144 -0
- package/lib/crap-cli.js +191 -0
- package/lib/crap.js +196 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/retro.js +139 -2
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +2 -2
package/lib/crap.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CRAP advisory ranking (#404 P1-2).
|
|
5
|
+
*
|
|
6
|
+
* CRAP(m) = comp(m)^2 * (1 - cov(m)) + comp(m)
|
|
7
|
+
*
|
|
8
|
+
* Uncle Bob's combined signal: complexity is only half the story, because a
|
|
9
|
+
* gnarly function with dense tests is cheap to change while a simple one with
|
|
10
|
+
* none is not. This module produces a RANKING for navigation. It is explicitly
|
|
11
|
+
* not a gate -- see README/issue #404 for why.
|
|
12
|
+
*
|
|
13
|
+
* Three constraints came out of measuring the indicator on this repo before
|
|
14
|
+
* writing it, each of which changes the design:
|
|
15
|
+
*
|
|
16
|
+
* 1. Coverage is per FILE in the artifact the gates already produce
|
|
17
|
+
* (coverage-final.json). Function-level positions could not be joined to
|
|
18
|
+
* lizard's reliably (8.4% match rate measured), so cov(m) here is the
|
|
19
|
+
* enclosing file's coverage. That over-smooths within a file; it is
|
|
20
|
+
* acceptable for ranking and not acceptable for a pass/fail verdict.
|
|
21
|
+
*
|
|
22
|
+
* 2. 21.8% of non-mirror source files are never loaded by any test and so have
|
|
23
|
+
* no coverage at all. Treating those as 0% would push every untested file to
|
|
24
|
+
* the top of the ranking purely for being unmeasured; treating them as 100%
|
|
25
|
+
* would hide them. They are reported separately as `unmeasured`.
|
|
26
|
+
*
|
|
27
|
+
* 3. src/npm-package/** is a generated mirror (canonical source is
|
|
28
|
+
* plugins/* / the repo root) and is overwritten at publish. Ranking it would
|
|
29
|
+
* direct refactoring at a file that does not survive. Excluded by default.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
const path = require('path');
|
|
34
|
+
|
|
35
|
+
/** Repository-relative paths whose canonical source lives elsewhere. */
|
|
36
|
+
const MIRROR_PREFIXES = ['src/npm-package/'];
|
|
37
|
+
|
|
38
|
+
function isMirrorPath(filePath) {
|
|
39
|
+
if (!filePath) return false;
|
|
40
|
+
const normalized = String(filePath).split('\\').join('/').replace(/^\.\//, '');
|
|
41
|
+
return MIRROR_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* CRAP for one function.
|
|
46
|
+
*
|
|
47
|
+
* @param {number} comp cyclomatic complexity
|
|
48
|
+
* @param {number|null} cov coverage ratio in [0,1], or null when unmeasured
|
|
49
|
+
* @returns {number|null} null when coverage is unknown -- deliberately NOT a
|
|
50
|
+
* number, so an unmeasured function cannot be ranked at all
|
|
51
|
+
*/
|
|
52
|
+
function computeCrap(comp, cov) {
|
|
53
|
+
if (cov === null || cov === undefined || Number.isNaN(cov)) return null;
|
|
54
|
+
if (!Number.isFinite(comp)) return null;
|
|
55
|
+
|
|
56
|
+
// Out-of-range coverage does occur in real istanbul output (a file counted at
|
|
57
|
+
// >100% after source-map folding). Clamping keeps the score monotonic and
|
|
58
|
+
// never negative, which a negative CRAP would violate by sorting below honest
|
|
59
|
+
// clean functions.
|
|
60
|
+
const clamped = Math.min(1, Math.max(0, cov));
|
|
61
|
+
return comp * comp * (1 - clamped) + comp;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Normalise a path to the repo-relative form used by the coverage artifact. */
|
|
65
|
+
function toRepoRelative(filePath) {
|
|
66
|
+
return String(filePath).split('\\').join('/').replace(/^\.\//, '');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Attach each function's file coverage and CRAP score.
|
|
71
|
+
*
|
|
72
|
+
* @param {Array<{file:string,name:string,ccn:number}>} functions
|
|
73
|
+
* @param {Map<string, number>} coverage repo-relative path -> ratio
|
|
74
|
+
* @returns {Array<object>} each with `cov` and `crap` (either may be null)
|
|
75
|
+
*/
|
|
76
|
+
function joinCoverage(functions, coverage) {
|
|
77
|
+
return functions.map((fn) => {
|
|
78
|
+
const key = toRepoRelative(fn.file);
|
|
79
|
+
const cov = coverage instanceof Map ? coverage.get(key) : undefined;
|
|
80
|
+
const known = cov === undefined ? null : cov;
|
|
81
|
+
return { ...fn, file: key, cov: known, crap: computeCrap(fn.ccn, known) };
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Build the advisory report.
|
|
87
|
+
*
|
|
88
|
+
* @param {Array<object>} functions joined functions (may carry null cov)
|
|
89
|
+
* @param {{threshold?: number, top?: number, mirrors?: boolean}} options
|
|
90
|
+
* `mirrors: true` also ranks the generated mirror tree; default false.
|
|
91
|
+
*/
|
|
92
|
+
function buildCrapReport(functions, options = {}) {
|
|
93
|
+
const threshold = Number.isFinite(options.threshold) ? options.threshold : 6;
|
|
94
|
+
const excludeMirrors = options.mirrors !== true;
|
|
95
|
+
|
|
96
|
+
const scored = [];
|
|
97
|
+
let unmeasured = 0;
|
|
98
|
+
|
|
99
|
+
for (const fn of functions) {
|
|
100
|
+
if (excludeMirrors && isMirrorPath(fn.file)) continue;
|
|
101
|
+
|
|
102
|
+
if (fn.cov === null || fn.cov === undefined) {
|
|
103
|
+
unmeasured += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const crap = fn.crap === null || fn.crap === undefined ? computeCrap(fn.ccn, fn.cov) : fn.crap;
|
|
107
|
+
if (crap === null) {
|
|
108
|
+
unmeasured += 1;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
scored.push({ ...fn, crap });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
scored.sort((a, b) => b.crap - a.crap || a.file.localeCompare(b.file));
|
|
115
|
+
|
|
116
|
+
const hotspots = scored.filter((s) => s.crap >= threshold);
|
|
117
|
+
const limited = Number.isFinite(options.top) && options.top >= 0 ? hotspots.slice(0, options.top) : hotspots;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
hotspots: limited,
|
|
121
|
+
measured: scored.length,
|
|
122
|
+
unmeasured,
|
|
123
|
+
threshold,
|
|
124
|
+
advisory: true,
|
|
125
|
+
blocking: false,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Read per-file coverage ratios out of an istanbul coverage-final.json.
|
|
131
|
+
* Missing or unreadable input yields an empty map, which surfaces as "every
|
|
132
|
+
* function unmeasured" rather than as a silently perfect score.
|
|
133
|
+
*/
|
|
134
|
+
function readFileCoverage(coverageFile) {
|
|
135
|
+
const coverage = new Map();
|
|
136
|
+
let raw;
|
|
137
|
+
try {
|
|
138
|
+
raw = fs.readFileSync(coverageFile, 'utf8');
|
|
139
|
+
} catch {
|
|
140
|
+
return coverage;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let parsed;
|
|
144
|
+
try {
|
|
145
|
+
parsed = JSON.parse(raw);
|
|
146
|
+
} catch {
|
|
147
|
+
return coverage;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const root = process.cwd();
|
|
151
|
+
for (const [absPath, entry] of Object.entries(parsed)) {
|
|
152
|
+
if (!entry || !entry.s) continue;
|
|
153
|
+
const counts = Object.values(entry.s);
|
|
154
|
+
if (!counts.length) continue;
|
|
155
|
+
const covered = counts.filter((c) => c > 0).length;
|
|
156
|
+
const rel = path.isAbsolute(absPath)
|
|
157
|
+
? toRepoRelative(path.relative(root, absPath))
|
|
158
|
+
: toRepoRelative(absPath);
|
|
159
|
+
coverage.set(rel, covered / counts.length);
|
|
160
|
+
}
|
|
161
|
+
return coverage;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parse lizard's CSV into functions with a repo-relative file and a CCN.
|
|
166
|
+
* Column order: NLOC,CCN,token,PARAM,length,location,file,name,signature,...
|
|
167
|
+
*/
|
|
168
|
+
function parseLizardCsv(csvText) {
|
|
169
|
+
const functions = [];
|
|
170
|
+
for (const line of String(csvText || '').split(/\r?\n/)) {
|
|
171
|
+
if (!line.trim()) continue;
|
|
172
|
+
const cells = line.match(/("([^"]|"")*"|[^,]*)(,|$)/g);
|
|
173
|
+
if (!cells || cells.length < 8) continue;
|
|
174
|
+
const clean = (i) => cells[i].replace(/,$/, '').replace(/^"|"$/g, '');
|
|
175
|
+
|
|
176
|
+
const ccn = Number(clean(1));
|
|
177
|
+
if (!Number.isFinite(ccn)) continue; // header row
|
|
178
|
+
|
|
179
|
+
functions.push({
|
|
180
|
+
file: toRepoRelative(clean(6)),
|
|
181
|
+
name: clean(7),
|
|
182
|
+
ccn,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return functions;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = {
|
|
189
|
+
computeCrap,
|
|
190
|
+
joinCoverage,
|
|
191
|
+
buildCrapReport,
|
|
192
|
+
isMirrorPath,
|
|
193
|
+
readFileCoverage,
|
|
194
|
+
parseLizardCsv,
|
|
195
|
+
MIRROR_PREFIXES,
|
|
196
|
+
};
|
package/lib/retro.js
CHANGED
|
@@ -116,13 +116,133 @@ function readSprintHistory(dir, sprintFilter) {
|
|
|
116
116
|
return results;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Read Gate M rows out of the pre-push journals (#404 P1-3).
|
|
121
|
+
*
|
|
122
|
+
* Gate M lives in `.xp-gate/reports/pre-push/*.json`, not in the audit log: the
|
|
123
|
+
* audit log only sees the 12 pre-commit gates. Reading it here is the only way
|
|
124
|
+
* a retrospective can say anything about mutation testing at all.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} dir - Repository root
|
|
127
|
+
* @returns {object[]} One entry per journal that carried a Gate M row
|
|
128
|
+
*/
|
|
129
|
+
function readGateMRuns(dir) {
|
|
130
|
+
const journalDir = path.join(dir, '.xp-gate', 'reports', 'pre-push');
|
|
131
|
+
if (!fs.existsSync(journalDir)) return [];
|
|
132
|
+
|
|
133
|
+
const runs = [];
|
|
134
|
+
for (const f of fs.readdirSync(journalDir).filter(n => n.endsWith('.json'))) {
|
|
135
|
+
let journal;
|
|
136
|
+
try {
|
|
137
|
+
journal = JSON.parse(fs.readFileSync(path.join(journalDir, f), 'utf8'));
|
|
138
|
+
} catch {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const rows = Array.isArray(journal.gates) ? journal.gates : [];
|
|
142
|
+
// The TS gate is "M"; the per-language variants are M-Python/M-Go/... and
|
|
143
|
+
// are counted separately by the caller when it wants them.
|
|
144
|
+
const row = rows.find(r => r && r.id === 'M');
|
|
145
|
+
if (!row) continue;
|
|
146
|
+
const details = row.details && typeof row.details === 'object' ? row.details : {};
|
|
147
|
+
runs.push({
|
|
148
|
+
timestamp: journal.timestamp || '',
|
|
149
|
+
status: row.status || 'UNKNOWN',
|
|
150
|
+
skipReason: details.skip_reason || '',
|
|
151
|
+
durationMs: details.duration_ms != null && details.duration_ms !== '' ? Number(details.duration_ms) : null,
|
|
152
|
+
score: details.mutation_score != null && details.mutation_score !== '' ? Number(details.mutation_score) : null,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return runs;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Summarise Gate M runs for the report.
|
|
160
|
+
* @param {object[]} runs - Output of readGateMRuns
|
|
161
|
+
* @returns {object} Counts plus measured score/duration samples
|
|
162
|
+
*/
|
|
163
|
+
function summariseGateM(runs) {
|
|
164
|
+
const total = runs.length;
|
|
165
|
+
const skipped = runs.filter(r => r.status === 'SKIP').length;
|
|
166
|
+
const timedOut = runs.filter(r => r.status === 'TIMEOUT').length;
|
|
167
|
+
// The journals write "BLOCKED" (pre-push's literal), not "BLOCK". Matching
|
|
168
|
+
// only one spelling silently reported 5 real mutation blocks as zero, which
|
|
169
|
+
// is the exact failure mode this whole issue is about.
|
|
170
|
+
const blocked = runs.filter(r => r.status === 'BLOCK' || r.status === 'BLOCKED').length;
|
|
171
|
+
const passed = runs.filter(r => r.status === 'PASS').length;
|
|
172
|
+
// A journal row that predates the #404 fix has an empty details object; that
|
|
173
|
+
// is "this run told us nothing", not "this run scored zero".
|
|
174
|
+
const measured = runs.filter(r => Number.isFinite(r.score));
|
|
175
|
+
const durations = runs.filter(r => Number.isFinite(r.durationMs)).map(r => r.durationMs);
|
|
176
|
+
const reasons = {};
|
|
177
|
+
for (const r of runs) {
|
|
178
|
+
if (r.status !== 'SKIP' && r.status !== 'TIMEOUT') continue;
|
|
179
|
+
const key = r.skipReason || '(no reason recorded)';
|
|
180
|
+
reasons[key] = (reasons[key] || 0) + 1;
|
|
181
|
+
}
|
|
182
|
+
return { total, skipped, timedOut, blocked, passed, measured, durations, reasons };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build the Gate M retrospective section.
|
|
187
|
+
* @param {object[]} runs - Output of readGateMRuns
|
|
188
|
+
* @returns {string[]} Markdown lines
|
|
189
|
+
*/
|
|
190
|
+
function buildGateMSection(runs) {
|
|
191
|
+
const L = [];
|
|
192
|
+
L.push('## Gate M (Mutation Testing) (#404)');
|
|
193
|
+
|
|
194
|
+
if (runs.length === 0) {
|
|
195
|
+
L.push('No pre-push journals found — Gate M has never been measured here.');
|
|
196
|
+
L.push('');
|
|
197
|
+
return L;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const s = summariseGateM(runs);
|
|
201
|
+
const pct = n => `${((n / s.total) * 100).toFixed(1)}%`;
|
|
202
|
+
|
|
203
|
+
L.push(`- Runs recorded: ${s.total} (PASS ${s.passed}, BLOCK ${s.blocked}, TIMEOUT ${s.timedOut}, SKIP ${s.skipped})`);
|
|
204
|
+
L.push(`- Skip rate: ${pct(s.skipped)}; timeout rate: ${pct(s.timedOut)}`);
|
|
205
|
+
|
|
206
|
+
if (s.measured.length > 0) {
|
|
207
|
+
const scores = s.measured.map(r => r.score).sort((a, b) => a - b);
|
|
208
|
+
const median = scores[Math.floor(scores.length / 2)];
|
|
209
|
+
const p10 = scores[Math.floor(scores.length * 0.1)];
|
|
210
|
+
const p90 = scores[Math.min(scores.length - 1, Math.floor(scores.length * 0.9))];
|
|
211
|
+
L.push(`- Mutation score over ${scores.length} measured run(s): median ${median.toFixed(1)}%, p10 ${p10.toFixed(1)}%, p90 ${p90.toFixed(1)}%`);
|
|
212
|
+
} else {
|
|
213
|
+
// Printing "median 0%" or omitting the line would both read as a healthy
|
|
214
|
+
// result. There is no sample, and the report must say exactly that.
|
|
215
|
+
L.push('- Mutation score: no measured runs — the score has never been recorded in this window.');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (s.durations.length > 0) {
|
|
219
|
+
const sorted = [...s.durations].sort((a, b) => a - b);
|
|
220
|
+
const p50 = sorted[Math.floor(sorted.length / 2)];
|
|
221
|
+
const max = sorted[sorted.length - 1];
|
|
222
|
+
L.push(`- Duration over ${sorted.length} measured run(s): p50 ${p50}ms, max ${max}ms`);
|
|
223
|
+
} else {
|
|
224
|
+
L.push('- Duration: no measured runs.');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const reasonKeys = Object.keys(s.reasons);
|
|
228
|
+
if (reasonKeys.length > 0) {
|
|
229
|
+
L.push('- Why it did not run:');
|
|
230
|
+
for (const key of reasonKeys.sort((a, b) => s.reasons[b] - s.reasons[a])) {
|
|
231
|
+
L.push(` - ${key}: ${s.reasons[key]}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
L.push('');
|
|
236
|
+
return L;
|
|
237
|
+
}
|
|
238
|
+
|
|
119
239
|
/**
|
|
120
240
|
* Build the Markdown retrospective report.
|
|
121
241
|
* @param {object} opts - Report data
|
|
122
242
|
* @returns {string} Markdown report
|
|
123
243
|
*/
|
|
124
244
|
function buildReport(opts) {
|
|
125
|
-
const { days, commits, audit, quality, sprints } = opts;
|
|
245
|
+
const { days, commits, audit, quality, sprints, gateMRuns = [] } = opts;
|
|
126
246
|
const now = new Date();
|
|
127
247
|
const start = new Date(now.getTime() - days * 86400000);
|
|
128
248
|
const L = [];
|
|
@@ -201,6 +321,9 @@ function buildReport(opts) {
|
|
|
201
321
|
}
|
|
202
322
|
L.push('');
|
|
203
323
|
|
|
324
|
+
// Gate M (Mutation Testing) — #404 P1-3
|
|
325
|
+
L.push(...buildGateMSection(gateMRuns));
|
|
326
|
+
|
|
204
327
|
// Quality Trend
|
|
205
328
|
L.push('## Quality Trend');
|
|
206
329
|
if (quality.length === 0) {
|
|
@@ -238,6 +361,7 @@ async function handleRetro(args = []) {
|
|
|
238
361
|
const audit = readAuditEntries(dir);
|
|
239
362
|
const quality = readQualityHistory(dir);
|
|
240
363
|
const sprints = readSprintHistory(dir, sprintFilter);
|
|
364
|
+
const gateMRuns = readGateMRuns(dir);
|
|
241
365
|
|
|
242
366
|
if (jsonFlag) {
|
|
243
367
|
const gateEntries = audit.filter(e => e.gate_id);
|
|
@@ -249,11 +373,24 @@ async function handleRetro(args = []) {
|
|
|
249
373
|
rework_rates: sprints.filter(s => s.metrics?.rework_rate != null).map(s => ({ id: s.id, rate: s.metrics.rework_rate })),
|
|
250
374
|
evidence_skipped: audit.filter(e => e.event === 'evidence_skipped'),
|
|
251
375
|
quality: quality.map(q => ({ timestamp: q.timestamp, score: q.score })),
|
|
376
|
+
gate_m: (() => {
|
|
377
|
+
const s = summariseGateM(gateMRuns);
|
|
378
|
+
return {
|
|
379
|
+
runs: s.total,
|
|
380
|
+
passed: s.passed,
|
|
381
|
+
blocked: s.blocked,
|
|
382
|
+
timed_out: s.timedOut,
|
|
383
|
+
skipped: s.skipped,
|
|
384
|
+
skip_reasons: s.reasons,
|
|
385
|
+
measured_scores: s.measured.map(r => r.score),
|
|
386
|
+
measured_durations_ms: s.durations,
|
|
387
|
+
};
|
|
388
|
+
})(),
|
|
252
389
|
}, null, 2));
|
|
253
390
|
return 0;
|
|
254
391
|
}
|
|
255
392
|
|
|
256
|
-
console.log(buildReport({ days, commits, audit, quality, sprints }));
|
|
393
|
+
console.log(buildReport({ days, commits, audit, quality, sprints, gateMRuns }));
|
|
257
394
|
return 0;
|
|
258
395
|
}
|
|
259
396
|
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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.30.
|
|
3
|
+
"version": "0.30.2",
|
|
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": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/plugins/dsh/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boyingliu01/dsh-plugin-xp-gate",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.2",
|
|
4
4
|
"description": "XP-Gate deterministic quality gates + AI workflow skills as a DeepSeek Harness (DSH) native plugin: gate-check / gate-principles / gate-arch tools + 12 SKILL.md skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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": "xp-gate",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.2",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Qoder. 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": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/principles/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# PRINCIPLES CHECKER MODULE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Clean Code & SOLID principles checker — **Gate 4** of pre-commit. 14 rules × 9 language adapters, SARIF 2.1.0 output. Houses the **Boy Scout Rule** enforcement engine (Gate 6) and warning-baseline storage.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.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
3
|
**Generated:** 2026-10-20
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 9fab5b3b
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.30.
|
|
6
|
+
**Version:** 0.30.2.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|