@boyingliu01/xp-gate 0.18.2 → 0.18.4

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 (79) hide show
  1. package/adapter-common.sh +0 -0
  2. package/adapters/cpp.sh +0 -0
  3. package/adapters/dart.sh +0 -0
  4. package/adapters/flutter.sh +0 -0
  5. package/adapters/gate-9.sh +1 -0
  6. package/adapters/go.sh +0 -0
  7. package/adapters/iac.sh +0 -0
  8. package/adapters/java.sh +0 -0
  9. package/adapters/kotlin.sh +0 -0
  10. package/adapters/objectivec.sh +0 -0
  11. package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
  12. package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
  13. package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
  14. package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
  15. package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
  16. package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
  17. package/adapters/powershell.sh +0 -0
  18. package/adapters/python.sh +0 -0
  19. package/adapters/shell.sh +0 -0
  20. package/adapters/swift.sh +0 -0
  21. package/adapters/typescript.sh +0 -0
  22. package/bin/xp-gate.js +45 -0
  23. package/gate-3.sh +0 -0
  24. package/gate-4.sh +0 -0
  25. package/gate-7.sh +0 -0
  26. package/gate-8.sh +0 -0
  27. package/gate-9.sh +0 -0
  28. package/hooks/adapter-common.sh +0 -0
  29. package/hooks/gate-3.sh +0 -0
  30. package/hooks/gate-4.sh +0 -0
  31. package/hooks/gate-7.sh +0 -0
  32. package/hooks/gate-8.sh +0 -0
  33. package/hooks/gate-9.sh +0 -0
  34. package/hooks/pre-commit +108 -9
  35. package/hooks/pre-push +63 -232
  36. package/hooks/sprint-gate.sh +0 -0
  37. package/lib/__tests__/install-skill.test.js +3 -3
  38. package/lib/__tests__/phase-transition.test.js +512 -512
  39. package/lib/__tests__/sprint-audit.test.js +207 -207
  40. package/lib/__tests__/test-alignment.test.ts +761 -0
  41. package/lib/doctor.js +1 -1
  42. package/lib/install-skill.js +11 -3
  43. package/lib/language-tools.js +833 -833
  44. package/lib/mattpocock-license-verdict.json +1 -1
  45. package/lib/phase-transition.js +604 -499
  46. package/lib/sprint-audit.js +235 -235
  47. package/lib/sprint-init.js +248 -207
  48. package/lib/test-alignment.ts +651 -0
  49. package/mock-policy/AGENTS.md +3 -3
  50. package/mutation/AGENTS.md +3 -3
  51. package/mutation/gate-m.ts +18 -4
  52. package/package.json +1 -1
  53. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  54. package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
  55. package/plugins/claude-code/bin/xp-gate-check +0 -0
  56. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  57. package/plugins/claude-code/skills/grilling/SKILL.md +12 -12
  58. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  59. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  60. package/plugins/opencode/package.json +1 -1
  61. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  62. package/plugins/opencode/skills/grilling/SKILL.md +12 -12
  63. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  64. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  65. package/plugins/qoder/agents/delphi-architecture.md +48 -48
  66. package/plugins/qoder/agents/delphi-feasibility.md +48 -48
  67. package/plugins/qoder/agents/delphi-technical.md +48 -48
  68. package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
  69. package/plugins/qoder/bin/xp-gate-check +0 -0
  70. package/plugins/qoder/plugin.json +1 -1
  71. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  72. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  73. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  74. package/principles/AGENTS.md +3 -3
  75. package/scripts/delphi-external-review.cjs +432 -432
  76. package/skills/delphi-review/AGENTS.md +3 -3
  77. package/skills/grilling/SKILL.md +12 -12
  78. package/skills/sprint-flow/AGENTS.md +3 -3
  79. package/skills/test-specification-alignment/AGENTS.md +3 -3
package/adapter-common.sh CHANGED
File without changes
package/adapters/cpp.sh CHANGED
File without changes
package/adapters/dart.sh CHANGED
File without changes
File without changes
@@ -102,3 +102,4 @@ console.log(' ['+severity+'] '+ruleId);console.log(' '+path+':'+line+' → '+m
102
102
  fi
103
103
  fi
104
104
  record_gate_audit "gate-9" "sast-security" "$GATE_9_STATUS" "0" "$GATE_9_START"
105
+
package/adapters/go.sh CHANGED
File without changes
package/adapters/iac.sh CHANGED
File without changes
package/adapters/java.sh CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
package/adapters/shell.sh CHANGED
File without changes
package/adapters/swift.sh CHANGED
File without changes
File without changes
package/bin/xp-gate.js CHANGED
@@ -28,6 +28,46 @@ function handleUIReview() {
28
28
  }
29
29
  }
30
30
 
31
+ async function runAlignmentCheck(subargs) {
32
+ const specIdx = subargs.indexOf('--spec');
33
+ const specPath = specIdx >= 0 ? subargs[specIdx + 1] : path.join(process.cwd(), 'specification.yaml');
34
+
35
+ const testsIdx = subargs.indexOf('--tests');
36
+ const testDirs = testsIdx >= 0 ? [subargs[testsIdx + 1]] : undefined;
37
+
38
+ const outputIdx = subargs.indexOf('--output');
39
+ const outputPath = outputIdx >= 0 ? subargs[outputIdx + 1] : undefined;
40
+
41
+ const jsonMode = subargs.includes('--json');
42
+
43
+ // Load TS module via tsx
44
+ const { execSync } = require('child_process');
45
+ const alignmentPath = path.join(__dirname, '..', 'lib', 'test-alignment.ts');
46
+
47
+ try {
48
+ execSync(`npx tsx -e "
49
+ import { runFullAlignment } from '${alignmentPath}';
50
+ const report = runFullAlignment({
51
+ specPath: '${specPath}',
52
+ testDirs: ${JSON.stringify(testDirs || null)},
53
+ headCommit: require('child_process').execSync('git rev-parse HEAD 2>/dev/null || echo unknown').toString().trim(),
54
+ outputPath: ${outputPath ? `'${outputPath}'` : 'undefined'},
55
+ });
56
+ if (${jsonMode}) {
57
+ console.log(JSON.stringify(report, null, 2));
58
+ } else {
59
+ console.log('Status:', report.alignment_status);
60
+ console.log('Score:', report.score);
61
+ console.log('Misaligned:', report.misaligned_tests.length);
62
+ }
63
+ process.exit(report.alignment_status === 'PASS' ? 0 : 1);
64
+ "`, { stdio: 'inherit' });
65
+ } catch (err) {
66
+ return 1;
67
+ }
68
+ return 0;
69
+ }
70
+
31
71
  const COMMANDS = {
32
72
  'init': {
33
73
  description: 'Initialize xp-gate (use --global for all projects)',
@@ -131,6 +171,11 @@ const COMMANDS = {
131
171
  run: subargs => arch(subargs).then(code => process.exit(code)),
132
172
  usage: 'xp-gate arch [--config <path>]'
133
173
  },
174
+ 'check-alignment': {
175
+ description: 'Run test-specification alignment check (deterministic, no LLM)',
176
+ run: subargs => runAlignmentCheck(subargs).then(code => process.exit(code)),
177
+ usage: 'xp-gate check-alignment [--spec <path>] [--tests <dir>] [--json] [--output <path>]'
178
+ },
134
179
  'upgrade': {
135
180
  description: 'Check for xp-gate updates (--preview for JSON, --apply to upgrade)',
136
181
  run: subargs => upgrade(subargs).then(code => process.exit(code)),
package/gate-3.sh CHANGED
File without changes
package/gate-4.sh CHANGED
File without changes
package/gate-7.sh CHANGED
File without changes
package/gate-8.sh CHANGED
File without changes
package/gate-9.sh CHANGED
File without changes
File without changes
package/hooks/gate-3.sh CHANGED
File without changes
package/hooks/gate-4.sh CHANGED
File without changes
package/hooks/gate-7.sh CHANGED
File without changes
package/hooks/gate-8.sh CHANGED
File without changes
package/hooks/gate-9.sh CHANGED
File without changes
package/hooks/pre-commit CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/bin/bash
2
- # OpenCode Quality Gates - Pre-Commit Hook - Refactored 6-Type-Based Gates
2
+ # OpenCode Quality Gates - Pre-Commit Hook - 12 numbered gates (Gate 0–11)
3
3
  #
4
- # DESIGN PRINCIPLE: Zero tolerance - tool unavailable = BLOCK (per CODE-OF-CONDUCT.md)
5
- # Each gate requires its tools to be installed; missing tools block the commit.
4
+ # DESIGN PRINCIPLE: Tool unavailable = SKIP (graceful degradation), tool available + check fails = BLOCK
5
+ # Missing tools do NOT block the commit — the gate degrades to SKIP. Block fires only when
6
+ # the tool exists AND the check fails. This prevents broken toolchains from blocking work.
6
7
  #
7
- # Converts 9 sequential gates into 6 type-based comprehensive gates
8
8
  # Sourced from adapter-common.sh and language-specific adapters
9
9
 
10
10
  # Source the common adapter functions — 3-tier resolution:
@@ -1328,7 +1328,9 @@ source "$GATE_DIR/gate-4.sh"
1328
1328
  # v0.14.3+ (Issue #312): Skip test run when no code files changed.
1329
1329
  # Code files = src/**/*.ts, plugins/**/*.ts, *.ts, *.tsx, etc. (language-aware).
1330
1330
  # VERSION, CHANGELOG, docs, config-only changes → SKIP test run entirely.
1331
- # Gate 5a (new file pairing) and Gate 5b (mock density) still run — they're lightweight.
1331
+ # Gate 5a (new file pairing), 5b (mock density), and 5c (semantic annotation)
1332
+ # are lightweight and still run even when no code files changed.
1333
+ # Gate 5a (new file pairing) and Gate 5b (mock density) still run — they're lightweight.
1332
1334
  # ============================================================================
1333
1335
  2>&1 echo ""
1334
1336
  2>&1 echo "→ Gate 5: Tests & coverage..."
@@ -1640,6 +1642,82 @@ else
1640
1642
  done
1641
1643
  fi
1642
1644
 
1645
+ # ============================================================================
1646
+ # Gate 5c: Semantic Annotation Check (v0.19.0+)
1647
+ # Validates @test REQ-XXX annotations against specification.yaml.
1648
+ #
1649
+ # Strategy: Reads pre-compiled req-ids.json (produced by xp-gate check-alignment).
1650
+ # If req-ids.json is absent or stale, degrades to SKIP.
1651
+ # If present: greps staged tests for @test REQ-XXX, cross-references against
1652
+ # the pre-compiled REQ ID list. BLOCKs if any REQ ID doesn't exist in spec.
1653
+ # If any REQ in spec has no annotated test, emits visible [FAIL] message
1654
+ # (exit 0, appears in hook summary) — full alignment check runs at CLI time.
1655
+ # ============================================================================
1656
+ REQ_IDS_FILE=".sprint-state/phase-outputs/req-ids.json"
1657
+ if [ -f "$REQ_IDS_FILE" ] && command -v jq >/dev/null 2>&1; then
1658
+ # Check staleness: req-ids.json must be newer than specification.yaml
1659
+ SPEC_FILE="specification.yaml"
1660
+ REQ_IDS_STALE=false
1661
+ if [ -f "$SPEC_FILE" ]; then
1662
+ if [ "$REQ_IDS_FILE" -ot "$SPEC_FILE" ]; then
1663
+ REQ_IDS_STALE=true
1664
+ fi
1665
+ fi
1666
+
1667
+ if [ "$REQ_IDS_STALE" = true ]; then
1668
+ echo "⚠️ Gate 5c: req-ids.json is stale (specification.yaml updated since last check-alignment)"
1669
+ echo " Run 'xp-gate check-alignment' to regenerate before next commit."
1670
+ else
1671
+ CHANGED_TEST_FILES_ANNOTATED=$(echo "$CHANGED_FILES" | grep -E '\.(test|spec)\.(ts|tsx|js|jsx)$' || true)
1672
+ if [ -n "$CHANGED_TEST_FILES_ANNOTATED" ]; then
1673
+ echo " → Checking @test REQ-XXX annotations..."
1674
+ ANNOTATION_ERRORS=0
1675
+ for tf in $CHANGED_TEST_FILES_ANNOTATED; do
1676
+ if [ -f "$tf" ]; then
1677
+ # Extract @test REQ-XXX annotations from staged test file (word-bounded)
1678
+ ANNOTATED_REQS=$(grep -oE '@test\s+REQ-[A-Z0-9-]+' "$tf" 2>/dev/null | sed 's/@test\s*//' | sort -u || true)
1679
+ for req_id in $ANNOTATED_REQS; do
1680
+ # Cross-reference against pre-compiled REQ ID list
1681
+ FOUND=$(jq -r --arg rid "$req_id" '.[] | select(. == $rid)' "$REQ_IDS_FILE" 2>/dev/null || true)
1682
+ if [ -z "$FOUND" ]; then
1683
+ echo "❌ Gate 5c: $tf — @test $req_id: REQ ID not found in specification.yaml"
1684
+ ANNOTATION_ERRORS=$((ANNOTATION_ERRORS + 1))
1685
+ fi
1686
+ done
1687
+ fi
1688
+ done
1689
+
1690
+ if [ "$ANNOTATION_ERRORS" -gt 0 ]; then
1691
+ echo ""
1692
+ echo "❌ $ANNOTATION_ERRORS test annotation(s) reference non-existent REQ IDs — commit BLOCKED"
1693
+ echo " Fix: Verify REQ IDs against specification.yaml, or run 'xp-gate check-alignment'"
1694
+ exit 1
1695
+ fi
1696
+
1697
+ # Check reverse: any REQ in spec with no annotated test → visible FAIL (non-blocking)
1698
+ ALL_SPEC_REQS=$(jq -r '.[]' "$REQ_IDS_FILE" 2>/dev/null)
1699
+ ANNOTATED_ALL=""
1700
+ for tf in $(echo "$CHANGED_FILES" | grep -E '\.(test|spec)\.(ts|tsx|js|jsx)$' || true); do
1701
+ if [ -f "$tf" ]; then
1702
+ ANNOTATED_ALL="$ANNOTATED_ALL $(grep -oE '@test\s+REQ-[A-Z0-9-]+' "$tf" 2>/dev/null | sed 's/@test\s*//' || true)"
1703
+ fi
1704
+ done
1705
+ UNTESTED_COUNT=0
1706
+ for spec_req in $ALL_SPEC_REQS; do
1707
+ if ! echo "$ANNOTATED_ALL" | grep -qF "$spec_req"; then
1708
+ UNTESTED_COUNT=$((UNTESTED_COUNT + 1))
1709
+ fi
1710
+ done
1711
+ if [ "$UNTESTED_COUNT" -gt 0 ]; then
1712
+ echo "⚠️ [FAIL] Gate 5c: $UNTESTED_COUNT REQ(s) in specification.yaml have no annotated tests"
1713
+ echo " This is non-blocking. Run 'xp-gate check-alignment' for full alignment report."
1714
+ fi
1715
+
1716
+ echo " ✅ @test annotation check complete."
1717
+ fi
1718
+ fi
1719
+ fi
1720
+
1643
1721
  # ============================================================================
1644
1722
  # v0.14.3+ (Issue #312): Early skip when no code files changed.
1645
1723
  # If the commit only touches VERSION, CHANGELOG, docs, config, .gitignore,
@@ -2439,13 +2517,13 @@ generate_quality_report() {
2439
2517
  local TIMESTAMP
2440
2518
  local BRANCH
2441
2519
  local PASSED_COUNT=0
2442
- local TOTAL_GATES=11
2520
+ local TOTAL_GATES=12
2443
2521
 
2444
2522
  COMMIT_HASH=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
2445
2523
  BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
2446
2524
  TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
2447
2525
 
2448
- for gate in 1 2 3 4 5 6 7 8 9 10 11; do
2526
+ for gate in 1 2 3 4 5 6 7 8 9 10 11 12; do
2449
2527
  local status_var="GATE_${gate}_STATUS"
2450
2528
  if [ "${!status_var}" = "PASS" ]; then
2451
2529
  PASSED_COUNT=$((PASSED_COUNT + 1))
@@ -2558,6 +2636,11 @@ generate_quality_report() {
2558
2636
  "name": "Sprint Flow Enforcement",
2559
2637
  "status": "${GATE_11_STATUS:-PASS}",
2560
2638
  "tool": "sprint-gate.sh"
2639
+ },
2640
+ "gate12_file_hygiene": {
2641
+ "name": "File Hygiene Check",
2642
+ "status": "${GATE_12_STATUS:-WARN}",
2643
+ "tool": "gate-12-file-hygiene.sh"
2561
2644
  }
2562
2645
  }
2563
2646
  }
@@ -2583,8 +2666,8 @@ ENDJSON
2583
2666
  GATES_JSON="${GATES_JSON}\"gate8\":{\"status\":\"${GATE_8_STATUS:-PASS}\",\"name\":\"Secret Scanning\"},"
2584
2667
  GATES_JSON="${GATES_JSON}\"gate9\":{\"status\":\"${GATE_9_STATUS:-PASS}\",\"name\":\"Build Integrity\"},"
2585
2668
  GATES_JSON="${GATES_JSON}\"gate10\":{\"status\":\"${GATE_10_STATUS:-PASS}\",\"name\":\"SAST Security\"},"
2586
- GATES_JSON="${GATES_JSON}\"gate11\":{\"status\":\"${GATE_11_STATUS:-PASS}\",\"name\":\"Sprint Flow\"}}"
2587
- GATES_JSON="${GATES_JSON}}"
2669
+ GATES_JSON="${GATES_JSON}\"gate11\":{\"status\":\"${GATE_11_STATUS:-PASS}\",\"name\":\"Sprint Flow\"},"
2670
+ GATES_JSON="${GATES_JSON}\"gate12\":{\"status\":\"${GATE_12_STATUS:-WARN}\",\"name\":\"File Hygiene\"}}"
2588
2671
 
2589
2672
  echo "{\"timestamp\":\"$TIMESTAMP\",\"commit\":\"$COMMIT_HASH\",\"branch\":\"$BRANCH\",\"score\":$SCORE,\"passed\":$PASSED_COUNT,\"total\":$TOTAL_GATES,\"gates\":$GATES_JSON,\"coverage\":\"$COV_PCT\",\"complexityWarnings\":${CC_WARNINGS:-0},\"principleWarnings\":${WARNING_COUNT:-0},\"boyScoutBlocked\":${BS_BLOCKED}}" >> "$HISTORY_FILE"
2590
2673
 
@@ -2605,6 +2688,7 @@ ENDJSON
2605
2688
  printf " %-45s %s\n" "Gate 9: Build Integrity" "${GATE_9_STATUS:-PASS}"
2606
2689
  printf " %-45s %s\n" "Gate 10: SAST Security" "${GATE_10_STATUS:-PASS}"
2607
2690
  printf " %-45s %s\n" "Gate 11: Sprint Flow" "${GATE_11_STATUS:-PASS}"
2691
+ printf " %-45s %s\n" "Gate 12: File Hygiene" "${GATE_12_STATUS:-WARN}"
2608
2692
  echo ""
2609
2693
  echo " Overall Score: $SCORE/10 | $PASSED_COUNT/$TOTAL_GATES gates passed"
2610
2694
  echo " Branch status: $REPORT_FILE"
@@ -2612,8 +2696,23 @@ ENDJSON
2612
2696
  echo " History saved: $HISTORY_FILE"
2613
2697
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
2614
2698
  echo ""
2699
+ # AI-friendly common footer: tells LLM agents how to fix issues instead of bypassing
2700
+ echo " ── AI AGENT DIRECTIVE ──"
2701
+ echo " If any gate FAILED above:"
2702
+ echo " - Each error includes a file path and rule identifier"
2703
+ echo " - Fix each issue, then re-run: git commit"
2704
+ echo " - Do NOT use --no-verify — bypassed commits block sprint progression"
2705
+ echo " ─────────────────────────"
2706
+ echo ""
2615
2707
  }
2616
2708
 
2709
+ # Post-commit bypass marker: write a sentinel file in .git/ directory
2710
+ # that the post-commit hook checks to detect --no-verify bypasses
2711
+ _wp_marker="$(git rev-parse --git-dir 2>/dev/null)/xp-gate-precommit-marker"
2712
+ if [ -n "$_wp_marker" ] && [ "$_wp_marker" != "/xp-gate-precommit-marker" ]; then
2713
+ echo "$(date +%s)" > "$_wp_marker"
2714
+ fi
2715
+
2617
2716
  generate_quality_report
2618
2717
 
2619
2718
  exit 0