@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
package/hooks/pre-push CHANGED
@@ -1,9 +1,13 @@
1
1
  #!/bin/bash
2
- # Pre-push Hook - Code Walkthrough Result Validator
2
+ # Pre-push Hook — 8 gates: Gate 10 (Build Integrity) + M/M-Python/M-Go/M-Java/M-Kotlin (Mutation)
3
+ # + M2 (Mock Density, WARNING) + ML (Mock Layering) + UI (UI Sprint Gates) + MW (Code Walkthrough) + S (Sprint Flow)
3
4
  #
4
- # DESIGN: Hook validates result file, Skill executes review
5
+ # DESIGN: Hook validates result/executes checks, Skill executes AI review
5
6
  # No CLI skill invocation - avoids OpenCode architecture mismatch
6
7
  #
8
+ # Size limits intentionally REMOVED — AI workflows generate large cumulative pushes;
9
+ # file count is not a quality signal. All pre-push runs journaled to .xp-gate/reports/pre-push/.
10
+ #
7
11
  # See: docs/plans/delphi-review --mode code-walkthrough-pre-push-design-v2.md
8
12
  #
9
13
  # Install: cp this-file .git/hooks/pre-push && chmod +x .git/hooks/pre-push
@@ -27,7 +31,41 @@ write_pre_push_report() {
27
31
  commit_hash=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
28
32
  verdict=$([ "$exit_code" -eq 0 ] && echo "PASS" || echo "BLOCKED")
29
33
  mkdir -p "$reports_dir"
30
- _XP_RF="$report_file" _XP_TS="$timestamp" _XP_BR="$CURRENT_BRANCH" _XP_CH="$commit_hash" _XP_PF="${PUSHED_FILES:-}" _XP_VD="$verdict" _XP_EC="$exit_code" _XP_G10="${GATE_10_STATUS:-SKIP}" _XP_GM="${GATE_M_STATUS:-SKIP}" _XP_GMP="${GATE_M_PYTHON_STATUS:-SKIP}" _XP_GMGO="${GATE_M_GO_STATUS:-SKIP}" _XP_GMJA="${GATE_M_JAVA_STATUS:-SKIP}" _XP_GMKO="${GATE_M_KOTLIN_STATUS:-SKIP}" _XP_GMD="${GATE_M2_STATUS:-SKIP}" _XP_GML="${GATE_M3_STATUS:-SKIP}" _XP_GUI="${GATE_UI_STATUS:-SKIP}" _XP_GMW="${GATE_DELPHI_STATUS:-SKIP}" _XP_RM="$REMOTE" _XP_URL="$URL" node -e "const fs=require('fs');const e=process.env;const pf=(e._XP_PF||'').split('\n').filter(f=>f);const r={timestamp:e._XP_TS,trigger:'pre-push',branch:e._XP_BR,commit:e._XP_CH,changed_files:pf,overall:{score:e._XP_VD==='PASS'?'10.0/10':'0.0/10',gates_passed:e._XP_VD==='PASS'?1:0,gates_total:1,verdict:e._XP_VD},gates:[{id:'10',name:'Build Integrity',status:e._XP_G10,details:{}},{id:'M',name:'Mutation Testing',status:e._XP_GM,details:{}},{id:'M-Python',name:'Mutation Testing (Python)',status:e._XP_GMP,details:{}},{id:'M-Go',name:'Mutation Testing (Go)',status:e._XP_GMGO,details:{}},{id:'M-Java',name:'Mutation Testing (Java)',status:e._XP_GMJA,details:{}},{id:'M-Kotlin',name:'Mutation Testing (Kotlin)',status:e._XP_GMKO,details:{}},{id:'MD',name:'Mock Density',status:e._XP_GMD,details:{}},{id:'ML',name:'Mock Layering',status:e._XP_GML,details:{}},{id:'UI',name:'UI Sprint Gates',status:e._XP_GUI,details:{}},{id:'MW',name:'Code Walkthrough',status:e._XP_GMW,details:{remote:e._XP_RM,url:e._XP_URL}}],warnings:[],errors:e._XP_VD==='PASS'?[]:['pre-push hook exited with code '+e._XP_EC]};fs.writeFileSync(e._XP_RF,JSON.stringify(r,null,2)+'\n');" 2>/dev/null || true
34
+ python3 - <<PY 2>/dev/null || true
35
+ import json
36
+ from pathlib import Path
37
+ pushed_files = '''${PUSHED_FILES:-}'''.splitlines()
38
+ passed = 1 if '$verdict' == 'PASS' else 0
39
+ report = {
40
+ 'timestamp': '$timestamp',
41
+ 'trigger': 'pre-push',
42
+ 'branch': '$CURRENT_BRANCH',
43
+ 'commit': '$commit_hash',
44
+ 'changed_files': [f for f in pushed_files if f],
45
+ 'overall': {
46
+ 'score': '10.0/10' if '$verdict' == 'PASS' else '0.0/10',
47
+ 'gates_passed': passed,
48
+ 'gates_total': 8,
49
+ 'verdict': '$verdict',
50
+ },
51
+ 'gates': [
52
+ {'id': '10', 'name': 'Build Integrity', 'status': '${GATE_10_STATUS:-SKIP}', 'details': {}},
53
+ {'id': 'M', 'name': 'Mutation Testing', 'status': '${GATE_M_STATUS:-SKIP}', 'details': {}},
54
+ {'id': 'M-Python', 'name': 'Mutation Testing (Python)', 'status': '${GATE_M_PYTHON_STATUS:-SKIP}', 'details': {}},
55
+ {'id': 'M-Go', 'name': 'Mutation Testing (Go)', 'status': '${GATE_M_GO_STATUS:-SKIP}', 'details': {}},
56
+ {'id': 'M-Java', 'name': 'Mutation Testing (Java)', 'status': '${GATE_M_JAVA_STATUS:-SKIP}', 'details': {}},
57
+ {'id': 'M-Kotlin', 'name': 'Mutation Testing (Kotlin)', 'status': '${GATE_M_KOTLIN_STATUS:-SKIP}', 'details': {}},
58
+ {'id': 'M2', 'name': 'Mock Density (WARNING only)', 'status': '${GATE_M2_STATUS:-SKIP}', 'details': {}},
59
+ {'id': 'ML', 'name': 'Mock Layering', 'status': '${GATE_M3_STATUS:-SKIP}', 'details': {}},
60
+ {'id': 'UI', 'name': 'UI Sprint Gates', 'status': '${GATE_UI_STATUS:-SKIP}', 'details': {}},
61
+ {'id': 'MW', 'name': 'Code Walkthrough', 'status': '${GATE_DELPHI_STATUS:-SKIP}', 'details': {'remote': '$REMOTE', 'url': '$URL'}},
62
+ {'id': 'S', 'name': 'Sprint Flow Enforcement', 'status': '${GATE_S_STATUS:-SKIP}', 'details': {}}
63
+ ],
64
+ 'warnings': [],
65
+ 'errors': [] if '$verdict' == 'PASS' else ['pre-push hook exited with code $exit_code'],
66
+ }
67
+ Path('$report_file').write_text(json.dumps(report, indent=2) + '\n')
68
+ PY
31
69
  }
32
70
 
33
71
  _pre_push_report_on_exit() {
@@ -680,12 +718,6 @@ run_gate_m3() {
680
718
  echo " GATE ML: MOCK LAYERING STRATEGY"
681
719
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
682
720
 
683
- # Skip on main/master
684
- if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
685
- echo " SKIP — pushing to main/master"
686
- return 0
687
- fi
688
-
689
721
  # Check for mock-policy module in installed modules first, then project src/
690
722
  local GATE_M3_SCRIPT=""
691
723
  if [ -f ".xp-gate/modules/mock-policy/gate-m3.ts" ]; then
@@ -751,20 +783,15 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
751
783
  echo " GATE UI: UI SPRINT QUALITY GATES"
752
784
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
753
785
 
754
- # Skip on main/master (pre-reviewed via PR)
755
- if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
756
- echo "⚠️ Pushing to main/master — UI Gate skipped (pre-reviewed via PR)"
786
+ if [[ "$XP_GATE_SKIP_UI_GATES" == "1" ]]; then
787
+ if [[ -z "$XP_GATE_BYPASS_REASON" || ${#XP_GATE_BYPASS_REASON// /} -lt 10 ]]; then
788
+ echo "❌ XP_GATE_BYPASS_REASON required (min 10 non-whitespace chars)"
789
+ exit 1
790
+ fi
791
+ echo "⚠️ UI Gate BYPASSED — reason: $XP_GATE_BYPASS_REASON"
792
+ AUDIT_ENTRY="{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"branch\":\"$CURRENT_BRANCH\",\"commit\":\"$(git rev-parse HEAD)\",\"user\":\"$(git config user.name || echo 'unknown')\",\"reason\":\"$XP_GATE_BYPASS_REASON\",\"bypass_type\":\"ui-gates\"}"
793
+ echo "$AUDIT_ENTRY" >> .audit-log.jsonl
757
794
  else
758
- # Check bypass
759
- if [[ "$XP_GATE_SKIP_UI_GATES" == "1" ]]; then
760
- if [[ -z "$XP_GATE_BYPASS_REASON" || ${#XP_GATE_BYPASS_REASON// /} -lt 10 ]]; then
761
- echo "❌ XP_GATE_BYPASS_REASON required (min 10 non-whitespace chars)"
762
- exit 1
763
- fi
764
- echo "⚠️ UI Gate BYPASSED — reason: $XP_GATE_BYPASS_REASON"
765
- AUDIT_ENTRY="{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"branch\":\"$CURRENT_BRANCH\",\"commit\":\"$(git rev-parse HEAD)\",\"user\":\"$(git config user.name || echo 'unknown')\",\"reason\":\"$XP_GATE_BYPASS_REASON\",\"bypass_type\":\"ui-gates\"}"
766
- echo "$AUDIT_ENTRY" >> .audit-log.jsonl
767
- else
768
795
  # Detect UI changes in pushed files
769
796
  UI_DETECTION=$(echo "$PUSHED_FILES" | npx -y tsx src/npm-package/lib/ui-detector.ts --push-mode --from-stdin 2>/dev/null) || true
770
797
 
@@ -816,17 +843,12 @@ else
816
843
  echo "ℹ️ No UI changes detected — skipping UI Gate"
817
844
  fi
818
845
  fi
819
- fi
820
846
 
821
847
  # ============================================================================
822
848
  # GATE MW: VALIDATE CODE WALKTHROUGH RESULT FILE
823
849
  # ============================================================================
824
850
  RESULT_FILE=".code-walkthrough-result.json"
825
851
 
826
- # Skip MW walkthrough for main/master (Gate M already ran above)
827
- if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
828
- echo "⚠️ Pushing to main/master - MW walkthrough skipped"
829
- else
830
852
 
831
853
  if [ ! -f "$RESULT_FILE" ]; then
832
854
  echo ""
@@ -865,11 +887,16 @@ if ! command -v jq &> /dev/null; then
865
887
  fi
866
888
 
867
889
  # Validate result file content
868
- # SHA self-reference paradox fix (Issue #346):
869
- # The walkthrough file may reference an earlier commit if it was generated before
870
- # subsequent walkthrough-only commits. We check if the referenced commit is in
871
- # the current branch's history and if no functional commits occurred after it.
890
+ # SHA self-reference paradox fix: if the result file was committed as part of
891
+ # HEAD, its content references HEAD^ (the commit being reviewed), not HEAD.
872
892
  CURRENT_COMMIT=$(git rev-parse HEAD)
893
+ if git diff-tree --no-commit-id --name-only -r HEAD 2>/dev/null | grep -qx "$RESULT_FILE"; then
894
+ # Result file is part of the latest commit — compare against the reviewed commit
895
+ EXPECTED_COMMIT=$(git rev-parse HEAD^ 2>/dev/null || echo "unknown")
896
+ else
897
+ # Result file is working-tree only — compare against current HEAD
898
+ EXPECTED_COMMIT="$CURRENT_COMMIT"
899
+ fi
873
900
 
874
901
  # Check JSON validity
875
902
  if ! jq empty "$RESULT_FILE" 2>/dev/null; then
@@ -901,42 +928,16 @@ if [ "$RESULT_BRANCH" != "$CURRENT_BRANCH" ]; then
901
928
  echo "Proceeding with commit verification..."
902
929
  fi
903
930
 
904
- # Commit match check (Issue #346: flexible matching to avoid circular dependency)
905
- # Check if RESULT_COMMIT is in the current branch's history
906
- if ! git merge-base --is-ancestor "$RESULT_COMMIT" HEAD 2>/dev/null; then
907
- echo ""
908
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
909
- echo " ❌ RESULT FILE INVALID - PUSH BLOCKED"
910
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
911
- echo ""
912
- echo "Result file references a commit not in current branch:"
913
- echo " Referenced: $RESULT_COMMIT"
914
- echo " Current HEAD: $CURRENT_COMMIT"
915
- echo ""
916
- echo "Re-run: /delphi-review --mode code-walkthrough"
917
- echo ""
918
- exit 1
919
- fi
920
-
921
- # Check if there are any functional commits after RESULT_COMMIT
922
- # Functional commits are those that are NOT walkthrough-only updates or Gate MW fixes
923
- FUNCTIONAL_COMMITS_AFTER=$(git log --oneline "$RESULT_COMMIT"..HEAD -- . \
924
- ':!.code-walkthrough-result.json' \
925
- ':!.delphi/code-walkthrough.md' \
926
- ':!githooks/pre-push' \
927
- ':!src/npm-package/hooks/pre-push' 2>/dev/null | wc -l)
928
- if [ "$FUNCTIONAL_COMMITS_AFTER" -gt 0 ]; then
931
+ # Commit match check
932
+ if [ "$RESULT_COMMIT" != "$EXPECTED_COMMIT" ]; then
929
933
  echo ""
930
934
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
931
935
  echo " ❌ RESULT FILE OUTDATED - PUSH BLOCKED"
932
936
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
933
937
  echo ""
934
- echo "Functional commits found after walkthrough:"
935
- git log --oneline "$RESULT_COMMIT"..HEAD -- . \
936
- ':!.code-walkthrough-result.json' \
937
- ':!.delphi/code-walkthrough.md' \
938
- ':!githooks/pre-push' \
939
- ':!src/npm-package/hooks/pre-push' 2>/dev/null | head -5
938
+ echo "Result file is for a different commit:"
939
+ echo " Expected: $EXPECTED_COMMIT"
940
+ echo " Found: $RESULT_COMMIT"
940
941
  echo ""
941
942
  echo "Re-run: /delphi-review --mode code-walkthrough"
942
943
  echo ""
@@ -982,178 +983,9 @@ if [[ "$CURRENT_TIME" > "$RESULT_EXPIRES" ]]; then
982
983
  exit 1
983
984
  fi
984
985
 
985
- # ============================================================================
986
- # PROVENANCE VALIDATION (Issue #339)
987
- # Validate that the walkthrough was genuinely produced by Delphi review,
988
- # not fabricated by an LLM.
989
- # ============================================================================
990
-
991
- # Check provenance fields exist
992
- EXPERTS_COUNT=$(jq -r '.experts // [] | length' "$RESULT_FILE")
993
- CONSENSUS=$(jq -r '.consensus // 0' "$RESULT_FILE")
994
- WALKTHROUGH_HASH=$(jq -r '.walkthroughHash // empty' "$RESULT_FILE")
995
- GENERATED_AT=$(jq -r '.generatedAt // empty' "$RESULT_FILE")
996
-
997
- # Grace period: configurable via env var (default 30 days)
998
- GRACE_DAYS="${XP_GATE_MW_GRACE_DAYS:-30}"
999
-
1000
- # Check if this is a "new" walkthrough (has provenance fields)
1001
- HAS_PROVENANCE="false"
1002
- if [ -n "$WALKTHROUGH_HASH" ] && [ -n "$GENERATED_AT" ] && [ "$EXPERTS_COUNT" -ge 3 ]; then
1003
- HAS_PROVENANCE="true"
1004
- fi
1005
-
1006
- if [ "$HAS_PROVENANCE" = "true" ]; then
1007
- # Full provenance validation
1008
-
1009
- # 1. Experts count check (minimum 3)
1010
- if [ "$EXPERTS_COUNT" -lt 3 ]; then
1011
- echo ""
1012
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1013
- echo " ❌ INSUFFICIENT EXPERTS - PUSH BLOCKED"
1014
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1015
- echo ""
1016
- echo "Walkthrough requires minimum 3 experts:"
1017
- echo " Found: $EXPERTS_COUNT experts"
1018
- echo ""
1019
- echo "Re-run: /delphi-review --mode code-walkthrough"
1020
- echo ""
1021
- exit 1
1022
- fi
1023
-
1024
- # 2. Consensus check (≥90%)
1025
- # Use integer math: multiply by 10 to avoid bc dependency
1026
- CONSENSUS_X10=$(echo "$CONSENSUS * 10" | bc -l | cut -d'.' -f1)
1027
- if [ "$CONSENSUS_X10" -lt 900 ]; then
1028
- echo ""
1029
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1030
- echo " ❌ CONSENSUS TOO LOW - PUSH BLOCKED"
1031
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1032
- echo ""
1033
- echo "Walkthrough requires ≥90% consensus:"
1034
- echo " Found: ${CONSENSUS}%"
1035
- echo ""
1036
- echo "Re-run: /delphi-review --mode code-walkthrough"
1037
- echo ""
1038
- exit 1
1039
- fi
1040
-
1041
- # 3. Walkthrough hash verification
1042
- WALKTHROUGH_DOC=".delphi/code-walkthrough.md"
1043
- if [ ! -f "$WALKTHROUGH_DOC" ]; then
1044
- echo ""
1045
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1046
- echo " ❌ WALKTHROUGH DOC MISSING - PUSH BLOCKED"
1047
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1048
- echo ""
1049
- echo "Walkthrough document not found:"
1050
- echo " Expected: $WALKTHROUGH_DOC"
1051
- echo ""
1052
- echo "Re-run: /delphi-review --mode code-walkthrough"
1053
- echo ""
1054
- exit 1
1055
- fi
1056
-
1057
- # Cross-platform SHA-256 computation
1058
- COMPUTED_HASH=""
1059
- if command -v sha256sum &> /dev/null; then
1060
- COMPUTED_HASH=$(sha256sum "$WALKTHROUGH_DOC" | cut -d' ' -f1)
1061
- elif command -v shasum &> /dev/null; then
1062
- COMPUTED_HASH=$(shasum -a 256 "$WALKTHROUGH_DOC" | cut -d' ' -f1)
1063
- elif command -v openssl &> /dev/null; then
1064
- COMPUTED_HASH=$(openssl sha256 -r "$WALKTHROUGH_DOC" | cut -d' ' -f1)
1065
- fi
1066
-
1067
- if [ -z "$COMPUTED_HASH" ]; then
1068
- echo ""
1069
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1070
- echo " ❌ NO SHA-256 TOOL - PUSH BLOCKED"
1071
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1072
- echo ""
1073
- echo "No SHA-256 tool available (install sha256sum, shasum, or openssl)"
1074
- echo ""
1075
- exit 1
1076
- fi
1077
-
1078
- if [ "$WALKTHROUGH_HASH" != "$COMPUTED_HASH" ]; then
1079
- echo ""
1080
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1081
- echo " ❌ WALKTHROUGH HASH MISMATCH - PUSH BLOCKED"
1082
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1083
- echo ""
1084
- echo "Walkthrough document has been modified after review:"
1085
- echo " Expected: $WALKTHROUGH_HASH"
1086
- echo " Found: $COMPUTED_HASH"
1087
- echo ""
1088
- echo "Re-run: /delphi-review --mode code-walkthrough"
1089
- echo ""
1090
- exit 1
1091
- fi
1092
-
1093
- # 4. generatedAt check (replay protection)
1094
- if [ -z "$GENERATED_AT" ]; then
1095
- echo ""
1096
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1097
- echo " ❌ MISSING generatedAt - PUSH BLOCKED"
1098
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1099
- echo ""
1100
- echo "Walkthrough result missing generatedAt timestamp"
1101
- echo ""
1102
- echo "Re-run: /delphi-review --mode code-walkthrough"
1103
- echo ""
1104
- exit 1
1105
- fi
1106
- else
1107
- # Grace period: old walkthroughs without provenance get WARNING
1108
- echo ""
1109
- echo "⚠️ WARNING: Legacy walkthrough format (no provenance fields)"
1110
- echo " Provenance fields will be required after grace period (${GRACE_DAYS} days)"
1111
- echo " Consider re-running: /delphi-review --mode code-walkthrough"
1112
- echo ""
1113
- fi
1114
-
1115
986
  # ============================================================================
1116
987
  # ALL CHECKS PASSED
1117
988
  # ============================================================================
1118
- echo ""
1119
-
1120
- # ============================================================================
1121
- # SPRINT-STATE BACKUP CHECK (WARNING ONLY — Issue #318)
1122
- # Scan for worktree sprint-state directories that haven't been backed up
1123
- # to .sprint-history/ before push. WARN only (doesn't block) — sprint-state
1124
- # inside worktrees is deleted on `git worktree remove`, causing permanent
1125
- # data loss if orchestrator forgets to back up during CLOSE phase.
1126
- # ============================================================================
1127
- if [ -d ".worktrees/sprint" ]; then
1128
- for worktree_dir in .worktrees/sprint/sprint-*; do
1129
- [ -d "$worktree_dir" ] || continue
1130
- [ -d "$worktree_dir/.sprint-state" ] || continue
1131
- sprint_id=$(basename "$worktree_dir")
1132
- if [ -f "$worktree_dir/.sprint-state/sprint-state.json" ]; then
1133
- if [ ! -d ".sprint-history/$sprint_id" ]; then
1134
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1135
- echo " ⚠️ SPRINT-STATE NOT BACKED UP: $sprint_id"
1136
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1137
- echo ""
1138
- echo "Unarchived sprint state found in worktree:"
1139
- echo " Path: $worktree_dir/.sprint-state/"
1140
- echo " Backup: .sprint-history/$sprint_id/ (MISSING)"
1141
- echo ""
1142
- echo "RISK: If this worktree is removed before CLOSE backup,"
1143
- echo " design docs, Delphi verdicts, and phase summaries"
1144
- echo " are permanently lost."
1145
- echo ""
1146
- echo "Backup now:"
1147
- echo " mkdir -p .sprint-history/$sprint_id"
1148
- echo " cp -r $worktree_dir/.sprint-state/* .sprint-history/$sprint_id/"
1149
- echo ""
1150
- echo "See: https://github.com/boyingliu01/xp-gate/issues/318"
1151
- echo ""
1152
- fi
1153
- fi
1154
- done
1155
- fi
1156
-
1157
989
  echo ""
1158
990
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1159
991
  echo " ✅ GATE MW: CODE WALKTHROUGH VERIFIED"
@@ -1176,5 +1008,4 @@ fi
1176
1008
 
1177
1009
  echo ""
1178
1010
  echo "Proceeding with push..."
1179
- fi
1180
1011
  exit 0
@@ -249,7 +249,7 @@ describe('install-skill', () => {
249
249
  mockHttpsGet({ statusCode: 200, body: '# X' });
250
250
 
251
251
  const { installSkill } = require('../install-skill');
252
- await installSkill('test-spec', { verbose: true });
252
+ await installSkill('test-driven-development', { verbose: true });
253
253
 
254
254
  expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Downloading '));
255
255
  expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Installed to '));
@@ -320,10 +320,10 @@ describe('install-skill', () => {
320
320
  mockHttpsGet({ errorAfter: true });
321
321
 
322
322
  const { installSkill } = require('../install-skill');
323
- const result = await installSkill('test-spec');
323
+ const result = await installSkill('test-driven-development');
324
324
 
325
325
  expect(result).toBe(1);
326
326
  expect(console.warn).not.toHaveBeenCalled();
327
- expect(console.error).toHaveBeenCalledWith('Error: Failed to download test-spec');
327
+ expect(console.error).toHaveBeenCalledWith('Error: Failed to download test-driven-development');
328
328
  });
329
329
  });