@boyingliu01/xp-gate 0.34.1 → 0.35.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 +8 -0
- package/gate-3.sh +46 -2
- package/gate-4.sh +20 -3
- package/hooks/gate-3.sh +46 -2
- package/hooks/gate-4.sh +20 -3
- package/hooks/lib/ccn-ratchet.sh +109 -0
- package/hooks/pre-commit +92 -0
- package/lib/__tests__/doctor-timeout-partial.test.js +79 -0
- package/lib/__tests__/rework.test.js +127 -0
- package/lib/doctor.js +60 -3
- package/lib/gate-audit.ts +8 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/rework.js +262 -0
- 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/scripts/__tests__/sync-package-content-isolation.test.js +73 -0
- package/scripts/__tests__/sync-package-content.test.js +109 -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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @test REQ-645
|
|
3
|
+
* @intent `sync-package-content.test.js` used the REAL repository root as the
|
|
4
|
+
* spawn cwd:
|
|
5
|
+
*
|
|
6
|
+
* const WORKTREE_PATH = path.resolve(__dirname, '..', '..', '..', '..');
|
|
7
|
+
*
|
|
8
|
+
* Four levels up from `src/npm-package/scripts/__tests__/` is the repo
|
|
9
|
+
* root, not a temp dir. The tests then ran the real sync script with
|
|
10
|
+
* `cwd: WORKTREE_PATH`, so the script's writes landed in the repository
|
|
11
|
+
* instead of a sandbox. CI's Gate 5 failed at `vitest-worktree-guard`:
|
|
12
|
+
*
|
|
13
|
+
* Work tree polluted by the test run — 1 path(s) the run introduced:
|
|
14
|
+
* M src/npm-package/mock-policy/gate-m3.ts
|
|
15
|
+
*
|
|
16
|
+
* The reason this is easy to miss: the pollution is CONDITIONAL. The
|
|
17
|
+
* script's writes are idempotent, so when the mirror already matches the
|
|
18
|
+
* source nothing changes and the run looks clean. It only dirties the tree
|
|
19
|
+
* when the checked-out commit happens to carry a stale mirror — which is
|
|
20
|
+
* exactly when CI is checking out a commit that the fix was supposed to
|
|
21
|
+
* validate. So it presents as "flaky, just re-run it", and re-running
|
|
22
|
+
* hides the cause while leaving the latent defect in place.
|
|
23
|
+
*
|
|
24
|
+
* The guard catching it is good, but it catches the CONSEQUENCE. This file
|
|
25
|
+
* pins the CAUSE: a test may not write into the repository.
|
|
26
|
+
* @covers AC-670-54, AC-670-55
|
|
27
|
+
*/
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
|
|
33
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
|
34
|
+
const TEST_FILE = path.join(
|
|
35
|
+
REPO_ROOT, 'src', 'npm-package', 'scripts', '__tests__', 'sync-package-content.test.js',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
function readSuite() {
|
|
39
|
+
return fs.readFileSync(TEST_FILE, 'utf8');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('sync-package-content tests must not write into the repository (#645)', () => {
|
|
43
|
+
it('AC-670-54: no test uses the repo root as a spawn cwd', () => {
|
|
44
|
+
const source = readSuite();
|
|
45
|
+
// The exact offending expression. `path.resolve(__dirname, '..' x4)` from the
|
|
46
|
+
// test file IS the repo root -- there is no temp dir anywhere in that chain.
|
|
47
|
+
const offenders = source
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map((line, i) => ({ line: line.trim(), n: i + 1 }))
|
|
50
|
+
.filter((x) => /WORKTREE_PATH\s*=\s*path\.resolve\(__dirname/.test(x.line));
|
|
51
|
+
expect(
|
|
52
|
+
offenders,
|
|
53
|
+
`tests still anchor on the real repo root:\n${offenders.map((o) => ` :${o.n} ${o.line}`).join('\n')}`,
|
|
54
|
+
).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('AC-670-55: the sync tests run against a temp worktree, not the checkout', () => {
|
|
58
|
+
const source = readSuite();
|
|
59
|
+
// Whatever the variable is called, the cwd handed to a spawned sync must be
|
|
60
|
+
// one the test CREATED. mkdtemp is the only way to guarantee that: a path
|
|
61
|
+
// built from __dirname is by construction inside the repository.
|
|
62
|
+
expect(source, 'suite must create a throwaway worktree').toMatch(/mkdtemp/);
|
|
63
|
+
// Every spawnSync `cwd:` must point at that temp root, never at the checkout.
|
|
64
|
+
const cwdLines = source
|
|
65
|
+
.split('\n')
|
|
66
|
+
.filter((line) => /^\s*cwd:\s*/.test(line))
|
|
67
|
+
.map((line) => line.trim());
|
|
68
|
+
expect(cwdLines.length, 'expected at least one spawn cwd').toBeGreaterThan(0);
|
|
69
|
+
for (const line of cwdLines) {
|
|
70
|
+
expect(line, `spawn cwd escapes the sandbox: ${line}`).not.toMatch(/cwd:\s*REPO_ROOT/);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -27,6 +27,37 @@ function writeDoc(filePath, content) {
|
|
|
27
27
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Copy the canonical source trees a sync run READS into `destRoot` (#645).
|
|
32
|
+
*
|
|
33
|
+
* The sync script mirrors githooks/, skills/, plugins/ and every src/ module into
|
|
34
|
+
* the package. Seeding the whole of `src/` except the package itself keeps this
|
|
35
|
+
* list from rotting: syncModules() gains a module, this helper needs no edit.
|
|
36
|
+
* Only reads from the real checkout -- nothing is ever written back to it.
|
|
37
|
+
*/
|
|
38
|
+
function seedCanonicalSources(realRoot, destRoot) {
|
|
39
|
+
const real = realRoot || path.resolve(__dirname, '..', '..', '..', '..');
|
|
40
|
+
const dest = destRoot;
|
|
41
|
+
for (const tree of ['githooks', 'skills', 'plugins']) {
|
|
42
|
+
const from = path.join(real, tree);
|
|
43
|
+
if (fs.existsSync(from)) fs.cpSync(from, path.join(dest, tree), { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
const srcRoot = path.join(real, 'src');
|
|
46
|
+
for (const name of fs.readdirSync(srcRoot)) {
|
|
47
|
+
// The package itself is the DESTINATION of the sync, not a canonical source;
|
|
48
|
+
// copying it would let the test compare a mirror against a stale mirror.
|
|
49
|
+
if (name === 'npm-package') continue;
|
|
50
|
+
const from = path.join(srcRoot, name);
|
|
51
|
+
if (fs.statSync(from).isDirectory()) {
|
|
52
|
+
fs.cpSync(from, path.join(dest, 'src', name), { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
fs.mkdirSync(path.join(dest, 'src', 'npm-package', 'scripts'), { recursive: true });
|
|
56
|
+
for (const rel of ['scripts/sync-package-content.js', 'package.json']) {
|
|
57
|
+
fs.copyFileSync(path.join(srcRoot, 'npm-package', rel), path.join(dest, 'src', 'npm-package', rel));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
30
61
|
describe('checkDocsDrift', () => {
|
|
31
62
|
let tempDir;
|
|
32
63
|
let preCommitPath;
|
|
@@ -269,12 +300,56 @@ describe('syncGateScripts', () => {
|
|
|
269
300
|
* @covers AC-450-01, AC-450-02, AC-450-03, AC-450-04
|
|
270
301
|
*/
|
|
271
302
|
describe('syncHooks', () => {
|
|
272
|
-
|
|
303
|
+
// #645: run the sync against a THROWAWAY worktree, never the checkout.
|
|
304
|
+
//
|
|
305
|
+
// This block used to spawn the real sync script with
|
|
306
|
+
// cwd: path.resolve(__dirname, '..', '..', '..', '..')
|
|
307
|
+
// -- four levels up from src/npm-package/scripts/__tests__ is the REPOSITORY
|
|
308
|
+
// ROOT, not a temp dir -- so the script's writes landed in the repo and CI's
|
|
309
|
+
// Gate 5 failed at vitest-worktree-guard with
|
|
310
|
+
// Work tree polluted by the test run: M src/npm-package/mock-policy/gate-m3.ts
|
|
311
|
+
//
|
|
312
|
+
// The pollution was CONDITIONAL (the writes are idempotent, so it only dirties
|
|
313
|
+
// the tree when the checked-out commit carries a stale mirror), which made it
|
|
314
|
+
// look flaky and encouraged "just re-run it" -- hiding the cause. The guard
|
|
315
|
+
// catches the consequence; a temp cwd removes the cause.
|
|
316
|
+
const REAL_REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
|
317
|
+
const TMP_WORKTREE = fs.mkdtempSync(path.join(os.tmpdir(), 'xp-gate-sync-hooks-'));
|
|
318
|
+
const WORKTREE_PATH = TMP_WORKTREE;
|
|
273
319
|
const PKG_ROOT = path.join(WORKTREE_PATH, 'src', 'npm-package');
|
|
274
320
|
const HOOKS_DIR = path.join(PKG_ROOT, 'hooks');
|
|
275
321
|
const syncScript = path.join(WORKTREE_PATH, 'src', 'npm-package', 'scripts', 'sync-package-content.js');
|
|
276
322
|
const nodeExe = process.execPath;
|
|
277
323
|
|
|
324
|
+
/**
|
|
325
|
+
* Lay out just enough of the repo shape inside the temp root for the sync script
|
|
326
|
+
* to read its canonical sources and write its mirrors. Copies only the trees the
|
|
327
|
+
* script touches: githooks/ (the hook source of truth) and the packages it
|
|
328
|
+
* mirrors into. The REAL checkout is only ever READ.
|
|
329
|
+
*/
|
|
330
|
+
function seedTempWorktree() {
|
|
331
|
+
seedCanonicalSources(REAL_REPO_ROOT, WORKTREE_PATH);
|
|
332
|
+
fs.mkdirSync(path.join(PKG_ROOT, 'scripts'), { recursive: true });
|
|
333
|
+
fs.copyFileSync(
|
|
334
|
+
path.join(REAL_REPO_ROOT, 'src', 'npm-package', 'scripts', 'sync-package-content.js'),
|
|
335
|
+
syncScript,
|
|
336
|
+
);
|
|
337
|
+
// The script resolves its package root relative to its own location, so it
|
|
338
|
+
// must sit at <temp>/src/npm-package/scripts/ exactly as in the real tree.
|
|
339
|
+
fs.copyFileSync(
|
|
340
|
+
path.join(REAL_REPO_ROOT, 'src', 'npm-package', 'package.json'),
|
|
341
|
+
path.join(PKG_ROOT, 'package.json'),
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
beforeAll(() => {
|
|
346
|
+
seedTempWorktree();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
afterAll(() => {
|
|
350
|
+
fs.rmSync(TMP_WORKTREE, { recursive: true, force: true });
|
|
351
|
+
});
|
|
352
|
+
|
|
278
353
|
// Ensure hooks are re-synced once before tests run; share the result.
|
|
279
354
|
let syncOk = false;
|
|
280
355
|
beforeAll(() => {
|
|
@@ -473,9 +548,41 @@ describe('checkAdapterDrift', () => {
|
|
|
473
548
|
const { spawnSync } = require('child_process');
|
|
474
549
|
|
|
475
550
|
describe('sync-package-content subprocess invocation', () => {
|
|
476
|
-
|
|
551
|
+
// #645: same rule as the syncHooks block above -- a test may not write into the
|
|
552
|
+
// repository. `path.resolve(__dirname, '..', '..', '..', '..')` IS the repo root,
|
|
553
|
+
// so spawning the sync script with it as cwd copied mirrors over the checkout and
|
|
554
|
+
// CI's Gate 5 failed at vitest-worktree-guard.
|
|
555
|
+
//
|
|
556
|
+
// This block additionally DELETES package directories to prove the sync
|
|
557
|
+
// recreates them, which is precisely the operation that must never touch a real
|
|
558
|
+
// checkout. Everything therefore runs inside a throwaway tree seeded from the
|
|
559
|
+
// real sources (read-only).
|
|
560
|
+
const REAL_REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..');
|
|
561
|
+
const TMP_WORKTREE = fs.mkdtempSync(path.join(os.tmpdir(), 'xp-gate-sync-sub-'));
|
|
562
|
+
const WORKTREE_PATH = TMP_WORKTREE;
|
|
477
563
|
const PKG_ROOT = path.join(WORKTREE_PATH, 'src', 'npm-package');
|
|
478
564
|
const REPO_ROOT = path.join(WORKTREE_PATH);
|
|
565
|
+
|
|
566
|
+
function seedTempWorktree() {
|
|
567
|
+
seedCanonicalSources(REAL_REPO_ROOT, WORKTREE_PATH);
|
|
568
|
+
fs.mkdirSync(path.join(PKG_ROOT, 'scripts'), { recursive: true });
|
|
569
|
+
fs.copyFileSync(
|
|
570
|
+
path.join(REAL_REPO_ROOT, 'src', 'npm-package', 'scripts', 'sync-package-content.js'),
|
|
571
|
+
path.join(PKG_ROOT, 'scripts', 'sync-package-content.js'),
|
|
572
|
+
);
|
|
573
|
+
fs.copyFileSync(
|
|
574
|
+
path.join(REAL_REPO_ROOT, 'src', 'npm-package', 'package.json'),
|
|
575
|
+
path.join(PKG_ROOT, 'package.json'),
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
beforeAll(() => {
|
|
580
|
+
seedTempWorktree();
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
afterAll(() => {
|
|
584
|
+
fs.rmSync(TMP_WORKTREE, { recursive: true, force: true });
|
|
585
|
+
});
|
|
479
586
|
const CORE_SKILLS = [
|
|
480
587
|
'admin-template-guidelines',
|
|
481
588
|
'delphi-review',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 2ce96f13
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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:** 2ce96f13
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.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.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-10-30
|
|
4
|
-
**Commit:**
|
|
4
|
+
**Commit:** 2ce96f13
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.
|
|
6
|
+
**Version:** 0.35.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|