@boyingliu01/xp-gate 0.18.2 → 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.
- package/adapter-common.sh +0 -0
- package/adapters/cpp.sh +0 -0
- package/adapters/dart.sh +0 -0
- package/adapters/flutter.sh +0 -0
- package/adapters/gate-9.sh +1 -0
- package/adapters/go.sh +0 -0
- package/adapters/iac.sh +0 -0
- package/adapters/java.sh +0 -0
- package/adapters/kotlin.sh +0 -0
- package/adapters/objectivec.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
- package/adapters/powershell.sh +0 -0
- package/adapters/python.sh +0 -0
- package/adapters/shell.sh +0 -0
- package/adapters/swift.sh +0 -0
- package/adapters/typescript.sh +0 -0
- package/bin/xp-gate.js +45 -0
- package/gate-3.sh +0 -0
- package/gate-4.sh +0 -0
- package/gate-7.sh +0 -0
- package/gate-8.sh +0 -0
- package/gate-9.sh +0 -0
- package/hooks/adapter-common.sh +0 -0
- package/hooks/gate-3.sh +0 -0
- package/hooks/gate-4.sh +0 -0
- package/hooks/gate-7.sh +0 -0
- package/hooks/gate-8.sh +0 -0
- package/hooks/gate-9.sh +0 -0
- package/hooks/pre-commit +108 -9
- package/hooks/pre-push +63 -232
- package/hooks/sprint-gate.sh +0 -0
- package/lib/__tests__/install-skill.test.js +3 -3
- package/lib/__tests__/phase-transition.test.js +512 -512
- package/lib/__tests__/sprint-audit.test.js +207 -207
- package/lib/__tests__/test-alignment.test.ts +761 -0
- package/lib/install-skill.js +11 -3
- package/lib/language-tools.js +833 -833
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/phase-transition.js +604 -499
- package/lib/sprint-audit.js +235 -235
- package/lib/sprint-init.js +248 -207
- package/lib/test-alignment.ts +651 -0
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/mutation/gate-m.ts +18 -4
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
- package/plugins/claude-code/bin/xp-gate-check +0 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/grilling/SKILL.md +12 -12
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/grilling/SKILL.md +12 -12
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/agents/delphi-architecture.md +48 -48
- package/plugins/qoder/agents/delphi-feasibility.md +48 -48
- package/plugins/qoder/agents/delphi-technical.md +48 -48
- package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
- package/plugins/qoder/bin/xp-gate-check +0 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/scripts/delphi-external-review.cjs +432 -432
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/grilling/SKILL.md +12 -12
- package/skills/sprint-flow/AGENTS.md +3 -3
- 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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
755
|
-
if [[ "$
|
|
756
|
-
|
|
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
|
|
869
|
-
#
|
|
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
|
|
905
|
-
|
|
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 "
|
|
935
|
-
|
|
936
|
-
|
|
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
|
package/hooks/sprint-gate.sh
CHANGED
|
File without changes
|
|
@@ -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-
|
|
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-
|
|
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-
|
|
327
|
+
expect(console.error).toHaveBeenCalledWith('Error: Failed to download test-driven-development');
|
|
328
328
|
});
|
|
329
329
|
});
|