@boyingliu01/xp-gate 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/gate-13-compiler-strictness.sh +129 -4
- package/gate-3.sh +46 -2
- package/gate-4.sh +28 -4
- package/hooks/gate-13-compiler-strictness.sh +129 -4
- package/hooks/gate-3.sh +46 -2
- package/hooks/gate-4.sh +28 -4
- package/hooks/lib/ccn-ratchet.sh +109 -0
- package/lib/__tests__/doctor-timeout-partial.test.js +79 -0
- package/lib/__tests__/foreign-hooks-global.test.js +125 -0
- package/lib/__tests__/uninstall-global-residue.test.js +160 -0
- package/lib/doctor.js +130 -3
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/uninstall.js +225 -1
- 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/principles/rules/solid/dip.ts +94 -14
- 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/uninstall.js
CHANGED
|
@@ -8,10 +8,41 @@ const {
|
|
|
8
8
|
TEMPLATE_DIR,
|
|
9
9
|
GLOBAL_HOOKS_DIR,
|
|
10
10
|
GLOBAL_ADAPTERS_DIR,
|
|
11
|
+
GLOBAL_MODULES_DIR,
|
|
11
12
|
} = require('./shared-paths.js');
|
|
12
13
|
|
|
13
14
|
const BACKUP_DIR = path.join(CONFIG_DIR, '.uninstall-backup');
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Skill directory roots the CLI installs into, per platform (mirror of the
|
|
18
|
+
* installers in install-skill.js). Read at call time so a test can point HOME at a
|
|
19
|
+
* throwaway tree.
|
|
20
|
+
*/
|
|
21
|
+
function globalSkillRoots() {
|
|
22
|
+
return [
|
|
23
|
+
path.join(HOME_DIR(), '.qoder', 'skills'),
|
|
24
|
+
path.join(HOME_DIR(), '.config', 'opencode', 'skills'),
|
|
25
|
+
path.join(HOME_DIR(), '.claude', 'skills'),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Directories inside ~/.config/xp-gate/ that `setup-global` creates and that
|
|
31
|
+
* `uninstall` used to leave behind (#663).
|
|
32
|
+
*/
|
|
33
|
+
const GLOBAL_INSTALL_RESIDUE = [
|
|
34
|
+
{ key: 'modules', label: 'modules/' },
|
|
35
|
+
{ key: 'node_modules', label: 'node_modules/' },
|
|
36
|
+
{ key: 'package.json', label: 'package.json' },
|
|
37
|
+
{ key: 'package-lock.json', label: 'package-lock.json' },
|
|
38
|
+
{ key: 'xp-gate.json', label: 'xp-gate.json' },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/** User DATA that must survive an uninstall, and must be reported as retained. */
|
|
42
|
+
const GLOBAL_USER_DATA = [
|
|
43
|
+
{ key: '.delphi-config.json', label: '.delphi-config.json (your Delphi expert config)' },
|
|
44
|
+
];
|
|
45
|
+
|
|
15
46
|
/**
|
|
16
47
|
* Signature strings used to verify file ownership.
|
|
17
48
|
* Each hook/adapter file contains a unique marker string.
|
|
@@ -276,6 +307,44 @@ function buildPlan(mode) {
|
|
|
276
307
|
critical: true
|
|
277
308
|
});
|
|
278
309
|
}
|
|
310
|
+
|
|
311
|
+
// #663: `setup-global` also writes modules/, a node_modules/ tree, a
|
|
312
|
+
// package.json + package-lock.json and the xp-gate.json manifest. None of them
|
|
313
|
+
// were in the plan, so uninstall printed "Uninstall complete" over an
|
|
314
|
+
// installation that was still ~1000 files on disk. The residue is not
|
|
315
|
+
// cosmetic: a leftover node_modules/ makes the NEXT setup-global skip its own
|
|
316
|
+
// install and reuse a stale dependency tree, and a leftover xp-gate.json
|
|
317
|
+
// carries the previous version's manifest/lastInit into `doctor`, which then
|
|
318
|
+
// reports it as the current installation.
|
|
319
|
+
const configDir = CONFIG_DIR;
|
|
320
|
+
for (const item of GLOBAL_INSTALL_RESIDUE) {
|
|
321
|
+
const target = path.join(configDir, item.key);
|
|
322
|
+
if (fs.existsSync(target)) {
|
|
323
|
+
const isDir = fs.statSync(target).isDirectory();
|
|
324
|
+
plan.push({
|
|
325
|
+
type: isDir ? 'dir' : 'file',
|
|
326
|
+
path: target,
|
|
327
|
+
label: `~/.config/xp-gate/${item.label}`,
|
|
328
|
+
critical: false
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// User data is deliberately NOT deleted. Reporting it is the second half of
|
|
334
|
+
// the fix: leaving it behind silently is how a user ends up with a stray
|
|
335
|
+
// config they cannot attribute.
|
|
336
|
+
for (const item of GLOBAL_USER_DATA) {
|
|
337
|
+
const target = path.join(configDir, item.key);
|
|
338
|
+
if (fs.existsSync(target)) {
|
|
339
|
+
plan.push({
|
|
340
|
+
type: 'retain',
|
|
341
|
+
retain: true,
|
|
342
|
+
path: target,
|
|
343
|
+
label: item.label,
|
|
344
|
+
critical: false
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
279
348
|
}
|
|
280
349
|
|
|
281
350
|
// Template dir is common to both modes
|
|
@@ -318,6 +387,30 @@ function removeFile(item, config) {
|
|
|
318
387
|
*/
|
|
319
388
|
function removeDir(item) {
|
|
320
389
|
if (!fs.existsSync(item.path)) return false;
|
|
390
|
+
|
|
391
|
+
// #664: the global `core.hooksPath` is a SHARED directory -- `git lfs install`
|
|
392
|
+
// writes its own hooks into it -- so a blind recursive delete of a hooks dir
|
|
393
|
+
// removes another tool's working files. Classify first; when foreign content is
|
|
394
|
+
// present, delete only xp-gate's own entries and say what was kept.
|
|
395
|
+
//
|
|
396
|
+
// Restricted to directories that ARE a hooks dir. The template dir and the
|
|
397
|
+
// adapters dir are wholly xp-gate-owned (the template legitimately contains an
|
|
398
|
+
// `adapters/` subdir, which would otherwise be read as foreign), so only a path
|
|
399
|
+
// that looks like a hooks root gets the conservative treatment.
|
|
400
|
+
const isHooksDir = /(^|[\/])hooks$/.test(item.path) || item.label.includes('hooks/');
|
|
401
|
+
const removal = isHooksDir ? planHooksDirRemoval(item.path) : { action: 'remove-all' };
|
|
402
|
+
if (removal.action === 'remove-owned-only') {
|
|
403
|
+
for (const name of removal.owned) {
|
|
404
|
+
fs.rmSync(path.join(item.path, name), { recursive: true, force: true });
|
|
405
|
+
}
|
|
406
|
+
try { fs.rmdirSync(item.path); } catch { /* non-empty: foreign files remain */ }
|
|
407
|
+
const sources = removal.foreign.map((f) => (f.source ? `${f.name} (${f.source})` : f.name));
|
|
408
|
+
console.log(` Removed xp-gate hooks from ${item.label}`);
|
|
409
|
+
console.log(` ⚠️ Kept ${removal.foreign.length} file(s) that are not xp-gate's: ${sources.join(', ')}`);
|
|
410
|
+
console.log(' The global core.hooksPath is a shared directory; other tools (e.g. git lfs) install here.');
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
|
|
321
414
|
fs.rmSync(item.path, { recursive: true, force: true });
|
|
322
415
|
console.log(` Removed ${item.label}`);
|
|
323
416
|
return true;
|
|
@@ -528,4 +621,135 @@ async function uninstall(args) {
|
|
|
528
621
|
return 0;
|
|
529
622
|
}
|
|
530
623
|
|
|
531
|
-
|
|
624
|
+
/**
|
|
625
|
+
* Decide which installed skill directories an uninstall may remove (#663).
|
|
626
|
+
*
|
|
627
|
+
* `uninstall` never touched skills at all, so 12 of them stayed in
|
|
628
|
+
* ~/.qoder/skills/ and ~/.config/opencode/skills/ after `uninstall --global`, and
|
|
629
|
+
* the next `init` treated them as already installed.
|
|
630
|
+
*
|
|
631
|
+
* Safety rule from the issue: only remove a directory whose content still matches
|
|
632
|
+
* the bundled copy. A user's edits are not residue, and deleting them is a far
|
|
633
|
+
* worse outcome than leaving a stale skill behind -- the user can always delete a
|
|
634
|
+
* leftover, but cannot recover an edit we removed.
|
|
635
|
+
*
|
|
636
|
+
* Returns [{ path, label }] for the directories that are safe to delete.
|
|
637
|
+
*/
|
|
638
|
+
function planSkillRemovals(skillNames, installedRoot, bundledRoot) {
|
|
639
|
+
const removals = [];
|
|
640
|
+
if (!Array.isArray(skillNames)) return removals;
|
|
641
|
+
|
|
642
|
+
for (const name of skillNames) {
|
|
643
|
+
if (typeof name !== 'string' || !name) continue;
|
|
644
|
+
const installedDir = path.join(installedRoot, name);
|
|
645
|
+
const bundledDir = path.join(bundledRoot, name);
|
|
646
|
+
if (!fs.existsSync(installedDir)) continue;
|
|
647
|
+
// No bundled copy to compare against -> cannot prove it is untouched, so keep it.
|
|
648
|
+
if (!fs.existsSync(bundledDir)) continue;
|
|
649
|
+
if (directoryContentMatches(installedDir, bundledDir)) {
|
|
650
|
+
removals.push({ path: installedDir, label: `${installedRoot}/${name}/` });
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return removals;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* True when two directory trees hold the same files with the same bytes.
|
|
658
|
+
* Skips `.git` and node_modules, which are never part of a skill bundle.
|
|
659
|
+
*/
|
|
660
|
+
function directoryContentMatches(a, b) {
|
|
661
|
+
const filesA = listFilesRelative(a);
|
|
662
|
+
const filesB = listFilesRelative(b);
|
|
663
|
+
if (filesA.length !== filesB.length) return false;
|
|
664
|
+
for (let i = 0; i < filesA.length; i++) {
|
|
665
|
+
if (filesA[i] !== filesB[i]) return false;
|
|
666
|
+
try {
|
|
667
|
+
if (!fs.readFileSync(path.join(a, filesA[i])).equals(fs.readFileSync(path.join(b, filesB[i])))) {
|
|
668
|
+
return false;
|
|
669
|
+
}
|
|
670
|
+
} catch {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/** Sorted relative file paths under a directory. */
|
|
678
|
+
function listFilesRelative(root) {
|
|
679
|
+
const out = [];
|
|
680
|
+
const walk = (dir, prefix) => {
|
|
681
|
+
let entries;
|
|
682
|
+
try {
|
|
683
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
684
|
+
} catch {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
for (const entry of entries) {
|
|
688
|
+
if (entry.name === '.git' || entry.name === 'node_modules') continue;
|
|
689
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
690
|
+
if (entry.isDirectory()) {
|
|
691
|
+
walk(path.join(dir, entry.name), rel);
|
|
692
|
+
} else {
|
|
693
|
+
out.push(rel);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
walk(root, '');
|
|
698
|
+
return out.sort();
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Decide how a hooks directory may be removed (#664).
|
|
703
|
+
*
|
|
704
|
+
* `uninstall` did `rmSync(GLOBAL_HOOKS_DIR, {recursive: true})`. The global
|
|
705
|
+
* `core.hooksPath` is a SHARED directory: `git lfs install` writes its own hooks
|
|
706
|
+
* there, and a recursive delete took them with it. The user then had a broken
|
|
707
|
+
* git-lfs setup and no idea which command caused it — deleting another tool's
|
|
708
|
+
* working files is a worse outcome than leaving residue the user can see.
|
|
709
|
+
*
|
|
710
|
+
* Returns:
|
|
711
|
+
* { action: 'remove-all', foreign: [] } — only xp-gate content, safe
|
|
712
|
+
* { action: 'remove-owned-only', foreign } — foreign files present, keep them
|
|
713
|
+
* { action: 'absent', foreign: [] } — nothing to do
|
|
714
|
+
*/
|
|
715
|
+
function planHooksDirRemoval(hooksDir) {
|
|
716
|
+
if (!fs.existsSync(hooksDir)) return { action: 'absent', foreign: [] };
|
|
717
|
+
|
|
718
|
+
const foreign = [];
|
|
719
|
+
const owned = [];
|
|
720
|
+
let entries;
|
|
721
|
+
try {
|
|
722
|
+
entries = fs.readdirSync(hooksDir, { withFileTypes: true });
|
|
723
|
+
} catch {
|
|
724
|
+
// Unreadable is not "empty": report it as foreign so nothing is deleted blind.
|
|
725
|
+
return { action: 'remove-owned-only', foreign: [{ name: hooksDir, source: null }] };
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
for (const entry of entries) {
|
|
729
|
+
const name = entry.name;
|
|
730
|
+
// xp-gate's own hook entry points and support files.
|
|
731
|
+
if (OWNED_HOOK_NAMES.includes(name) || name.endsWith('.sh') || name === 'lib') {
|
|
732
|
+
owned.push(name);
|
|
733
|
+
} else {
|
|
734
|
+
foreign.push({ name, source: foreignHookSource(path.join(hooksDir, name)) });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return foreign.length > 0
|
|
739
|
+
? { action: 'remove-owned-only', foreign, owned }
|
|
740
|
+
: { action: 'remove-all', foreign: [], owned };
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/** Hook entry points xp-gate installs. */
|
|
744
|
+
const OWNED_HOOK_NAMES = ['pre-commit', 'pre-push', 'post-merge'];
|
|
745
|
+
|
|
746
|
+
/** 'git-lfs' when the file proves it, else null. Content-based: see doctor.js. */
|
|
747
|
+
function foreignHookSource(filePath) {
|
|
748
|
+
try {
|
|
749
|
+
return /\bgit\s+lfs\b/.test(fs.readFileSync(filePath, 'utf8')) ? 'git-lfs' : null;
|
|
750
|
+
} catch {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
module.exports = { uninstall, isXpGateFile, SIGNATURES, buildPlan, planSkillRemovals, planHooksDirRemoval };
|
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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|
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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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": "xp-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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.35.0",
|
|
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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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": "xp-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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.
|
|
@@ -21,19 +21,80 @@ function shouldSkipInstantiation(className: string): boolean {
|
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Composition-root markers (#627): a class whose JOB is to construct and wire
|
|
26
|
+
* concrete implementations. `new` there is dependency injection working, not being
|
|
27
|
+
* bypassed -- measured on a real 178-file Java repo, all 29 `solid.dip` warnings
|
|
28
|
+
* landed on 9 such wiring points.
|
|
29
|
+
*
|
|
30
|
+
* Matched against the class body text rather than a parsed AST because the rule
|
|
31
|
+
* consumes whatever each language adapter emits as "class code", and those shapes
|
|
32
|
+
* differ. These markers are conventions with low ambiguity, which is what makes a
|
|
33
|
+
* text test acceptable here.
|
|
34
|
+
*/
|
|
35
|
+
const COMPOSITION_ROOT_MARKERS: RegExp[] = [
|
|
36
|
+
// Spring / Quarkus / Micronaut configuration and bean producers.
|
|
37
|
+
/@Configuration\b/, /@Bean\b/, /@Provides\b/, /@Produces\b/, /@Factory\b/,
|
|
38
|
+
// DI module definitions (Dagger / Guice / Spring Java Config).
|
|
39
|
+
/@Module\b/, /extends\s+AbstractModule\b/, /implements\s+Module\b/,
|
|
40
|
+
// Framework lifecycle entry points: the framework built the object, so wiring
|
|
41
|
+
// belongs in these methods.
|
|
42
|
+
/\bvoid\s+init\s*\(/, /\bvoid\s+initialize\s*\(/, /\bvoid\s+afterPropertiesSet\s*\(/,
|
|
43
|
+
/\bvoid\s+onCreate\s*\(/,
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/** A static factory method -- the class is an assembler by construction. */
|
|
47
|
+
const STATIC_FACTORY_METHOD = /\bstatic\s+[\w<>\[\],.?\s]+\s+\w+\s*\(/;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* True when this class body is a composition root.
|
|
51
|
+
*
|
|
52
|
+
* A class whose name ends in Factory is already handled by
|
|
53
|
+
* shouldSkipInstantiation for its PRODUCT names; this covers the assEMBLER itself,
|
|
54
|
+
* e.g. `PlanCollectorFactory.assemble()` which news eight collaborators.
|
|
55
|
+
*/
|
|
56
|
+
function isCompositionRoot(code: string, className?: string): boolean {
|
|
57
|
+
if (className && /(?:Factory|Assembler|Module|Config|Configuration|Provider)$/.test(className)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (COMPOSITION_ROOT_MARKERS.some((marker) => marker.test(code))) return true;
|
|
61
|
+
return STATIC_FACTORY_METHOD.test(code);
|
|
62
|
+
}
|
|
63
|
+
|
|
24
64
|
/** `new Foo(` -> `Foo`. */
|
|
25
65
|
function classNameFromNewMatch(match: string): string {
|
|
26
66
|
return match.replace(/new\s+/, '').replace(/\s*\(/, '');
|
|
27
67
|
}
|
|
28
68
|
|
|
29
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Violations for one class body: each `new X(` that is not exempt.
|
|
71
|
+
*
|
|
72
|
+
* #626: every violation used to carry the CLASS DECLARATION line, because the
|
|
73
|
+
* adapter only reports where the class starts and the match itself carried no
|
|
74
|
+
* position. A 200-line class with one instantiation on line 187 therefore pointed
|
|
75
|
+
* the developer at line 1 -- the message named the right file and the wrong place,
|
|
76
|
+
* which is the least useful combination: it looks actionable and is not.
|
|
77
|
+
*
|
|
78
|
+
* The offset of each match within the class body is now added to the class's
|
|
79
|
+
* starting line, so the reported line is the `new` expression itself. Multi-line
|
|
80
|
+
* matches count their newlines, since a match may span `new Foo(\n arg,\n)`.
|
|
81
|
+
*
|
|
82
|
+
* `classBodyStartLine` is the line the adapter says the class begins on; when the
|
|
83
|
+
* adapter cannot say (0), the violation keeps 0 rather than inventing line 1 --
|
|
84
|
+
* a placeholder 1 would look like a real location.
|
|
85
|
+
*/
|
|
30
86
|
function instantiationViolations(
|
|
31
|
-
code: string,
|
|
87
|
+
code: string, classBodyStartLine: number, file: string, severity: Severity,
|
|
32
88
|
): Violation[] {
|
|
33
89
|
const violations: Violation[] = [];
|
|
34
|
-
|
|
35
|
-
|
|
90
|
+
const newPattern = /new\s+(\w+)\s*\(/g;
|
|
91
|
+
let match: RegExpExecArray | null;
|
|
92
|
+
while ((match = newPattern.exec(code)) !== null) {
|
|
93
|
+
const className = classNameFromNewMatch(match[0]);
|
|
36
94
|
if (shouldSkipInstantiation(className)) continue;
|
|
95
|
+
// Offset -> line: count the newlines between the class start and this match.
|
|
96
|
+
const linesIntoBody = (code.slice(0, match.index).match(/\n/g) || []).length;
|
|
97
|
+
const line = classBodyStartLine > 0 ? classBodyStartLine + linesIntoBody : 0;
|
|
37
98
|
violations.push({
|
|
38
99
|
file,
|
|
39
100
|
line,
|
|
@@ -45,21 +106,40 @@ function instantiationViolations(
|
|
|
45
106
|
return violations;
|
|
46
107
|
}
|
|
47
108
|
|
|
109
|
+
/** Rule severity from the active config, defaulting to warning. */
|
|
110
|
+
function configuredSeverity(): Severity {
|
|
111
|
+
const settings = getActiveConfig().rules['solid']['dip'];
|
|
112
|
+
return (settings.severity as Severity) ?? 'warning';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Violations contributed by ONE extracted class.
|
|
117
|
+
*
|
|
118
|
+
* Split out of `dipRule.check` to keep that function inside the CCN ratchet: the
|
|
119
|
+
* #627 composition-root test and the #626 line arithmetic each added a branch, and
|
|
120
|
+
* the ratchet is there to force exactly this trade rather than a baseline refresh.
|
|
121
|
+
*/
|
|
122
|
+
function classViolations(
|
|
123
|
+
clsAny: unknown, file: string, severity: Severity,
|
|
124
|
+
): Violation[] {
|
|
125
|
+
const cls = clsAny as { code?: string; line?: number; name?: string };
|
|
126
|
+
if (!cls.code) return [];
|
|
127
|
+
// #627: a composition root's `new` is the inversion working, not bypassed.
|
|
128
|
+
// Skipping the whole class body is right here: when the class IS the wiring
|
|
129
|
+
// point, every instantiation in it is wiring.
|
|
130
|
+
if (isCompositionRoot(cls.code, cls.name)) return [];
|
|
131
|
+
return instantiationViolations(cls.code, cls.line || 0, file, severity);
|
|
132
|
+
}
|
|
133
|
+
|
|
48
134
|
export const dipRule: Rule = {
|
|
49
135
|
id: 'solid.dip',
|
|
50
136
|
name: 'Dependency Inversion Principle Rule',
|
|
51
137
|
threshold: 0,
|
|
52
138
|
severity: 'warning',
|
|
53
139
|
check: (file: string, adapter: import('../../types').Adapter): Violation[] => {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
for (const clsAny of adapter.extractClasses() || []) {
|
|
59
|
-
const cls = clsAny as { code?: string; line?: number };
|
|
60
|
-
if (!cls.code) continue;
|
|
61
|
-
violations.push(...instantiationViolations(cls.code, cls.line || 0, file, severity));
|
|
62
|
-
}
|
|
63
|
-
return violations;
|
|
140
|
+
const severity = configuredSeverity();
|
|
141
|
+
return (adapter.extractClasses() || []).flatMap((clsAny) =>
|
|
142
|
+
classViolations(clsAny, file, severity),
|
|
143
|
+
);
|
|
64
144
|
}
|
|
65
145
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.0.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:** c8a73b2a
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|