@boyingliu01/xp-gate 0.32.3 → 0.33.1
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 +2 -2
- package/gates/common-process.ts +178 -0
- package/gates/common.ts +208 -0
- package/gates/gate-3.ts +144 -0
- package/gates/gate-7.ts +126 -0
- package/gates/gate-8.ts +92 -0
- package/gates/gate-9.ts +269 -0
- package/gates/pbt-detect.ts +163 -0
- package/gates/python-health.ts +266 -0
- package/gates/test-layers.ts +312 -0
- package/lib/__tests__/check-changed-brief.test.js +63 -0
- package/lib/__tests__/check-normalize-exit.test.js +24 -0
- package/lib/__tests__/shared-utils.test.js +21 -0
- package/lib/__tests__/uninstall.test.js +18 -0
- package/lib/check.js +140 -5
- package/lib/gate-runner.js +6 -1
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/shared-utils.js +6 -1
- package/lib/uninstall.js +21 -1
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +2 -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/sprint-flow/references/phase-3-build.md +26 -0
- 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/sprint-flow/references/phase-3-build.md +26 -0
- 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/sprint-flow/references/phase-3-build.md +26 -0
- 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/sprint-flow/references/phase-3-build.md +26 -0
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/scripts/sync-package-content.js +6 -1
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/sprint-flow/references/phase-3-build.md +26 -0
- package/skills/test-specification-alignment/AGENTS.md +2 -2
|
@@ -309,6 +309,24 @@ describe('uninstall', () => {
|
|
|
309
309
|
expect(cfg.uninstalled).toBeDefined();
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
+
it('AC-02: uninstall in global mode unsets even when git returns a forward-slash path (#656)', async () => {
|
|
313
|
+
// Windows: git returns "C:/Users/.../hooks" while expectedPath uses
|
|
314
|
+
// backslashes. A raw === skipped the unset, leaving a dangling hooksPath.
|
|
315
|
+
setupGlobalInstall();
|
|
316
|
+
const expectedHooksPath = globalHooksDir();
|
|
317
|
+
mockExecGlobalHooksPath(expectedHooksPath.replace(/\\/g, '/'));
|
|
318
|
+
const { uninstall } = require('../uninstall');
|
|
319
|
+
|
|
320
|
+
const result = await uninstall([]);
|
|
321
|
+
|
|
322
|
+
expect(result).toBe(0);
|
|
323
|
+
expect(execSpy).toHaveBeenCalledWith(
|
|
324
|
+
expect.stringContaining('git config --global --unset core.hooksPath'),
|
|
325
|
+
expect.any(Object)
|
|
326
|
+
);
|
|
327
|
+
expect(warnSpy).not.toHaveBeenCalledWith(expect.stringContaining('does not match'));
|
|
328
|
+
});
|
|
329
|
+
|
|
312
330
|
it('AC-02: uninstall in global mode skips unset when hooksPath does not match', async () => {
|
|
313
331
|
setupGlobalInstall();
|
|
314
332
|
mockExecGlobalHooksPath('/some/other/path');
|
package/lib/check.js
CHANGED
|
@@ -13,13 +13,15 @@ function resolveGateToken(token) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function parseCheckArgs(args) {
|
|
16
|
-
const flags = { all: false, list: false, gateIds: null };
|
|
16
|
+
const flags = { all: false, list: false, gateIds: null, changed: false, brief: false };
|
|
17
17
|
const posArgs = [];
|
|
18
18
|
let i = 0;
|
|
19
19
|
while (i < args.length) {
|
|
20
20
|
const arg = args[i];
|
|
21
21
|
if (arg === '--all') { flags.all = true; i++; }
|
|
22
22
|
else if (arg === '--list') { flags.list = true; i++; }
|
|
23
|
+
else if (arg === '--changed') { flags.changed = true; i++; }
|
|
24
|
+
else if (arg === '--brief') { flags.brief = true; i++; }
|
|
23
25
|
else if (arg === '--gates' && i + 1 < args.length) {
|
|
24
26
|
flags.gateIds = args[i + 1].split(',').map(s => s.trim()).filter(Boolean);
|
|
25
27
|
i += 2;
|
|
@@ -41,11 +43,31 @@ function parseCheckArgs(args) {
|
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Coerce a runGate() verdict into a valid process exit code (#657). The only
|
|
48
|
+
* non-numeric verdict is GATE_NOT_RUN (a string sentinel meaning "did not run");
|
|
49
|
+
* it must become 0, because a gate that honestly reported nothing to analyse is
|
|
50
|
+
* not a failure — and passing the string to process.exit() crashes Node.
|
|
51
|
+
* @param {number|string} verdict
|
|
52
|
+
* @returns {number}
|
|
53
|
+
*/
|
|
54
|
+
function normalizeGateExit(verdict) {
|
|
55
|
+
return typeof verdict === 'number' ? verdict : 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
44
58
|
async function check(args) {
|
|
45
59
|
const opts = parseCheckArgs(args);
|
|
46
60
|
|
|
47
61
|
if (opts.list) return listGates();
|
|
48
|
-
if (opts.
|
|
62
|
+
if (opts.changed) return runChangedBrief();
|
|
63
|
+
// #657: runGate() can return the GATE_NOT_RUN sentinel (a string) when a gate
|
|
64
|
+
// has no module/fallback. Returning it straight to the caller let it reach
|
|
65
|
+
// process.exit(), which throws ERR_INVALID_ARG_TYPE and dumps a stack trace.
|
|
66
|
+
// Map it to exit 0 here (the gate honestly reported "did not run"); a real
|
|
67
|
+
// failure code passes through unchanged.
|
|
68
|
+
if (opts.gateNumber && !opts.all && !opts.gateIds) {
|
|
69
|
+
return normalizeGateExit(await runGate(opts.gateNumber, opts.pathArg));
|
|
70
|
+
}
|
|
49
71
|
if (opts.all) return checkAllGates(opts.pathArg);
|
|
50
72
|
if (opts.gateIds && opts.gateIds.length > 0) return runGatesByIds(opts.gateIds, opts.pathArg);
|
|
51
73
|
|
|
@@ -55,6 +77,7 @@ async function check(args) {
|
|
|
55
77
|
console.error('Options:');
|
|
56
78
|
console.error(' --gates <ids> Run specific gates (comma-separated IDs or aliases, e.g. "version,4,secrets")');
|
|
57
79
|
console.error(' --all Run all invokable gates');
|
|
80
|
+
console.error(' --changed --brief Fast: run only the gates the current diff can trip, one-screen summary');
|
|
58
81
|
console.error(' --list List all available gates');
|
|
59
82
|
console.error('');
|
|
60
83
|
console.error('Available Gates:');
|
|
@@ -68,6 +91,7 @@ async function check(args) {
|
|
|
68
91
|
console.error(' xp-gate check secrets # Run Gate 8 via alias');
|
|
69
92
|
console.error(' xp-gate check . --gates version,4,secrets # Run gates 0, 4, and 8');
|
|
70
93
|
console.error(' xp-gate check . --all # Run all invokable gates');
|
|
94
|
+
console.error(' xp-gate check . --changed --brief # Fast: only what this diff trips');
|
|
71
95
|
console.error(' xp-gate check --list # List available gates');
|
|
72
96
|
return 1;
|
|
73
97
|
}
|
|
@@ -174,6 +198,7 @@ async function checkAllGates(target) {
|
|
|
174
198
|
|
|
175
199
|
async function runGatesByIds(gateIds, target) {
|
|
176
200
|
let failures = 0;
|
|
201
|
+
let skipped = 0;
|
|
177
202
|
let ran = 0;
|
|
178
203
|
const resolvedGates = [];
|
|
179
204
|
const unknown = [];
|
|
@@ -196,8 +221,10 @@ async function runGatesByIds(gateIds, target) {
|
|
|
196
221
|
}
|
|
197
222
|
|
|
198
223
|
for (const resolvedId of resolvedGates) {
|
|
224
|
+
// #618/#657: a NOT_RUN sentinel is a skip, not a pass and not a failure.
|
|
199
225
|
const code = await runGate(resolvedId, target);
|
|
200
|
-
if (code
|
|
226
|
+
if (code === GATE_NOT_RUN) skipped += 1;
|
|
227
|
+
else if (code !== 0) failures += 1;
|
|
201
228
|
ran += 1;
|
|
202
229
|
console.log('');
|
|
203
230
|
}
|
|
@@ -208,7 +235,115 @@ async function runGatesByIds(gateIds, target) {
|
|
|
208
235
|
return 1;
|
|
209
236
|
}
|
|
210
237
|
|
|
211
|
-
|
|
238
|
+
const tally = { passed: ran - failures - skipped, failed: failures, warned: 0, skipped, total: ran };
|
|
239
|
+
console.log(`[xp-gate check] ${formatGateTally(tally)}`);
|
|
240
|
+
return failures === 0 ? 0 : 1;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Fast "what will this commit trip?" view (P1-4, diagnosis 2026-10-30).
|
|
245
|
+
*
|
|
246
|
+
* `check . --all` runs the whole tree (minutes). During BUILD the developer only
|
|
247
|
+
* needs to know which blocking rules the CURRENT diff touches, in one screen and
|
|
248
|
+
* in seconds. This maps the diff to the gates that read changed files and runs
|
|
249
|
+
* only those, so the common case (docs / a couple of sources) returns fast.
|
|
250
|
+
*
|
|
251
|
+
* Honest by construction: prints only gate->verdict, never implying a gate ran
|
|
252
|
+
* when it was skipped. Gates not modelled here are listed as "commit-time".
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
/** Extension of a path, lowercased, without the dot ('' when none). */
|
|
256
|
+
function fileExtension(file) {
|
|
257
|
+
const m = /\.([a-z0-9]+)$/i.exec(file);
|
|
258
|
+
return m ? m[1].toLowerCase() : '';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Wall-clock budget for one `git diff` used by the --changed resolver. */
|
|
262
|
+
const GIT_DIFF_TIMEOUT_MS = 10000;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Run one git diff and split its stdout into file names. Returns null ONLY for a
|
|
266
|
+
* failed diff (not a git repo); any git error is caught and mapped to null so the
|
|
267
|
+
* caller can report "not a repository" instead of crashing on a null status.
|
|
268
|
+
*/
|
|
269
|
+
function gitChangedNames(spawnSync, args) {
|
|
270
|
+
try {
|
|
271
|
+
const res = spawnSync('git', args, { encoding: 'utf-8', timeout: GIT_DIFF_TIMEOUT_MS });
|
|
272
|
+
if (res.status !== 0 || typeof res.stdout !== 'string') return null;
|
|
273
|
+
return res.stdout.split('\n').map((f) => f.trim()).filter(Boolean);
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Diff scope mirrors pre-commit: staged first, then anything vs HEAD. */
|
|
280
|
+
function readChangedFiles() {
|
|
281
|
+
const { spawnSync } = require('child_process');
|
|
282
|
+
const staged = gitChangedNames(spawnSync, ['diff', '--cached', '--name-only', '--diff-filter=ACM']);
|
|
283
|
+
if (staged === null) return null;
|
|
284
|
+
if (staged.length > 0) return staged;
|
|
285
|
+
const vsHead = gitChangedNames(spawnSync, ['diff', 'HEAD', '--name-only', '--diff-filter=ACM']);
|
|
286
|
+
return vsHead === null ? staged : vsHead;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Run the gates a diff can trip that are both diff-scoped and fast. Gate 2
|
|
291
|
+
* (jscpd) is excluded on purpose: it scans the whole tree and duplicate
|
|
292
|
+
* detection is a codebase-wide question, not a per-diff one. Gate 4 is handed the
|
|
293
|
+
* actual files — calling it with '.' examined 0 files and printed a vacuous
|
|
294
|
+
* "No violations found", the exact anti-pattern this project forbids.
|
|
295
|
+
*/
|
|
296
|
+
async function runBriefGates(sourceFiles) {
|
|
297
|
+
const results = [];
|
|
298
|
+
if (sourceFiles.length === 0) return results;
|
|
299
|
+
results.push({ id: '3', code: await runGate('3', '.', {}) });
|
|
300
|
+
const { principles } = require('./principles.js');
|
|
301
|
+
results.push({ id: '4', code: await principles(sourceFiles) });
|
|
302
|
+
return results;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const BRIEF_GATE_NAMES = { '2': 'Duplicates', '3': 'Complexity', '4': 'Principles' };
|
|
306
|
+
const COMMIT_TIME_GATES = ['1 lint', '5 tests', '6 Boy Scout', '7 IaC', '8 secrets', '9 sast', '10 build', '11 sprint'];
|
|
307
|
+
|
|
308
|
+
/** Print the one-screen summary; returns the number of blocking gates. */
|
|
309
|
+
function printBriefSummary(changed, sourceFiles, results, ms) {
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log(`━━━ xp-gate check --changed (${changed.length} file(s), ${sourceFiles.length} source) ━━━`);
|
|
312
|
+
if (sourceFiles.length === 0) {
|
|
313
|
+
console.log(' No source files changed — no blocking source gate applies (docs/config only).');
|
|
314
|
+
}
|
|
315
|
+
let failures = 0;
|
|
316
|
+
for (const r of results) {
|
|
317
|
+
let verdict;
|
|
318
|
+
if (r.code === 0) verdict = 'PASS';
|
|
319
|
+
else if (r.code === 'NOT_RUN') verdict = 'SKIP';
|
|
320
|
+
else { verdict = 'BLOCK'; failures += 1; }
|
|
321
|
+
console.log(` Gate ${r.id} ${(BRIEF_GATE_NAMES[r.id] || r.id).padEnd(12)} ${verdict}`);
|
|
322
|
+
}
|
|
323
|
+
const rest = sourceFiles.length > 0 ? COMMIT_TIME_GATES.filter((g) => !/^(1 |5 |6 )/.test(g)) : COMMIT_TIME_GATES;
|
|
324
|
+
console.log(` commit-time (not run here): ${rest.join(', ')}`);
|
|
325
|
+
console.log(` ${failures === 0 ? '✅ clear' : `❌ ${failures} blocking`} — ${ms} ms`);
|
|
326
|
+
return failures;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function runChangedBrief() {
|
|
330
|
+
const { SOURCE_FILE_EXTENSIONS } = require('./gate-runner.js');
|
|
331
|
+
const t0 = Date.now();
|
|
332
|
+
|
|
333
|
+
const changed = readChangedFiles();
|
|
334
|
+
if (changed === null) {
|
|
335
|
+
console.error('[xp-gate check --changed] not a git repository');
|
|
336
|
+
return 1;
|
|
337
|
+
}
|
|
338
|
+
if (changed.length === 0) {
|
|
339
|
+
console.log('[xp-gate check --changed] no staged/tracked changes — nothing to gate (0 files).');
|
|
340
|
+
return 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const srcExt = new Set(SOURCE_FILE_EXTENSIONS);
|
|
344
|
+
const sourceFiles = changed.filter((f) => srcExt.has(fileExtension(f)));
|
|
345
|
+
const results = await runBriefGates(sourceFiles);
|
|
346
|
+
const failures = printBriefSummary(changed, sourceFiles, results, Date.now() - t0);
|
|
212
347
|
return failures === 0 ? 0 : 1;
|
|
213
348
|
}
|
|
214
349
|
|
|
@@ -244,4 +379,4 @@ function listGates() {
|
|
|
244
379
|
return 0;
|
|
245
380
|
}
|
|
246
381
|
|
|
247
|
-
module.exports = { check, checkAllGates, formatGateTally };
|
|
382
|
+
module.exports = { check, checkAllGates, formatGateTally, runChangedBrief, fileExtension, printBriefSummary, normalizeGateExit };
|
package/lib/gate-runner.js
CHANGED
|
@@ -252,7 +252,12 @@ function runGateAdapter(scriptPath, cwd = process.cwd(), opts = {}) {
|
|
|
252
252
|
execFileSync(
|
|
253
253
|
bashCommand,
|
|
254
254
|
['--noprofile', '--norc', '-c', wrap, 'xp-gate', commonScript, nowMsScript, scriptPath, process.execPath, listFile],
|
|
255
|
-
|
|
255
|
+
// #660: the pre-commit hook exports GATE_DIR before sourcing a fragment, but
|
|
256
|
+
// the CLI path never did, so gate-3.sh looked for $GATE_DIR/lib/ccn-ratchet.sh
|
|
257
|
+
// (empty), fell back to <repo>/githooks/lib, and falsely BLOCKED on any
|
|
258
|
+
// project without vendored gates. The fragment lives beside its lib/, so
|
|
259
|
+
// point GATE_DIR at its own directory.
|
|
260
|
+
{ stdio: 'inherit', cwd, env: { ...process.env, GATE_DIR: scriptDir } }
|
|
256
261
|
);
|
|
257
262
|
} finally {
|
|
258
263
|
removeFileList(listFile);
|
package/lib/shared-utils.js
CHANGED
|
@@ -37,7 +37,12 @@ function copyHooks(srcDir, destDir) {
|
|
|
37
37
|
// post-commit carries the audit-attribution flush and --no-verify detection.
|
|
38
38
|
// It was absent here, so neither ever ran for installed users (the source
|
|
39
39
|
// existed but no installer copied it).
|
|
40
|
-
|
|
40
|
+
//
|
|
41
|
+
// hook-module-resolution.sh (#655/#631) is loaded by pre-commit BEFORE any tier
|
|
42
|
+
// decision and fails closed — omitting it made every commit FATAL on a fresh
|
|
43
|
+
// install. It is generated, but it must still ship beside the hooks.
|
|
44
|
+
const hookFiles = ['pre-commit', 'pre-push', 'post-commit', 'post-merge', 'hook-module-resolution.sh'];
|
|
45
|
+
hookFiles.forEach(hook => {
|
|
41
46
|
const src = path.join(srcDir, 'hooks', hook);
|
|
42
47
|
const dest = path.join(destDir, hook);
|
|
43
48
|
if (fs.existsSync(src)) {
|
package/lib/uninstall.js
CHANGED
|
@@ -333,7 +333,11 @@ function unsetGitConfigIfMatch(item) {
|
|
|
333
333
|
console.log(` ${item.label} — not set`);
|
|
334
334
|
return true;
|
|
335
335
|
}
|
|
336
|
-
|
|
336
|
+
// #656: git returns a forward-slash path ("C:/Users/.../hooks"), while
|
|
337
|
+
// item.expectedPath is built with path.join (backslashes on Windows). A raw
|
|
338
|
+
// === compares these as different and the unset is silently skipped, leaving
|
|
339
|
+
// a dangling core.hooksPath. Normalize both sides to an absolute, real path.
|
|
340
|
+
if (sameHooksPath(currentPath, item.expectedPath)) {
|
|
337
341
|
unsetHooksPath();
|
|
338
342
|
console.log(` Unset ${item.label}`);
|
|
339
343
|
return true;
|
|
@@ -342,6 +346,22 @@ function unsetGitConfigIfMatch(item) {
|
|
|
342
346
|
return false;
|
|
343
347
|
}
|
|
344
348
|
|
|
349
|
+
/**
|
|
350
|
+
* True when two hook-directory paths refer to the same location (#656), tolerant
|
|
351
|
+
* of forward/back slashes, trailing separators, and case differences on Windows.
|
|
352
|
+
* @param {string} a
|
|
353
|
+
* @param {string} b
|
|
354
|
+
* @returns {boolean}
|
|
355
|
+
*/
|
|
356
|
+
function sameHooksPath(a, b) {
|
|
357
|
+
const norm = (p) => {
|
|
358
|
+
let r = path.resolve(p);
|
|
359
|
+
try { r = fs.realpathSync.native(r); } catch { /* path need not exist */ }
|
|
360
|
+
return process.platform === 'win32' ? r.toLowerCase() : r;
|
|
361
|
+
};
|
|
362
|
+
return norm(a) === norm(b);
|
|
363
|
+
}
|
|
364
|
+
|
|
345
365
|
/**
|
|
346
366
|
* Execute a single plan item (file/dir/gitconfig).
|
|
347
367
|
* @returns {boolean} true on success, false on skip/failure
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|
|
3
|
+
"version": "0.33.1",
|
|
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"
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"hooks/",
|
|
11
11
|
"adapters/",
|
|
12
12
|
"lib/",
|
|
13
|
+
"gates/",
|
|
13
14
|
"skills/",
|
|
14
15
|
"plugins/",
|
|
15
16
|
"dsh.bundle.patch.yml",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.1",
|
|
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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|
|
@@ -22,6 +22,32 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
## 提交前自检清单(P1 红线前移 — 2026-10-30,直接解 #652)
|
|
26
|
+
|
|
27
|
+
> 背景:47.6% 的提交是 fix/revert(323/679)。多数返工是"提交时才被门禁打回"——
|
|
28
|
+
> 红线原本只活在 `githooks/` 里,AI 在 BUILD 期间看不到。这一页把会**阻断提交**的
|
|
29
|
+
> 阈值提前到编码时自检,目标是"第一次提交就过",而不是"提交→被打回→改→再提交"。
|
|
30
|
+
|
|
31
|
+
**每次准备 `git commit` 前,逐条自检(按被拦频率排序):**
|
|
32
|
+
|
|
33
|
+
| # | 门禁 | 红线(会被 BLOCK/WARN 的硬阈值) | 自查方法 |
|
|
34
|
+
|---|------|------------------------------|----------|
|
|
35
|
+
| 1 | **Gate 3 CCN** | 单函数圈复杂度 **>10 BLOCK**、**>5 WARN** | 拆函数 / 早返回 / 抽策略表;`xp-gate check 3 <file>` |
|
|
36
|
+
| 2 | **Gate 6 Boy Scout** | **新文件 0 warning**;修改文件 warning 数不得高于 `.warnings-baseline.json`;**单函数 ≤50 行**;**嵌套 ≤4 层** | 新文件当"零容忍"写;改动前 `xp-gate check 6` 看基线 |
|
|
37
|
+
| 3 | **Gate 2 重复代码** | jscpd 相似度 **>5% BLOCK** | 抽公共函数,别复制粘贴;`xp-gate check 2 <dir>` |
|
|
38
|
+
| 4 | **Gate 4 Principles** | 14 条 Clean Code/SOLID 规则,任一 error/warning 即拦(按 `.principlesrc`) | `xp-gate check 4 <file>`;避免超长参数表、空 catch、`as any` |
|
|
39
|
+
| 5 | **Gate 1 Lint** | 适配器 lint 任一 error | 提交前跑一次项目 lint;不要留未用变量/导入 |
|
|
40
|
+
| 6 | **Gate 5 测试** | 测试全绿 + 覆盖率 ≥80%(新 .ts/.tsx 无测试直接 BLOCK) | 新代码先写测试(TDD);`npx vitest run <file>` |
|
|
41
|
+
| 7 | **mirror/sync 纪律** | 改 `githooks/` 或 `skills/` 后必须 `node src/npm-package/scripts/sync-package-content.js --fix` 且 `bash scripts/check-hook-mirror.sh` 报 PASS | 忘了同步会在 pre-push 的 mirror 校验被打回 |
|
|
42
|
+
|
|
43
|
+
**一条命令抢先看**:`xp-gate check . --changed --brief` 只跑"本次 diff 会触发的门禁",
|
|
44
|
+
一屏出 PASS/SKIP/BLOCK,目标 <5 s。把它放在 ralph-loop 每个 REQ 的 step 0,先看红不红,
|
|
45
|
+
再决定是否继续写——而不是写到一半才被 `git commit` 拦下。
|
|
46
|
+
|
|
47
|
+
**根因提醒**:Gate 3/6 是"返工双雄"。写完一个函数先扫一眼长度和嵌套,比提交后返工快得多。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
25
51
|
## BUILD Entry Contract Check (MANDATORY — v0.18.0+, 程序化)
|
|
26
52
|
|
|
27
53
|
**Purpose**: Validate `slices-manifest.json` schema and slice↔REQ cross-artifact consistency before entering BUILD.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|
|
3
|
+
"version": "0.33.1",
|
|
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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|
|
@@ -22,6 +22,32 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
## 提交前自检清单(P1 红线前移 — 2026-10-30,直接解 #652)
|
|
26
|
+
|
|
27
|
+
> 背景:47.6% 的提交是 fix/revert(323/679)。多数返工是"提交时才被门禁打回"——
|
|
28
|
+
> 红线原本只活在 `githooks/` 里,AI 在 BUILD 期间看不到。这一页把会**阻断提交**的
|
|
29
|
+
> 阈值提前到编码时自检,目标是"第一次提交就过",而不是"提交→被打回→改→再提交"。
|
|
30
|
+
|
|
31
|
+
**每次准备 `git commit` 前,逐条自检(按被拦频率排序):**
|
|
32
|
+
|
|
33
|
+
| # | 门禁 | 红线(会被 BLOCK/WARN 的硬阈值) | 自查方法 |
|
|
34
|
+
|---|------|------------------------------|----------|
|
|
35
|
+
| 1 | **Gate 3 CCN** | 单函数圈复杂度 **>10 BLOCK**、**>5 WARN** | 拆函数 / 早返回 / 抽策略表;`xp-gate check 3 <file>` |
|
|
36
|
+
| 2 | **Gate 6 Boy Scout** | **新文件 0 warning**;修改文件 warning 数不得高于 `.warnings-baseline.json`;**单函数 ≤50 行**;**嵌套 ≤4 层** | 新文件当"零容忍"写;改动前 `xp-gate check 6` 看基线 |
|
|
37
|
+
| 3 | **Gate 2 重复代码** | jscpd 相似度 **>5% BLOCK** | 抽公共函数,别复制粘贴;`xp-gate check 2 <dir>` |
|
|
38
|
+
| 4 | **Gate 4 Principles** | 14 条 Clean Code/SOLID 规则,任一 error/warning 即拦(按 `.principlesrc`) | `xp-gate check 4 <file>`;避免超长参数表、空 catch、`as any` |
|
|
39
|
+
| 5 | **Gate 1 Lint** | 适配器 lint 任一 error | 提交前跑一次项目 lint;不要留未用变量/导入 |
|
|
40
|
+
| 6 | **Gate 5 测试** | 测试全绿 + 覆盖率 ≥80%(新 .ts/.tsx 无测试直接 BLOCK) | 新代码先写测试(TDD);`npx vitest run <file>` |
|
|
41
|
+
| 7 | **mirror/sync 纪律** | 改 `githooks/` 或 `skills/` 后必须 `node src/npm-package/scripts/sync-package-content.js --fix` 且 `bash scripts/check-hook-mirror.sh` 报 PASS | 忘了同步会在 pre-push 的 mirror 校验被打回 |
|
|
42
|
+
|
|
43
|
+
**一条命令抢先看**:`xp-gate check . --changed --brief` 只跑"本次 diff 会触发的门禁",
|
|
44
|
+
一屏出 PASS/SKIP/BLOCK,目标 <5 s。把它放在 ralph-loop 每个 REQ 的 step 0,先看红不红,
|
|
45
|
+
再决定是否继续写——而不是写到一半才被 `git commit` 拦下。
|
|
46
|
+
|
|
47
|
+
**根因提醒**:Gate 3/6 是"返工双雄"。写完一个函数先扫一眼长度和嵌套,比提交后返工快得多。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
25
51
|
## BUILD Entry Contract Check (MANDATORY — v0.18.0+, 程序化)
|
|
26
52
|
|
|
27
53
|
**Purpose**: Validate `slices-manifest.json` schema and slice↔REQ cross-artifact consistency before entering BUILD.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|
|
@@ -22,6 +22,32 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
## 提交前自检清单(P1 红线前移 — 2026-10-30,直接解 #652)
|
|
26
|
+
|
|
27
|
+
> 背景:47.6% 的提交是 fix/revert(323/679)。多数返工是"提交时才被门禁打回"——
|
|
28
|
+
> 红线原本只活在 `githooks/` 里,AI 在 BUILD 期间看不到。这一页把会**阻断提交**的
|
|
29
|
+
> 阈值提前到编码时自检,目标是"第一次提交就过",而不是"提交→被打回→改→再提交"。
|
|
30
|
+
|
|
31
|
+
**每次准备 `git commit` 前,逐条自检(按被拦频率排序):**
|
|
32
|
+
|
|
33
|
+
| # | 门禁 | 红线(会被 BLOCK/WARN 的硬阈值) | 自查方法 |
|
|
34
|
+
|---|------|------------------------------|----------|
|
|
35
|
+
| 1 | **Gate 3 CCN** | 单函数圈复杂度 **>10 BLOCK**、**>5 WARN** | 拆函数 / 早返回 / 抽策略表;`xp-gate check 3 <file>` |
|
|
36
|
+
| 2 | **Gate 6 Boy Scout** | **新文件 0 warning**;修改文件 warning 数不得高于 `.warnings-baseline.json`;**单函数 ≤50 行**;**嵌套 ≤4 层** | 新文件当"零容忍"写;改动前 `xp-gate check 6` 看基线 |
|
|
37
|
+
| 3 | **Gate 2 重复代码** | jscpd 相似度 **>5% BLOCK** | 抽公共函数,别复制粘贴;`xp-gate check 2 <dir>` |
|
|
38
|
+
| 4 | **Gate 4 Principles** | 14 条 Clean Code/SOLID 规则,任一 error/warning 即拦(按 `.principlesrc`) | `xp-gate check 4 <file>`;避免超长参数表、空 catch、`as any` |
|
|
39
|
+
| 5 | **Gate 1 Lint** | 适配器 lint 任一 error | 提交前跑一次项目 lint;不要留未用变量/导入 |
|
|
40
|
+
| 6 | **Gate 5 测试** | 测试全绿 + 覆盖率 ≥80%(新 .ts/.tsx 无测试直接 BLOCK) | 新代码先写测试(TDD);`npx vitest run <file>` |
|
|
41
|
+
| 7 | **mirror/sync 纪律** | 改 `githooks/` 或 `skills/` 后必须 `node src/npm-package/scripts/sync-package-content.js --fix` 且 `bash scripts/check-hook-mirror.sh` 报 PASS | 忘了同步会在 pre-push 的 mirror 校验被打回 |
|
|
42
|
+
|
|
43
|
+
**一条命令抢先看**:`xp-gate check . --changed --brief` 只跑"本次 diff 会触发的门禁",
|
|
44
|
+
一屏出 PASS/SKIP/BLOCK,目标 <5 s。把它放在 ralph-loop 每个 REQ 的 step 0,先看红不红,
|
|
45
|
+
再决定是否继续写——而不是写到一半才被 `git commit` 拦下。
|
|
46
|
+
|
|
47
|
+
**根因提醒**:Gate 3/6 是"返工双雄"。写完一个函数先扫一眼长度和嵌套,比提交后返工快得多。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
25
51
|
## BUILD Entry Contract Check (MANDATORY — v0.18.0+, 程序化)
|
|
26
52
|
|
|
27
53
|
**Purpose**: Validate `slices-manifest.json` schema and slice↔REQ cross-artifact consistency before entering BUILD.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|
|
3
|
+
"version": "0.33.1",
|
|
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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** e713c2d1
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.33.1.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.
|