@boyingliu01/xp-gate 0.19.2 → 0.20.0
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/adapters/gate-4.sh +83 -18
- package/adapters/gate-8.sh +49 -5
- package/adapters/iac.sh +1 -1
- package/adapters/typescript.sh +15 -60
- package/bin/xp-gate.js +15 -16
- package/gate-10.sh +35 -2
- package/gate-4.sh +110 -21
- package/gate-8.sh +49 -5
- package/hooks/gate-10.sh +35 -2
- package/hooks/gate-4.sh +110 -21
- package/hooks/gate-8.sh +49 -5
- package/hooks/lib/jscpd-run.sh +63 -0
- package/hooks/lib/sprint-gate-report.sh +58 -0
- package/hooks/lib/test-failure.sh +152 -0
- package/hooks/lib/typecheck.sh +141 -0
- package/hooks/lib/validate-code-walkthrough.cjs +43 -5
- package/hooks/post-merge +100 -0
- package/hooks/pre-commit +401 -155
- package/hooks/pre-push +75 -19
- package/lib/__tests__/bootstrap.test.js +23 -41
- package/lib/__tests__/check-version.test.js +8 -1
- package/lib/__tests__/detect-deps.test.js +16 -0
- package/lib/__tests__/doctor-drift-round2.test.js +212 -0
- package/lib/__tests__/doctor-executed-surface.test.js +209 -0
- package/lib/__tests__/doctor-hook-drift.test.js +329 -0
- package/lib/__tests__/doctor-machine-mutation.test.js +116 -0
- package/lib/__tests__/doctor-module-drift.test.js +206 -0
- package/lib/__tests__/doctor-module-resolution-parity.test.js +314 -0
- package/lib/__tests__/doctor-path-equivalence.test.js +59 -0
- package/lib/__tests__/doctor-repair-boundary.test.js +52 -0
- package/lib/__tests__/doctor-tui-skill-freshness.test.js +105 -0
- package/lib/__tests__/doctor.test.js +155 -0
- package/lib/__tests__/gate-runner.test.js +19 -3
- package/lib/__tests__/helpers/lib-sandbox.cjs +55 -0
- package/lib/__tests__/init.test.js +44 -17
- package/lib/__tests__/install-skill-full-dir.test.js +151 -0
- package/lib/__tests__/install-skill.test.js +51 -132
- package/lib/__tests__/language-tools-detect.test.js +158 -0
- package/lib/__tests__/phase-transition.test.js +15 -1
- package/lib/__tests__/qoder-delphi-agents.test.js +285 -0
- package/lib/__tests__/shared-paths.test.js +20 -0
- package/lib/__tests__/shared-utils.test.js +69 -0
- package/lib/__tests__/skill-flags.test.js +48 -0
- package/lib/__tests__/ui-detector.test.ts +6 -2
- package/lib/__tests__/uninstall.test.js +20 -0
- package/lib/__tests__/update-hooks.test.js +12 -0
- package/lib/__tests__/update-skill.test.js +7 -4
- package/lib/detect-deps.js +3 -20
- package/lib/doctor-tui.js +22 -5
- package/lib/doctor.js +1178 -31
- package/lib/init.js +184 -33
- package/lib/install-cmd.js +7 -4
- package/lib/install-skill.js +38 -95
- package/lib/language-tools.js +83 -4
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/shared-paths.js +5 -2
- package/lib/shared-utils.js +47 -3
- package/lib/uninstall.js +12 -0
- package/lib/update-hooks.js +4 -4
- package/lib/update-skill.js +6 -7
- package/mock-policy/AGENTS.md +4 -4
- package/mutation/AGENTS.md +4 -4
- package/mutation/__tests__/gate-m.test.ts +66 -14
- package/mutation/__tests__/mutmut-runner.test.ts +91 -42
- package/mutation/__tests__/test-filter-parity.test.ts +37 -0
- package/mutation/gate-m.ts +21 -8
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +2 -2
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/claude-code/skills/delphi-review/SKILL.md +98 -14
- package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +36 -3
- package/plugins/claude-code/skills/delphi-review/references/requirements.md +6 -6
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/dsh/README.md +169 -4
- package/plugins/dsh/dsh-overlay.patch.yml +67 -0
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/scripts/prepack.cjs +24 -19
- package/plugins/dsh/setup-dsh.ps1 +160 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
- package/plugins/dsh/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/dsh/skills/delphi-review/SKILL.md +98 -14
- package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +36 -3
- package/plugins/dsh/skills/delphi-review/references/requirements.md +6 -6
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +3 -3
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/dsh/src/command.test.ts +66 -2
- package/plugins/dsh/src/command.ts +36 -14
- package/plugins/dsh/src/contract.test.ts +31 -1
- package/plugins/dsh/src/delphi-config.test.ts +190 -0
- package/plugins/dsh/src/delphi-config.ts +223 -0
- package/plugins/dsh/src/delphi-run.test.ts +181 -0
- package/plugins/dsh/src/delphi-run.ts +129 -0
- package/plugins/dsh/src/delphi.test.ts +222 -0
- package/plugins/dsh/src/delphi.ts +247 -0
- package/plugins/dsh/src/dialect.test.ts +59 -0
- package/plugins/dsh/src/dialect.ts +40 -0
- package/plugins/dsh/src/index.ts +70 -4
- package/plugins/dsh/src/skills.test.ts +26 -19
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/opencode/skills/delphi-review/SKILL.md +98 -14
- package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +36 -3
- package/plugins/opencode/skills/delphi-review/references/requirements.md +6 -6
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/qoder/agents/delphi-architecture.md +1 -1
- package/plugins/qoder/agents/delphi-feasibility.md +1 -1
- package/plugins/qoder/agents/delphi-technical.md +1 -1
- package/plugins/qoder/plugin.json +2 -2
- package/plugins/qoder/skills/admin-template-guidelines/SKILL.md +6 -797
- package/plugins/qoder/skills/admin-template-guidelines/references/rule-1.md +91 -0
- package/plugins/qoder/skills/admin-template-guidelines/references/rule-2.md +155 -0
- package/plugins/qoder/skills/admin-template-guidelines/references/rule-3.md +70 -0
- package/plugins/qoder/skills/admin-template-guidelines/references/rule-4.md +154 -0
- package/plugins/qoder/skills/admin-template-guidelines/references/rule-5.md +126 -0
- package/plugins/qoder/skills/admin-template-guidelines/references/rule-6.md +180 -0
- package/plugins/qoder/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
- package/plugins/qoder/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/qoder/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +61 -28
- package/plugins/qoder/skills/delphi-review/AGENTS.md +4 -4
- package/plugins/qoder/skills/delphi-review/INSTALL.md +97 -98
- package/plugins/qoder/skills/delphi-review/SKILL.md +170 -63
- package/plugins/qoder/skills/delphi-review/hooks.md +19 -0
- package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +36 -3
- package/plugins/qoder/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
- package/plugins/qoder/skills/delphi-review/references/requirements.md +402 -0
- package/plugins/qoder/skills/delphi-review/references/round-templates.md +134 -0
- package/plugins/qoder/skills/delphi-review/scripts/verify-consensus.sh +43 -0
- package/plugins/qoder/skills/delphi-review/tools.md +29 -0
- package/plugins/qoder/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/qoder/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/qoder/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/qoder/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/qoder/skills/grilling/SKILL.md +12 -0
- package/plugins/qoder/skills/ralph-loop/SKILL.md +87 -428
- package/plugins/qoder/skills/ralph-loop/references/components.md +113 -0
- package/plugins/qoder/skills/ralph-loop/references/workflow-details.md +99 -0
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/qoder/skills/sprint-flow/SKILL.md +255 -454
- package/plugins/qoder/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
- package/plugins/qoder/skills/sprint-flow/hooks.md +18 -0
- package/plugins/qoder/skills/sprint-flow/references/components/memory.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/components/middleware.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/components/skill-invocations.md +21 -20
- package/plugins/qoder/skills/sprint-flow/references/components/tool-descriptions.md +22 -23
- package/plugins/qoder/skills/sprint-flow/references/force-levels.md +0 -1
- package/plugins/qoder/skills/sprint-flow/references/orchestration-rules.md +34 -27
- package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +160 -123
- package/plugins/qoder/skills/sprint-flow/references/phase-3-build.md +203 -2
- package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +107 -34
- package/plugins/qoder/skills/sprint-flow/references/phase-5-ship.md +194 -12
- package/plugins/qoder/skills/sprint-flow/references/phase-6-close.md +139 -7
- package/plugins/qoder/skills/sprint-flow/references/phase-overview.md +486 -0
- package/plugins/qoder/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
- package/plugins/qoder/skills/sprint-flow/tools.md +29 -0
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/qoder/skills/test-specification-alignment/SKILL.md +104 -601
- package/plugins/qoder/skills/to-issues/SKILL.md +1 -1
- package/principles/AGENTS.md +9 -8
- package/principles/__tests__/adapter-metrics.test.ts +292 -0
- package/principles/__tests__/analyzer.test.ts +29 -6
- package/principles/__tests__/baseline-prune-timeout.test.ts +71 -0
- package/principles/__tests__/baseline-prune.test.ts +338 -0
- package/principles/__tests__/boy-scout-init-baseline.test.ts +167 -0
- package/principles/__tests__/boy-scout-integration.test.ts +97 -6
- package/principles/__tests__/boy-scout.test.ts +253 -7
- package/principles/__tests__/config-enforcement.test.ts +469 -0
- package/principles/__tests__/config-git-timeout.test.ts +66 -0
- package/principles/__tests__/direct-execution.test.ts +187 -0
- package/principles/__tests__/index.test.ts +16 -3
- package/principles/__tests__/warnings-baseline-budgets.test.ts +74 -0
- package/principles/adapters/typescript.ts +613 -13
- package/principles/analyzer.ts +22 -0
- package/principles/baseline-prune.ts +217 -0
- package/principles/boy-scout.ts +481 -51
- package/principles/config.ts +362 -54
- package/principles/direct-execution.ts +60 -0
- package/principles/index.ts +40 -17
- package/principles/rules/__tests__/clean-code/many-exports.test.ts +16 -10
- package/principles/rules/clean-code/code-duplication.ts +8 -8
- package/principles/rules/clean-code/deep-nesting.ts +8 -8
- package/principles/rules/clean-code/god-class.ts +8 -8
- package/principles/rules/clean-code/large-file.ts +15 -11
- package/principles/rules/clean-code/long-function.ts +8 -8
- package/principles/rules/clean-code/magic-numbers.ts +10 -7
- package/principles/rules/clean-code/many-exports.ts +7 -7
- package/principles/rules/clean-code/missing-error-handling.ts +5 -5
- package/principles/rules/clean-code/too-many-params.ts +8 -8
- package/principles/rules/clean-code/unused-imports.ts +5 -5
- package/principles/rules/solid/dip.ts +18 -8
- package/principles/rules/solid/isp.ts +9 -8
- package/principles/rules/solid/lsp.ts +6 -6
- package/principles/rules/solid/ocp.ts +6 -5
- package/principles/rules/solid/srp.ts +7 -8
- package/principles/types.ts +15 -0
- package/scripts/__tests__/sync-package-content.test.js +28 -0
- package/scripts/delphi-external-review.cjs +193 -10
- package/scripts/sync-package-content.js +7 -4
- package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
- package/skills/delphi-review/AGENTS.md +4 -4
- package/skills/delphi-review/SKILL.md +98 -14
- package/skills/delphi-review/references/code-walkthrough.md +36 -3
- package/skills/delphi-review/references/requirements.md +6 -6
- package/skills/sprint-flow/AGENTS.md +4 -4
- package/skills/sprint-flow/references/phase-2-design.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +4 -4
- package/lib/__tests__/download-skill.test.js +0 -281
- package/lib/download-skill.js +0 -96
- package/plugins/qoder/skills/ralph-loop/references/components/memory.md +0 -55
- package/plugins/qoder/skills/ralph-loop/references/components/middleware.md +0 -54
- package/plugins/qoder/skills/ralph-loop/references/components/skill-invocations.md +0 -39
- package/plugins/qoder/skills/ralph-loop/references/components/system-prompt.md +0 -24
- package/plugins/qoder/skills/ralph-loop/references/components/tool-descriptions.md +0 -32
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Gate 1 TypeScript static analysis — the single place that decides HOW a project
|
|
4
|
+
# is type-checked (#436).
|
|
5
|
+
#
|
|
6
|
+
# Before this file, `githooks/pre-commit` and `githooks/adapters/typescript.sh` each
|
|
7
|
+
# hardcoded `npx tsc --noEmit`. Plain tsc cannot resolve `.vue` single-file
|
|
8
|
+
# components, so every `import X from './Foo.vue'` became a TS2307 and a Vue project
|
|
9
|
+
# whose own `vue-tsc --noEmit` run is clean was blocked on every commit. Because
|
|
10
|
+
# pre-commit runs under `set -o pipefail`, those false findings really did block.
|
|
11
|
+
#
|
|
12
|
+
# Resolution order:
|
|
13
|
+
# 1. GATE_TS_TYPECHECK_CMD explicit override, same precedent as SKIP_VERSION_CHECK
|
|
14
|
+
# 2. package.json scripts.typecheck
|
|
15
|
+
# 3. package.json scripts.type-check (the name create-vue generates)
|
|
16
|
+
# 4. `npx tsc --noEmit --skipLibCheck` — the pre-#436 behavior, unchanged
|
|
17
|
+
#
|
|
18
|
+
# Return codes follow the gate exit-code contract: 0 clean, 1 findings, 2 checker
|
|
19
|
+
# unavailable (a tool failure must never be read as "no type errors").
|
|
20
|
+
|
|
21
|
+
resolve_typecheck_command() {
|
|
22
|
+
if [ -n "${GATE_TS_TYPECHECK_CMD:-}" ]; then
|
|
23
|
+
printf '%s' "$GATE_TS_TYPECHECK_CMD"
|
|
24
|
+
return 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
[ -f "package.json" ] || return 1
|
|
28
|
+
command -v node >/dev/null 2>&1 || return 1
|
|
29
|
+
|
|
30
|
+
local declared_script
|
|
31
|
+
declared_script=$(node -e '
|
|
32
|
+
let pkg;
|
|
33
|
+
try {
|
|
34
|
+
pkg = JSON.parse(require("fs").readFileSync("package.json", "utf8"));
|
|
35
|
+
} catch {
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
const scripts = (pkg && pkg.scripts) || {};
|
|
39
|
+
if (scripts.typecheck) process.stdout.write("typecheck");
|
|
40
|
+
else if (scripts["type-check"]) process.stdout.write("type-check");
|
|
41
|
+
' 2>/dev/null) || return 1
|
|
42
|
+
|
|
43
|
+
[ -n "$declared_script" ] || return 1
|
|
44
|
+
|
|
45
|
+
printf 'npm run %s' "$declared_script"
|
|
46
|
+
return 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# The #293 extra pass: projects commonly exclude test files from tsconfig.json, so
|
|
50
|
+
# their type errors accumulate silently. Only meaningful for the tsc fallback —
|
|
51
|
+
# a project checker covers the files its own config declares.
|
|
52
|
+
run_test_file_pass() {
|
|
53
|
+
local has_tests_in_tsc=false
|
|
54
|
+
if npx tsc --noEmit --skipLibCheck --listFiles 2>/dev/null | grep -qE '__tests__/|\.test\.ts|\.spec\.ts'; then
|
|
55
|
+
has_tests_in_tsc=true
|
|
56
|
+
fi
|
|
57
|
+
[ "$has_tests_in_tsc" = false ] || return 0
|
|
58
|
+
|
|
59
|
+
local test_pass_exit=0
|
|
60
|
+
if [ -f "tsconfig.tests.json" ]; then
|
|
61
|
+
echo "Checking test files with tsconfig.tests.json..."
|
|
62
|
+
npx tsc --noEmit --project tsconfig.tests.json 2>&1 | head -30
|
|
63
|
+
test_pass_exit=${PIPESTATUS[0]}
|
|
64
|
+
else
|
|
65
|
+
echo "Checking test files for type errors..."
|
|
66
|
+
local temp_tsconfig=".tsconfig.withtests.json"
|
|
67
|
+
node -e "
|
|
68
|
+
const cfg = JSON.parse(require('fs').readFileSync('tsconfig.json','utf8'));
|
|
69
|
+
delete cfg.exclude;
|
|
70
|
+
cfg.include = cfg.include || ['src/**/*'];
|
|
71
|
+
cfg.include.push('src/**/__tests__/**','src/**/*.test.ts','src/**/*.spec.ts');
|
|
72
|
+
require('fs').writeFileSync('$temp_tsconfig', JSON.stringify(cfg, null, 2));
|
|
73
|
+
" 2>/dev/null
|
|
74
|
+
npx tsc --noEmit --project "$temp_tsconfig" --skipLibCheck 2>&1 | head -30
|
|
75
|
+
test_pass_exit=${PIPESTATUS[0]}
|
|
76
|
+
rm -f "$temp_tsconfig"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
if [ "$test_pass_exit" -ne 0 ]; then
|
|
80
|
+
echo ""
|
|
81
|
+
echo "❌ BLOCKED - TYPE ERRORS in test files"
|
|
82
|
+
echo "Your project's tsconfig.json excludes test files from type checking."
|
|
83
|
+
echo "Add a tsconfig.tests.json to customise test file checking."
|
|
84
|
+
return 1
|
|
85
|
+
fi
|
|
86
|
+
return 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
run_typescript_typecheck() {
|
|
90
|
+
if ! command -v npx >/dev/null 2>&1; then
|
|
91
|
+
echo "ℹ️ SKIP - npx not available (no TypeScript checker)"
|
|
92
|
+
return 2
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
local project_checker
|
|
96
|
+
if project_checker=$(resolve_typecheck_command); then
|
|
97
|
+
# Word-split deliberately on the command name only: `npm run <script>` from
|
|
98
|
+
# package.json, or a user-set GATE_TS_TYPECHECK_CMD. Never a path list.
|
|
99
|
+
local checker_argv=()
|
|
100
|
+
read -r -a checker_argv <<< "$project_checker"
|
|
101
|
+
echo "Running project typecheck (${project_checker})..."
|
|
102
|
+
"${checker_argv[@]}" 2>&1 | head -30
|
|
103
|
+
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
|
104
|
+
echo ""
|
|
105
|
+
echo "❌ BLOCKED - TYPE ERRORS detected by the project's own typecheck script"
|
|
106
|
+
echo "Fix the type errors above before committing."
|
|
107
|
+
return 1
|
|
108
|
+
fi
|
|
109
|
+
echo "✅ PASSED - project typecheck."
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Verify tsc is the real compiler (npx may fetch the deprecated tsc@2.0.4 placeholder).
|
|
114
|
+
local tsc_version
|
|
115
|
+
tsc_version=$(npx tsc --version 2>&1)
|
|
116
|
+
if ! echo "$tsc_version" | grep -q "Version"; then
|
|
117
|
+
echo "ℹ️ SKIP - tsc not properly installed ($tsc_version)"
|
|
118
|
+
echo "Install typescript (npm install typescript --save-dev) for type checking."
|
|
119
|
+
return 2
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if [ ! -f "tsconfig.json" ]; then
|
|
123
|
+
echo "ℹ️ SKIP - tsconfig.json not found (no TypeScript project config)"
|
|
124
|
+
return 2
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
echo "Running TypeScript static analysis ($tsc_version)..."
|
|
128
|
+
npx tsc --noEmit --skipLibCheck 2>&1 | head -30
|
|
129
|
+
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
|
130
|
+
echo ""
|
|
131
|
+
echo "❌ BLOCKED - TYPE ERRORS detected"
|
|
132
|
+
echo "Fix the type errors above before committing."
|
|
133
|
+
return 1
|
|
134
|
+
fi
|
|
135
|
+
echo "✅ PASSED - TypeScript static analysis."
|
|
136
|
+
|
|
137
|
+
if [ -d "src/__tests__" ] || [ -d "src/tests" ] || [ -d "tests" ] || [ -d "__tests__" ]; then
|
|
138
|
+
run_test_file_pass || return 1
|
|
139
|
+
fi
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
@@ -6,6 +6,17 @@ const fs = require('node:fs');
|
|
|
6
6
|
const REQUIRED_ROLES = ['architecture', 'technical', 'feasibility'];
|
|
7
7
|
const VALIDITY_MS = 60 * 60 * 1000;
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Allowed provenance values for an expert record.
|
|
11
|
+
*
|
|
12
|
+
* `channel` is an EXPLICIT whitelist. An absent field is rejected rather than
|
|
13
|
+
* defaulted: treating "missing" as `local` (or as anything else) would be a
|
|
14
|
+
* zero-cost bypass, since omitting one key would let evidence that never came
|
|
15
|
+
* from a real model call pass the gate. Only these exact strings are accepted;
|
|
16
|
+
* casing and whitespace are not normalised.
|
|
17
|
+
*/
|
|
18
|
+
const VALID_CHANNELS = ['external', 'local'];
|
|
19
|
+
|
|
9
20
|
function fail(message) {
|
|
10
21
|
console.error(`Invalid code walkthrough evidence: ${message}`);
|
|
11
22
|
process.exit(1);
|
|
@@ -47,7 +58,7 @@ function parseTimestamp(value, field) {
|
|
|
47
58
|
return date.getTime();
|
|
48
59
|
}
|
|
49
60
|
|
|
50
|
-
function validateExpert(expert, index, roles, models) {
|
|
61
|
+
function validateExpert(expert, index, roles, models, resolvedModels) {
|
|
51
62
|
if (!isPlainObject(expert)) fail(`experts[${index}] must be a plain object.`);
|
|
52
63
|
if (!REQUIRED_ROLES.includes(expert.role)) fail(`experts[${index}].role is invalid.`);
|
|
53
64
|
if (roles.has(expert.role)) fail(`expert role ${expert.role} is duplicated.`);
|
|
@@ -61,6 +72,22 @@ function validateExpert(expert, index, roles, models) {
|
|
|
61
72
|
fail(`expert ${expert.role} contains an error or fallback marker.`);
|
|
62
73
|
}
|
|
63
74
|
|
|
75
|
+
// Provenance is mandatory and must be an exact whitelist value (#423). Without
|
|
76
|
+
// this, three hand-written records with distinct model NAMES satisfied the old
|
|
77
|
+
// check while no model was ever called.
|
|
78
|
+
if (!VALID_CHANNELS.includes(expert.channel)) {
|
|
79
|
+
fail(
|
|
80
|
+
`expert ${expert.role} channel must be one of: ${VALID_CHANNELS.join(', ')}.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// requested_model is compared EXACTLY (after trimming); resolved_model below is
|
|
85
|
+
// compared case-INSENSITIVELY. The asymmetry is intentional, not an oversight:
|
|
86
|
+
// a requested ID is a case-sensitive gateway identifier and AGENTS.md requires
|
|
87
|
+
// "three distinct trimmed requested model IDs", whereas resolved_model answers
|
|
88
|
+
// "did these actually run on one model?" -- and gateways do report the same model
|
|
89
|
+
// with different casing per call, which is how #423 slipped through. Pinned by
|
|
90
|
+
// scripts/__tests__/gate-mw-provenance.test.cjs (AC-423-08).
|
|
64
91
|
if (typeof expert.requested_model !== 'string' || expert.requested_model.trim() === '') {
|
|
65
92
|
fail(`expert ${expert.role} requested_model must be non-empty.`);
|
|
66
93
|
}
|
|
@@ -68,10 +95,18 @@ function validateExpert(expert, index, roles, models) {
|
|
|
68
95
|
if (models.has(model)) fail(`requested_model ${model} is duplicated.`);
|
|
69
96
|
models.add(model);
|
|
70
97
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
// `resolved_model` must be a real, DISTINCT model. Previously null was allowed,
|
|
99
|
+
// so an expert that never resolved a model still counted as successful; and
|
|
100
|
+
// nothing compared resolved models, so all three could report the same one
|
|
101
|
+
// while `requested_model` differed.
|
|
102
|
+
if (typeof expert.resolved_model !== 'string' || expert.resolved_model.trim() === '') {
|
|
103
|
+
fail(`expert ${expert.role} resolved_model must be a non-empty string.`);
|
|
104
|
+
}
|
|
105
|
+
const resolved = expert.resolved_model.trim().toLowerCase();
|
|
106
|
+
if (resolvedModels.has(resolved)) {
|
|
107
|
+
fail(`expert ${expert.role} resolved_model ${expert.resolved_model.trim()} is duplicated.`);
|
|
74
108
|
}
|
|
109
|
+
resolvedModels.add(resolved);
|
|
75
110
|
}
|
|
76
111
|
|
|
77
112
|
function validateEvidence(evidence, expectedCommit, expectedBranch, now) {
|
|
@@ -100,8 +135,11 @@ function validateEvidence(evidence, expectedCommit, expectedBranch, now) {
|
|
|
100
135
|
}
|
|
101
136
|
const roles = new Set();
|
|
102
137
|
const models = new Set();
|
|
138
|
+
// Scoped to this validation run, so one document's models cannot leak into the
|
|
139
|
+
// next (a long-lived process validating several pushes).
|
|
140
|
+
const resolvedModels = new Set();
|
|
103
141
|
evidence.experts.forEach((expert, index) => {
|
|
104
|
-
validateExpert(expert, index, roles, models);
|
|
142
|
+
validateExpert(expert, index, roles, models, resolvedModels);
|
|
105
143
|
});
|
|
106
144
|
if (roles.size !== REQUIRED_ROLES.length) fail('all required expert roles must be present.');
|
|
107
145
|
}
|
package/hooks/post-merge
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# post-merge — XP-Gate VERSION sync hook
|
|
4
|
+
#
|
|
5
|
+
# Detects whether VERSION changed during a merge (via ORIG_HEAD comparison)
|
|
6
|
+
# and, if so, runs scripts/sync-version.cjs (cross-platform Node — preferred)
|
|
7
|
+
# or falls back to scripts/sync-version.sh (legacy, kept for compatibility).
|
|
8
|
+
#
|
|
9
|
+
# Safety features:
|
|
10
|
+
# - Guard file (.xp-gate/.version-synced) prevents infinite loops
|
|
11
|
+
# - Runtime check (node/bash) before running the sync script
|
|
12
|
+
# - [skip ci] in auto-commit message to prevent recursive CI triggers
|
|
13
|
+
# - ORIG_HEAD fallback: if ORIG_HEAD is missing (shallow clone / first commit),
|
|
14
|
+
# the check is skipped with a warning.
|
|
15
|
+
#
|
|
16
|
+
set -e
|
|
17
|
+
|
|
18
|
+
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '')"
|
|
19
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
20
|
+
# not a git repo — silently exit
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
GUARD_FILE="$PROJECT_ROOT/.xp-gate/.version-synced"
|
|
25
|
+
|
|
26
|
+
# ── Guard: skip if already synced for this commit ──
|
|
27
|
+
CURRENT_HEAD="$(git rev-parse HEAD 2>/dev/null || echo '')"
|
|
28
|
+
if [[ -f "$GUARD_FILE" ]]; then
|
|
29
|
+
read -r GUARD_HASH < "$GUARD_FILE" 2>/dev/null || true
|
|
30
|
+
if [[ "$GUARD_HASH" == "$CURRENT_HEAD" ]]; then
|
|
31
|
+
# already synced for this commit — skip
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ── Only proceed if this is an xp-gate project ──
|
|
37
|
+
SYNC_CJS="$PROJECT_ROOT/scripts/sync-version.cjs"
|
|
38
|
+
SYNC_SH="$PROJECT_ROOT/scripts/sync-version.sh"
|
|
39
|
+
if [[ ! -f "$SYNC_CJS" && ! -f "$SYNC_SH" ]]; then
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# ── ORIG_HEAD check: detect VERSION change during merge ──
|
|
44
|
+
ORIG_HEAD="$(git rev-parse ORIG_HEAD 2>/dev/null || echo '')"
|
|
45
|
+
if [[ -z "$ORIG_HEAD" ]]; then
|
|
46
|
+
# ORIG_HEAD not available (shallow clone, first commit, or non-merge operation)
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
VERSION_CHANGED=false
|
|
51
|
+
if git diff-tree --name-only -r "$ORIG_HEAD" HEAD 2>/dev/null | grep -q '^VERSION$'; then
|
|
52
|
+
VERSION_CHANGED=true
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [[ "$VERSION_CHANGED" != "true" ]]; then
|
|
56
|
+
# VERSION not changed — zero overhead
|
|
57
|
+
exit 0
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# ── Run sync-version — .cjs preferred (cross-platform, covers more targets) ──
|
|
61
|
+
SYNC_OK=false
|
|
62
|
+
|
|
63
|
+
if [[ -f "$SYNC_CJS" ]] && command -v node &>/dev/null; then
|
|
64
|
+
echo "[xp-gate] VERSION changed in merge — running sync-version.cjs..."
|
|
65
|
+
if node "$SYNC_CJS"; then
|
|
66
|
+
SYNC_OK=true
|
|
67
|
+
else
|
|
68
|
+
echo "[xp-gate] Warning: sync-version.cjs failed — VERSION files may be out of sync." >&2
|
|
69
|
+
fi
|
|
70
|
+
elif [[ -f "$SYNC_SH" ]]; then
|
|
71
|
+
# Legacy fallback — the .sh implementation is Unix-only (MSYS path bug on Windows)
|
|
72
|
+
if ! command -v bash &>/dev/null; then
|
|
73
|
+
echo "[xp-gate] Warning: bash not found — cannot run sync-version.sh" >&2
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
echo "[xp-gate] VERSION changed in merge — running sync-version.sh (fallback)..."
|
|
77
|
+
if bash "$SYNC_SH"; then
|
|
78
|
+
SYNC_OK=true
|
|
79
|
+
else
|
|
80
|
+
echo "[xp-gate] Warning: sync-version.sh failed — VERSION files may be out of sync." >&2
|
|
81
|
+
fi
|
|
82
|
+
else
|
|
83
|
+
echo "[xp-gate] Warning: node not found — cannot run sync-version.cjs" >&2
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [[ "$SYNC_OK" == "true" ]]; then
|
|
87
|
+
# record guard
|
|
88
|
+
mkdir -p "$(dirname "$GUARD_FILE")"
|
|
89
|
+
echo "$CURRENT_HEAD" > "$GUARD_FILE"
|
|
90
|
+
echo "[xp-gate] VERSION synced. Affected files:"
|
|
91
|
+
echo " - VERSION"
|
|
92
|
+
echo " - package.json (+ src/npm-package/package.json)"
|
|
93
|
+
echo " - plugins/{claude-code,opencode,qoder,dsh} manifests"
|
|
94
|
+
echo " - src/npm-package/plugins/** mirrors"
|
|
95
|
+
echo " - AGENTS.md headers (date/commit/branch/version)"
|
|
96
|
+
echo "[skip ci]"
|
|
97
|
+
echo "Run 'git add' and 'git commit' to commit these changes."
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
exit 0
|