@boyingliu01/xp-gate 0.27.4 → 0.27.6
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-3.sh +59 -3
- package/adapters/gate-4.sh +73 -10
- package/adapters/gate-7.sh +4 -0
- package/adapters/powershell.sh +8 -0
- package/gate-10.sh +11 -0
- package/gate-3.sh +8 -0
- package/gate-4.sh +8 -0
- package/gate-7.sh +4 -0
- package/hooks/gate-10.sh +11 -0
- package/hooks/gate-3.sh +8 -0
- package/hooks/gate-4.sh +8 -0
- package/hooks/gate-7.sh +4 -0
- package/hooks/pre-commit +53 -11
- package/lib/__tests__/doctor-delphi.test.js +293 -244
- package/lib/doctor.js +46 -3
- package/lib/mattpocock-license-verdict.json +1 -1
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/batch-grill-me/SKILL.md +9 -2
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- 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/dsh/package.json +1 -1
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +9 -2
- package/plugins/dsh/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/batch-grill-me/SKILL.md +9 -2
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- 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/skills/batch-grill-me/SKILL.md +9 -2
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
package/hooks/pre-commit
CHANGED
|
@@ -215,34 +215,60 @@ file_exists_in_subdir() {
|
|
|
215
215
|
AUDIT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
216
216
|
PROJECT_ROOT_FOR_AUDIT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$AUDIT_SCRIPT_DIR/../..")"
|
|
217
217
|
|
|
218
|
+
# Issue #535: a failed `source` used to print one stderr line and keep going,
|
|
219
|
+
# so a missing lib silently removed gate logic from the run while every gate
|
|
220
|
+
# still reported PASS. Fail closed instead: abort with FATAL when the file is
|
|
221
|
+
# absent or its body exits non-zero while being sourced. `exit` inside a
|
|
222
|
+
# sourced file exits the SOURCING shell, so an `if ! source` wrapper cannot
|
|
223
|
+
# catch it — trap EXIT for the duration of the source instead.
|
|
224
|
+
source_gate_lib() {
|
|
225
|
+
local lib_file="$1"
|
|
226
|
+
if [ ! -f "$lib_file" ]; then
|
|
227
|
+
echo "FATAL: cannot load $lib_file (missing)" >&2
|
|
228
|
+
exit 1
|
|
229
|
+
fi
|
|
230
|
+
# Marker the EXIT trap compares against: only a source that never reached
|
|
231
|
+
# its end counts as a load failure. A lib calling `exit` at top level would
|
|
232
|
+
# otherwise be misread as a crash after it already did its job.
|
|
233
|
+
SOURCE_GATE_LIB_COMPLETED=""
|
|
234
|
+
local prev_trap=""
|
|
235
|
+
prev_trap="$(trap -p EXIT || true)"
|
|
236
|
+
# shellcheck disable=SC2064 # expand lib_file NOW: it is the failure target
|
|
237
|
+
trap "[ -n \"\$SOURCE_GATE_LIB_COMPLETED\" ] || { echo 'FATAL: cannot load $lib_file (source failed)' >&2; exit 1; }" EXIT
|
|
238
|
+
# shellcheck disable=SC1090 # sourcing the resolved lib is the contract here
|
|
239
|
+
source "$lib_file"
|
|
240
|
+
SOURCE_GATE_LIB_COMPLETED=1
|
|
241
|
+
eval "$prev_trap"
|
|
242
|
+
}
|
|
243
|
+
|
|
218
244
|
# Issue #370: source cross-platform now_ms() helper (node-preferred, regex-validated)
|
|
219
|
-
|
|
245
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/now-ms.sh"
|
|
220
246
|
|
|
221
247
|
# Issue #473 (+ #454): Gate 5's vitest exit-code judgement, sourced as a library
|
|
222
248
|
# so the fail-closed contract is unit-testable without executing this hook.
|
|
223
|
-
|
|
249
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/test-failure.sh"
|
|
224
250
|
|
|
225
251
|
# Issue #477: Gate 2's jscpd config probe and outcome classification, same reason.
|
|
226
|
-
|
|
252
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/jscpd-run.sh"
|
|
227
253
|
|
|
228
254
|
# Issue #476: Gate 11's sprint-gate.sh resolution chain and decision translation.
|
|
229
|
-
|
|
255
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/sprint-gate-report.sh"
|
|
230
256
|
|
|
231
257
|
# Issue #523: Gate 5's Python coverage parsing and threshold verdict. The old
|
|
232
258
|
# `grep -o '[0-9]*%'` never matched --format=total's bare number, so the 80%
|
|
233
259
|
# branch never ran; sourced as a library so the fail-closed contract is
|
|
234
260
|
# unit-testable without executing this hook.
|
|
235
|
-
|
|
261
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/coverage-total.sh"
|
|
236
262
|
|
|
237
263
|
# Issue #399: Python smart test selection — strategy decision + related-test
|
|
238
264
|
# lookup, kept in a library so the truth table and the path conventions are
|
|
239
265
|
# unit-testable without executing this hook.
|
|
240
|
-
|
|
266
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/python-test-selection.sh"
|
|
241
267
|
|
|
242
268
|
# Issue #534: incremental Python type-check for the pre-commit python branch —
|
|
243
269
|
# staged-only mypy (fast) that complements python.sh's CI-aligned full pass.
|
|
244
270
|
# Kept in a library so the exit-code capture and scope handling are unit-testable.
|
|
245
|
-
|
|
271
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/python-incremental-typecheck.sh"
|
|
246
272
|
|
|
247
273
|
# Read max_duration_ms from .xp-gate-config.json (default 7200000 = 2 hours)
|
|
248
274
|
_MAX_DURATION_MS=$(node -e "try{const c=JSON.parse(require('fs').readFileSync('${PROJECT_ROOT_FOR_AUDIT}/.xp-gate-config.json','utf8'));console.log((c.audit&&c.audit.max_duration_ms)||7200000);}catch(e){console.log(7200000);}" 2>/dev/null || echo "7200000")
|
|
@@ -1061,7 +1087,7 @@ else
|
|
|
1061
1087
|
; source "${AUDIT_SCRIPT_DIR}/lib/typecheck.sh" && run_typescript_typecheck )
|
|
1062
1088
|
TS_CHECK_EXIT=$?
|
|
1063
1089
|
else
|
|
1064
|
-
|
|
1090
|
+
source_gate_lib "${AUDIT_SCRIPT_DIR}/lib/typecheck.sh"
|
|
1065
1091
|
run_typescript_typecheck
|
|
1066
1092
|
TS_CHECK_EXIT=$?
|
|
1067
1093
|
fi
|
|
@@ -3264,10 +3290,14 @@ if any_changed_files_match "\.(ts|tsx|js|jsx|py|go|java|kt|cpp|c|swift|m|dart|sh
|
|
|
3264
3290
|
else
|
|
3265
3291
|
echo "⏭️ SKIPPED - SAST (gate-10.sh not found)"
|
|
3266
3292
|
GATE_10_STATUS="SKIP"
|
|
3293
|
+
GATE_10_REASON="gate_script_missing"
|
|
3267
3294
|
fi
|
|
3268
3295
|
else
|
|
3269
|
-
echo "
|
|
3270
|
-
|
|
3296
|
+
echo "⏭️ SKIPPED - No code files changed, skipping SAST scan."
|
|
3297
|
+
# #536: "nothing to scan" is NOT "scanned and clean". Record SKIP with a
|
|
3298
|
+
# reason so the quality report never renders this path as a PASS claim.
|
|
3299
|
+
GATE_10_STATUS="SKIP"
|
|
3300
|
+
GATE_10_REASON="no_code_files_changed"
|
|
3271
3301
|
fi
|
|
3272
3302
|
# ============================================================================
|
|
3273
3303
|
|
|
@@ -3389,6 +3419,11 @@ generate_quality_report() {
|
|
|
3389
3419
|
# non-passing gates INSIDE the effective denominator only -- a skipped gate
|
|
3390
3420
|
# is out of scope, not a failure.
|
|
3391
3421
|
local gate_status gate_default
|
|
3422
|
+
# #536: a WARN means the check could not run (typically tool_not_installed)
|
|
3423
|
+
# -- degraded coverage, kept in the denominator but flagged separately so a
|
|
3424
|
+
# degraded gate is never confused with a real PASS.
|
|
3425
|
+
local DEGRADED_COUNT=0
|
|
3426
|
+
local DEGRADED_JSON=""
|
|
3392
3427
|
for gate in 1 2 3 4 5 6 7 8 9 10 11 12; do
|
|
3393
3428
|
local status_var="GATE_${gate}_STATUS"
|
|
3394
3429
|
# Default mirrors the JSON report rendering below EXACTLY (gates 1-11
|
|
@@ -3401,7 +3436,7 @@ generate_quality_report() {
|
|
|
3401
3436
|
case "$gate_status" in
|
|
3402
3437
|
PASS) PASSED_COUNT=$((PASSED_COUNT + 1)) ;;
|
|
3403
3438
|
SKIP) SKIP_COUNT=$((SKIP_COUNT + 1)) ;;
|
|
3404
|
-
WARN) WARN_COUNT=$((WARN_COUNT + 1)); NONPASS_COUNT=$((NONPASS_COUNT + 1)) ;;
|
|
3439
|
+
WARN) WARN_COUNT=$((WARN_COUNT + 1)); NONPASS_COUNT=$((NONPASS_COUNT + 1)); DEGRADED_COUNT=$((DEGRADED_COUNT + 1)); DEGRADED_JSON="${DEGRADED_JSON:+$DEGRADED_JSON, }\"gate${gate}\"";;
|
|
3405
3440
|
*) NONPASS_COUNT=$((NONPASS_COUNT + 1)) ;;
|
|
3406
3441
|
esac
|
|
3407
3442
|
done
|
|
@@ -3480,6 +3515,8 @@ generate_quality_report() {
|
|
|
3480
3515
|
"gatesPassed": $PASSED_COUNT,
|
|
3481
3516
|
"gatesTotal": $TOTAL_GATES,
|
|
3482
3517
|
"gatesSkipped": $SKIP_COUNT,
|
|
3518
|
+
"gatesDegraded": $DEGRADED_COUNT,
|
|
3519
|
+
"degradedGates": [$DEGRADED_JSON],
|
|
3483
3520
|
"gatesEffective": $EFFECTIVE_TOTAL,
|
|
3484
3521
|
"effectivePassRate": $EFFECTIVE_PASS_RATE_JSON,
|
|
3485
3522
|
"score": $SCORE_JSON,
|
|
@@ -3499,6 +3536,7 @@ generate_quality_report() {
|
|
|
3499
3536
|
"gate3_complexity": {
|
|
3500
3537
|
"name": "Cyclomatic Complexity",
|
|
3501
3538
|
"status": "${GATE_3_STATUS:-PASS}",
|
|
3539
|
+
"reason": "${GATE_3_REASON:-}",
|
|
3502
3540
|
"threshold": "${CCN_THRESHOLD:-5}",
|
|
3503
3541
|
"blockThreshold": "${CCN_THRESHOLD:-5}",
|
|
3504
3542
|
"warnings": ${CC_WARNINGS:-0}
|
|
@@ -3506,6 +3544,7 @@ generate_quality_report() {
|
|
|
3506
3544
|
"gate4_principles": {
|
|
3507
3545
|
"name": "Clean Code + SOLID",
|
|
3508
3546
|
"status": "${GATE_4_STATUS:-PASS}",
|
|
3547
|
+
"reason": "${GATE_4_REASON:-}",
|
|
3509
3548
|
"warnings": ${WARNING_COUNT:-0}
|
|
3510
3549
|
},
|
|
3511
3550
|
"gate5_tests": {
|
|
@@ -3524,6 +3563,7 @@ generate_quality_report() {
|
|
|
3524
3563
|
"gate7_iac_security": {
|
|
3525
3564
|
"name": "IaC Security Scanning",
|
|
3526
3565
|
"status": "${GATE_7_STATUS:-PASS}",
|
|
3566
|
+
"scanned_files": ${GATE_7_SCANNED_FILES:-0},
|
|
3527
3567
|
"tools": "checkov, hadolint, kube-score, tflint"
|
|
3528
3568
|
},
|
|
3529
3569
|
"gate8_secret_scanning": {
|
|
@@ -3539,6 +3579,8 @@ generate_quality_report() {
|
|
|
3539
3579
|
"gate10_sast": {
|
|
3540
3580
|
"name": "SAST Security Scan",
|
|
3541
3581
|
"status": "${GATE_10_STATUS:-PASS}",
|
|
3582
|
+
"reason": "${GATE_10_REASON:-}",
|
|
3583
|
+
"scanned_files": ${GATE_10_SCANNED_FILES:-0},
|
|
3542
3584
|
"tool": "semgrep"
|
|
3543
3585
|
},
|
|
3544
3586
|
"gate11_sprint_flow": {
|