@boyingliu01/xp-gate 0.18.1 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/adapters/gate-3.sh +68 -0
  2. package/adapters/gate-4.sh +80 -0
  3. package/adapters/gate-7.sh +45 -0
  4. package/adapters/gate-8.sh +56 -0
  5. package/adapters/gate-9.sh +105 -0
  6. package/bin/xp-gate.js +45 -0
  7. package/hooks/pre-commit +108 -9
  8. package/hooks/pre-push +63 -232
  9. package/lib/__tests__/install-skill.test.js +3 -3
  10. package/lib/__tests__/test-alignment.test.ts +761 -0
  11. package/lib/install-skill.js +11 -3
  12. package/lib/mattpocock-license-verdict.json +1 -1
  13. package/lib/phase-transition.js +106 -1
  14. package/lib/sprint-init.js +41 -0
  15. package/lib/test-alignment.ts +651 -0
  16. package/mock-policy/AGENTS.md +3 -3
  17. package/mutation/AGENTS.md +3 -3
  18. package/mutation/gate-m.ts +18 -4
  19. package/package.json +1 -1
  20. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  21. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  22. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  23. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  24. package/plugins/opencode/package.json +1 -1
  25. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  26. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  27. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  28. package/plugins/qoder/plugin.json +1 -1
  29. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  30. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  31. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  32. package/principles/AGENTS.md +3 -3
  33. package/skills/delphi-review/AGENTS.md +3 -3
  34. package/skills/sprint-flow/AGENTS.md +3 -3
  35. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -30,10 +30,18 @@ function getCliVersion() {
30
30
  }
31
31
 
32
32
  const SKILLS_REGISTRY = {
33
- 'sprint-flow': { repo: 'boyingliu01/xp-gate', path: 'skills/sprint-flow' },
33
+ 'admin-template-guidelines': { repo: 'boyingliu01/xp-gate', path: 'skills/admin-template-guidelines' },
34
+ 'batch-grill-me': { repo: 'boyingliu01/xp-gate', path: 'skills/batch-grill-me' },
34
35
  'delphi-review': { repo: 'boyingliu01/xp-gate', path: 'skills/delphi-review' },
35
- 'test-spec': { repo: 'boyingliu01/xp-gate', path: 'skills/test-spec' },
36
- 'ralph-loop': { repo: 'boyingliu01/xp-gate', path: 'skills/ralph-loop' }
36
+ 'domain-modeling': { repo: 'boyingliu01/xp-gate', path: 'skills/domain-modeling' },
37
+ 'grill-with-docs': { repo: 'boyingliu01/xp-gate', path: 'skills/grill-with-docs' },
38
+ 'grilling': { repo: 'boyingliu01/xp-gate', path: 'skills/grilling' },
39
+ 'improve-codebase-architecture': { repo: 'boyingliu01/xp-gate', path: 'skills/improve-codebase-architecture' },
40
+ 'ralph-loop': { repo: 'boyingliu01/xp-gate', path: 'skills/ralph-loop' },
41
+ 'sprint-flow': { repo: 'boyingliu01/xp-gate', path: 'skills/sprint-flow' },
42
+ 'test-driven-development': { repo: 'boyingliu01/xp-gate', path: 'skills/test-driven-development' },
43
+ 'test-specification-alignment': { repo: 'boyingliu01/xp-gate', path: 'skills/test-specification-alignment' },
44
+ 'to-issues': { repo: 'boyingliu01/xp-gate', path: 'skills/to-issues' }
37
45
  };
38
46
 
39
47
  async function installSkill(name, options = {}) {
@@ -2,6 +2,6 @@
2
2
  "verdict": "PASS",
3
3
  "license_type": "MIT",
4
4
  "sha256": "0e7ac423bf2c6e223b7c5b156f8cf72da49d748e56a1641402c31f22ad07dbb5",
5
- "checked_at": "2026-07-27T08:52:37.842Z",
5
+ "checked_at": "2026-07-29T01:35:32.539Z",
6
6
  "source": "network"
7
7
  }
@@ -496,4 +496,109 @@ async function handlePhaseTransition(args = []) {
496
496
  }
497
497
  }
498
498
 
499
- module.exports = { handlePhaseTransition, renderDashboard, PHASE_NAMES, validateEvidence };
499
+ /**
500
+ * Read-only evidence check for a given phase. Validates evidence without modifying sprint state.
501
+ * D3 (Unify Evidence Validation): replaces sprint-gate.sh inline JSON parsing.
502
+ *
503
+ * @param {number} phase - Phase number (1-6)
504
+ * @param {string} projectDir - Project root directory
505
+ * @returns {{ ok: boolean, errors: string[], warnings: string[] }}
506
+ */
507
+ function checkEvidence(phase, projectDir) {
508
+ return validateEvidence(phase, projectDir);
509
+ }
510
+
511
+ /**
512
+ * Validate .code-walkthrough-result.json for code walkthrough mode.
513
+ * Ported from githooks/pre-push Gate MW inline jq logic.
514
+ *
515
+ * @param {string} projectDir - Project root directory
516
+ * @returns {{ ok: boolean, errors: string[], warnings: string[] }}
517
+ */
518
+ function checkWalkthrough(projectDir) {
519
+ const walkthroughFile = path.join(projectDir, '.code-walkthrough-result.json');
520
+ const errors = [];
521
+ const warnings = [];
522
+
523
+ if (!fs.existsSync(walkthroughFile)) {
524
+ errors.push('.code-walkthrough-result.json not found — run delphi-review --mode code-walkthrough before push');
525
+ return { ok: false, errors, warnings };
526
+ }
527
+
528
+ let data;
529
+ try {
530
+ data = JSON.parse(fs.readFileSync(walkthroughFile, 'utf8'));
531
+ } catch {
532
+ errors.push('.code-walkthrough-result.json is malformed JSON');
533
+ return { ok: false, errors, warnings };
534
+ }
535
+
536
+ // Check verdict
537
+ if (data.verdict !== 'APPROVED') {
538
+ errors.push(`Code walkthrough verdict is "${data.verdict}" — must be APPROVED`);
539
+ return { ok: false, errors, warnings };
540
+ }
541
+
542
+ // Check commit is ancestor of HEAD
543
+ if (data.commit) {
544
+ try {
545
+ execSync(`git merge-base --is-ancestor ${data.commit} HEAD`, {
546
+ cwd: projectDir, stdio: 'pipe',
547
+ });
548
+ } catch {
549
+ errors.push(`Code walkthrough commit ${data.commit} is not an ancestor of HEAD`);
550
+ return { ok: false, errors, warnings };
551
+ }
552
+ }
553
+
554
+ // Check expiration
555
+ if (data.expires) {
556
+ const expiry = new Date(data.expires).getTime();
557
+ if (Date.now() > expiry) {
558
+ errors.push(`Code walkthrough expired at ${data.expires}`);
559
+ return { ok: false, errors, warnings };
560
+ }
561
+ }
562
+
563
+ return { ok: true, errors, warnings };
564
+ }
565
+
566
+ /**
567
+ * D8 Layer 3: Check bypass audit log for any --no-verify bypassed commits on the current branch.
568
+ * Blocks phase transition if bypassed commits exist.
569
+ *
570
+ * @param {string} projectDir - Project root directory
571
+ * @returns {{ ok: boolean, bypassedCommits: string[] }}
572
+ */
573
+ function checkBypassAudit(projectDir) {
574
+ const auditFile = path.join(projectDir, '.xp-gate', 'bypass-audit.jsonl');
575
+ const bypassedCommits = [];
576
+
577
+ if (!fs.existsSync(auditFile)) {
578
+ return { ok: true, bypassedCommits };
579
+ }
580
+
581
+ const lines = fs.readFileSync(auditFile, 'utf8').trim().split('\n').filter(Boolean);
582
+ for (const line of lines) {
583
+ try {
584
+ const entry = JSON.parse(line);
585
+ if (entry.type === 'precommit_bypass' && entry.commit) {
586
+ // Verify the commit is on the current branch
587
+ try {
588
+ execSync(`git merge-base --is-ancestor ${entry.commit} HEAD`, {
589
+ cwd: projectDir, stdio: 'pipe',
590
+ });
591
+ bypassedCommits.push(entry.commit);
592
+ } catch {
593
+ // Commit not on current branch — not relevant
594
+ }
595
+ }
596
+ } catch {
597
+ // Skip malformed lines
598
+ }
599
+ }
600
+
601
+ return { ok: bypassedCommits.length === 0, bypassedCommits };
602
+ }
603
+
604
+ module.exports = { handlePhaseTransition, renderDashboard, PHASE_NAMES, validateEvidence, checkEvidence, checkWalkthrough, checkBypassAudit };
@@ -166,10 +166,32 @@ async function handleSprintInit(args = []) {
166
166
  return 0;
167
167
  }
168
168
 
169
+ // Read previous sprint retro data for auto-population (Phase 6 / D7)
170
+ const historyDir = path.join(projectDir, '.sprint-history');
171
+ let previousRetro = null;
172
+ if (fs.existsSync(historyDir)) {
173
+ const entries = fs.readdirSync(historyDir).filter(f => f.startsWith('sprint-')).sort().reverse();
174
+ for (const entry of entries) {
175
+ if (entry.endsWith('.json') && !entry.includes('retro')) continue;
176
+ const retroPath = path.join(historyDir, entry);
177
+ if (fs.existsSync(retroPath)) {
178
+ try {
179
+ const raw = fs.readFileSync(retroPath, 'utf8');
180
+ previousRetro = JSON.parse(raw);
181
+ break;
182
+ } catch {
183
+ // Skip unparseable files
184
+ }
185
+ }
186
+ break; // Only check the most recent sprint
187
+ }
188
+ }
189
+
169
190
  // Create new sprint state
170
191
  const sprintId = generateSprintId();
171
192
  const newState = {
172
193
  _schema_version: 1,
194
+ evidence_schema_version: 2, // Phase 5: new sprints default to full BLOCK enforcement
173
195
  id: sprintId,
174
196
  task_description: taskDescription.trim(),
175
197
  phase: 1,
@@ -191,6 +213,25 @@ async function handleSprintInit(args = []) {
191
213
  metrics: {},
192
214
  };
193
215
 
216
+ // Phase 6 / D7: Auto-populate from previous sprint retro data
217
+ if (previousRetro) {
218
+ if (previousRetro.rework_rate != null) {
219
+ newState.metrics.previous_rework_rate = previousRetro.rework_rate;
220
+ }
221
+ if (previousRetro.evidence_skips != null) {
222
+ newState.metrics.previous_evidence_skips = previousRetro.evidence_skips;
223
+ }
224
+ if (previousRetro.duration_seconds != null) {
225
+ newState.metrics.previous_duration_seconds = previousRetro.duration_seconds;
226
+ }
227
+ if (Object.keys(newState.metrics).length > 0) {
228
+ console.log('📊 Auto-populated from previous sprint retro data:');
229
+ for (const [key, value] of Object.entries(newState.metrics)) {
230
+ console.log(` ${key}: ${value}`);
231
+ }
232
+ }
233
+ }
234
+
194
235
  // Write state atomically
195
236
  manager.write(newState);
196
237