@boyingliu01/xp-gate 0.27.4 → 0.27.5
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-3.sh +59 -3
- package/adapters/gate-4.sh +73 -10
- package/adapters/gate-7.sh +4 -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 +8 -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 +0 -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/gate-8.sh +0 -0
- package/gate-9.sh +0 -0
- package/hooks/adapter-common.sh +0 -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/gate-8.sh +0 -0
- package/hooks/gate-9.sh +0 -0
- package/hooks/post-merge +0 -0
- package/hooks/pre-commit +53 -11
- package/hooks/pre-push +0 -0
- package/hooks/sprint-gate.sh +0 -0
- package/lib/__tests__/doctor-delphi.test.js +293 -244
- package/lib/__tests__/sprint-audit.test.js +207 -207
- package/lib/doctor.js +46 -3
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/sprint-audit.js +235 -235
- 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/bin/delphi-review-guard.sh +0 -0
- package/plugins/claude-code/bin/xp-gate-check +0 -0
- 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/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/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/sprint-gate.sh +0 -0
- package/plugins/dsh/lib/command.d.ts +0 -32
- package/plugins/dsh/lib/command.d.ts.map +0 -1
- package/plugins/dsh/lib/command.js +0 -81
- package/plugins/dsh/lib/delphi-config.d.ts +0 -75
- package/plugins/dsh/lib/delphi-config.d.ts.map +0 -1
- package/plugins/dsh/lib/delphi-config.js +0 -183
- package/plugins/dsh/lib/delphi-run.d.ts +0 -24
- package/plugins/dsh/lib/delphi-run.d.ts.map +0 -1
- package/plugins/dsh/lib/delphi-run.js +0 -92
- package/plugins/dsh/lib/delphi.d.ts +0 -88
- package/plugins/dsh/lib/delphi.d.ts.map +0 -1
- package/plugins/dsh/lib/delphi.js +0 -161
- package/plugins/dsh/lib/dialect.d.ts +0 -24
- package/plugins/dsh/lib/dialect.d.ts.map +0 -1
- package/plugins/dsh/lib/dialect.js +0 -36
- package/plugins/dsh/lib/gate-runner.d.ts +0 -18
- package/plugins/dsh/lib/gate-runner.d.ts.map +0 -1
- package/plugins/dsh/lib/gate-runner.js +0 -46
- package/plugins/dsh/lib/index.d.ts +0 -5
- package/plugins/dsh/lib/index.d.ts.map +0 -1
- package/plugins/dsh/lib/index.js +0 -162
- package/plugins/dsh/node_modules/.vite/vitest/results.json +0 -1
package/adapter-common.sh
CHANGED
|
File without changes
|
package/adapters/cpp.sh
CHANGED
|
File without changes
|
package/adapters/dart.sh
CHANGED
|
File without changes
|
package/adapters/flutter.sh
CHANGED
|
File without changes
|
package/adapters/gate-3.sh
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
1
2
|
# ============================================================================
|
|
2
3
|
# GATE 3: Cyclomatic Complexity Check
|
|
3
4
|
# Uses lizard - checks cyclomatic complexity of changed source files
|
|
@@ -6,18 +7,62 @@
|
|
|
6
7
|
2>&1 echo ""
|
|
7
8
|
2>&1 echo "→ Gate 3: Cyclomatic complexity..."
|
|
8
9
|
GATE_3_START=$(gate_start_ms)
|
|
10
|
+
# #537: every non-run records WHY so the report can say why a gate is SKIP.
|
|
11
|
+
GATE_3_REASON=""
|
|
9
12
|
|
|
10
13
|
if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
11
14
|
echo "⏭️ SKIPPED - Complexity (documentation project)."
|
|
12
15
|
GATE_3_STATUS="SKIP"
|
|
16
|
+
GATE_3_REASON="documentation_project"
|
|
13
17
|
|
|
14
18
|
elif [ "$PROJECT_LANG" = "powershell" ]; then
|
|
15
19
|
echo "ℹ️ No PowerShell Clean Code / SOLID tool available"
|
|
16
20
|
echo "⏭️ SKIPPED - Complexity (no PowerShell tool)"
|
|
17
21
|
GATE_3_STATUS="SKIP"
|
|
22
|
+
# #537: a PowerShell-primary repo was invisible to this dimension while the
|
|
23
|
+
# score implied full coverage — say exactly why nothing ran.
|
|
24
|
+
GATE_3_REASON="no_powershell_tool"
|
|
18
25
|
|
|
19
26
|
else
|
|
20
|
-
|
|
27
|
+
# S2 (#507): three-level CCN threshold override --
|
|
28
|
+
# .xp-gate/ccn-threshold (positive integer) > XP_GATE_CCN_THRESHOLD > 5.
|
|
29
|
+
# Invalid values WARN on stderr and fall back to the default; the resolved
|
|
30
|
+
# value reaches the gate-3 audit record via the --detail argument below.
|
|
31
|
+
# Implemented with bash builtins only (no tr/grep spawns): every process
|
|
32
|
+
# spawn is expensive on Windows under AV filter drivers.
|
|
33
|
+
resolve_ccn_threshold() {
|
|
34
|
+
local default=5 val
|
|
35
|
+
if [ -f ".xp-gate/ccn-threshold" ]; then
|
|
36
|
+
val=$(<".xp-gate/ccn-threshold")
|
|
37
|
+
val="${val//[[:space:]]/}"
|
|
38
|
+
case "$val" in
|
|
39
|
+
''|*[!0-9]*|0)
|
|
40
|
+
echo "WARN - invalid .xp-gate/ccn-threshold value '${val}' (expected a positive integer), falling back to ${default}" >&2
|
|
41
|
+
;;
|
|
42
|
+
*)
|
|
43
|
+
echo "$val"
|
|
44
|
+
return 0
|
|
45
|
+
;;
|
|
46
|
+
esac
|
|
47
|
+
echo "$default"
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
val=${XP_GATE_CCN_THRESHOLD:-}
|
|
51
|
+
if [ -n "$val" ]; then
|
|
52
|
+
case "$val" in
|
|
53
|
+
''|*[!0-9]*|0)
|
|
54
|
+
echo "WARN - invalid XP_GATE_CCN_THRESHOLD value '${val}' (expected a positive integer), falling back to ${default}" >&2
|
|
55
|
+
;;
|
|
56
|
+
*)
|
|
57
|
+
echo "$val"
|
|
58
|
+
return 0
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
fi
|
|
62
|
+
echo "$default"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
CCN_THRESHOLD=$(resolve_ccn_threshold)
|
|
21
66
|
|
|
22
67
|
# Check lizard availability
|
|
23
68
|
LIZARD_CMD=""
|
|
@@ -37,7 +82,16 @@ else
|
|
|
37
82
|
echo "Checking complexity for source files..."
|
|
38
83
|
|
|
39
84
|
# Run lizard with CCN threshold
|
|
40
|
-
|
|
85
|
+
# One path per line, into an array. Unquoted $CC_FILES relied on word
|
|
86
|
+
# splitting, which also splits any path containing a space -- handing
|
|
87
|
+
# lizard two bogus paths and checking neither (#457 defect class, same
|
|
88
|
+
# fix as gate-4's PRINCIPLES_ARGS). Quoting the scalar would instead
|
|
89
|
+
# collapse the list into one argument. An array does both.
|
|
90
|
+
CC_FILES_ARGS=()
|
|
91
|
+
while IFS= read -r _cc_file; do
|
|
92
|
+
[ -n "$_cc_file" ] && CC_FILES_ARGS+=("$_cc_file")
|
|
93
|
+
done <<< "$CC_FILES"
|
|
94
|
+
CC_OUTPUT=$("$LIZARD_PATH" -C "$CCN_THRESHOLD" ${CC_FILES_ARGS[@]+"${CC_FILES_ARGS[@]}"} 2>&1 || true)
|
|
41
95
|
|
|
42
96
|
# Parse warning count from the summary table: "Warning cnt 8"
|
|
43
97
|
# Use anchored grep to avoid matching lizard table headers (e.g. "Rt" column)
|
|
@@ -57,12 +111,14 @@ else
|
|
|
57
111
|
else
|
|
58
112
|
echo "⏭️ SKIPPED - Complexity (no source files to check)."
|
|
59
113
|
GATE_3_STATUS="SKIP"
|
|
114
|
+
GATE_3_REASON="no_source_files_changed"
|
|
60
115
|
fi
|
|
61
116
|
else
|
|
62
117
|
echo "⚠️ WARN - lizard not installed, complexity check not performed"
|
|
63
118
|
echo " Install with: pip install --user lizard"
|
|
64
119
|
echo " Gate 3: Complexity check (WARN, tool not available)"
|
|
65
120
|
GATE_3_STATUS="WARN"
|
|
121
|
+
GATE_3_REASON="tool_not_installed"
|
|
66
122
|
fi
|
|
67
123
|
fi
|
|
68
|
-
record_gate_audit "gate-3" "complexity" "$GATE_3_STATUS" "${CC_WARNINGS:-0}" "$GATE_3_START"
|
|
124
|
+
record_gate_audit "gate-3" "complexity" "$GATE_3_STATUS" "${CC_WARNINGS:-0}" "$GATE_3_START" "ccn_threshold=${CCN_THRESHOLD:-5}"
|
package/adapters/gate-4.sh
CHANGED
|
@@ -1,17 +1,67 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
1
2
|
# ============================================================================
|
|
2
3
|
# GATE 4: Principles Checker (Clean Code + SOLID)
|
|
3
4
|
# Reuses existing principles checker logic
|
|
4
5
|
# ============================================================================
|
|
5
6
|
|
|
7
|
+
# Run a TypeScript entrypoint under tsx without routing arguments through the
|
|
8
|
+
# npx shim: Windows cmd.exe caps command lines at 8191 chars and long staged
|
|
9
|
+
# file lists overflow it, turning a healthy run into a false BLOCK. Prefer the
|
|
10
|
+
# repo-local tsx CLI driven by node directly; keep npx as fallback for consumer
|
|
11
|
+
# repos relying on a global install. Defined here as well so this module also
|
|
12
|
+
# works when sourced outside pre-commit.
|
|
13
|
+
if ! declare -F run_tsx >/dev/null 2>&1; then
|
|
14
|
+
run_tsx() {
|
|
15
|
+
local _root="${PROJECT_ROOT:-$(pwd)}" _timeout=()
|
|
16
|
+
if [ -n "${RUN_TSX_TIMEOUT:-}" ]; then
|
|
17
|
+
_timeout=(timeout "$RUN_TSX_TIMEOUT")
|
|
18
|
+
fi
|
|
19
|
+
if [ -f "$_root/node_modules/tsx/dist/cli.mjs" ] && command -v node >/dev/null 2>&1; then
|
|
20
|
+
"${_timeout[@]}" node "$_root/node_modules/tsx/dist/cli.mjs" "$@"
|
|
21
|
+
else
|
|
22
|
+
"${_timeout[@]}" npx tsx "$@"
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Java rule coverage annotation (#507 S4, AC-507-04-02). The normative matrix
|
|
28
|
+
# lives in docs/java-principles-coverage.md; these constants mirror it. Before
|
|
29
|
+
# #507 the Java adapter fed rules only {name,type,line,code}, so the 12 rules
|
|
30
|
+
# reading structured metrics silently saw `undefined` and a Java commit got a
|
|
31
|
+
# green "checked" with zero effective coverage. 12/15 now fire; the three
|
|
32
|
+
# inert ones are rule-side gaps documented in the coverage doc -- they must
|
|
33
|
+
# NOT be announced as covered.
|
|
34
|
+
JAVA_COVERAGE_EFFECTIVE=12
|
|
35
|
+
JAVA_COVERAGE_TOTAL=15
|
|
36
|
+
JAVA_COVERAGE_INERT="lsp, many-exports, code-duplication"
|
|
37
|
+
|
|
38
|
+
# Print the coverage annotation when (and only when) the staged file list
|
|
39
|
+
# contains Java sources. Non-Java commits keep the original output byte for
|
|
40
|
+
# byte. Pure shell builtins: no subprocess per file.
|
|
41
|
+
java_coverage_note() {
|
|
42
|
+
local _f _has_java=""
|
|
43
|
+
for _f in "$@"; do
|
|
44
|
+
case "$_f" in
|
|
45
|
+
*.java|*.Java|*.JAVA) _has_java=1; break ;;
|
|
46
|
+
esac
|
|
47
|
+
done
|
|
48
|
+
[ -n "$_has_java" ] || return 0
|
|
49
|
+
echo "ℹ️ Java rule coverage: $JAVA_COVERAGE_EFFECTIVE/$JAVA_COVERAGE_TOTAL rules effective (inert on Java: $JAVA_COVERAGE_INERT -- see docs/java-principles-coverage.md)"
|
|
50
|
+
}
|
|
51
|
+
|
|
6
52
|
2>&1 echo ""
|
|
7
53
|
2>&1 echo "→ Gate 4: Principles checker (Clean Code + SOLID)..."
|
|
8
54
|
GATE_4_START=$(gate_start_ms)
|
|
9
55
|
|
|
10
56
|
GATE_4_STATUS=""
|
|
57
|
+
# #537: every non-run records WHY (no files / tool missing / unsupported lang)
|
|
58
|
+
# so the quality report can distinguish "not applicable" from "not covered".
|
|
59
|
+
GATE_4_REASON=""
|
|
11
60
|
|
|
12
61
|
if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
13
62
|
echo "⏭️ SKIPPED - Principles check (documentation project)."
|
|
14
63
|
GATE_4_STATUS="SKIP"
|
|
64
|
+
GATE_4_REASON="documentation_project"
|
|
15
65
|
|
|
16
66
|
else
|
|
17
67
|
# Get source files to check against principles
|
|
@@ -28,10 +78,13 @@ else
|
|
|
28
78
|
done <<< "$PRINCIPLES_FILES"
|
|
29
79
|
|
|
30
80
|
if [ -n "$PRINCIPLES_FILES" ]; then
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
81
|
+
# The checker resolves every path it is given -- and every path in CHANGED_FILES
|
|
82
|
+
# comes from `git diff`, so it is repo-root relative -- against its own working
|
|
83
|
+
# directory. Resolving against the hook's CWD broke in single-language subdir
|
|
84
|
+
# mode, where pre-commit has already cd'd into $PROJECT_SUBDIR: web/src/a.ts was
|
|
85
|
+
# looked up as web/web/src/a.ts, the probes below missed the checker that was
|
|
86
|
+
# installed one level up, and a working gate degraded to SKIPPED (#478).
|
|
87
|
+
# Everything this gate reads is therefore anchored at the repo root.
|
|
35
88
|
PRINCIPLES_BASE="${PROJECT_ROOT:-$(pwd)}"
|
|
36
89
|
|
|
37
90
|
PRINCIPLES_DIR=""
|
|
@@ -49,8 +102,13 @@ else
|
|
|
49
102
|
|
|
50
103
|
if [ -n "$PRINCIPLES_DIR" ]; then
|
|
51
104
|
echo "Checking Clean Code + SOLID principles..."
|
|
52
|
-
|
|
53
|
-
|
|
105
|
+
|
|
106
|
+
# AC-507-04-02: say what a Java commit is actually covered for, instead
|
|
107
|
+
# of implying a full 15-rule check (#507). Printed before the run so it
|
|
108
|
+
# appears on both the PASS and the FAIL path.
|
|
109
|
+
java_coverage_note ${PRINCIPLES_ARGS[@]+"${PRINCIPLES_ARGS[@]}"}
|
|
110
|
+
|
|
111
|
+
if command -v npx > /dev/null 2>&1 || [ -f "$PRINCIPLES_BASE/node_modules/tsx/dist/cli.mjs" ]; then
|
|
54
112
|
# Run principles checker and store results.
|
|
55
113
|
#
|
|
56
114
|
# Do NOT wrap this in `if ...; then`: the checker's exit codes are
|
|
@@ -72,6 +130,7 @@ else
|
|
|
72
130
|
# Sending it to /dev/null made an exit-2 SKIP undiagnosable and hid every
|
|
73
131
|
# .principlesrc typo (Delphi walkthrough MC-03). Captured to a file so a clean
|
|
74
132
|
# run stays quiet, then replayed only when the checker had a problem.
|
|
133
|
+
#
|
|
75
134
|
# `cd || exit 2`: a base we cannot enter means the checker never ran, which is
|
|
76
135
|
# this module's tool-failure code. Falling through would return the exit status
|
|
77
136
|
# of the failed cd (1), and 1 asserts "I examined files and found errors" -- an
|
|
@@ -86,7 +145,7 @@ else
|
|
|
86
145
|
PRINCIPLES_JSON=$(mktemp)
|
|
87
146
|
export PRINCIPLES_JSON
|
|
88
147
|
( cd "$PRINCIPLES_BASE" 2>/dev/null || exit 2
|
|
89
|
-
|
|
148
|
+
run_tsx "$PRINCIPLES_DIR/index.ts" --files ${PRINCIPLES_ARGS[@]+"${PRINCIPLES_ARGS[@]}"} --format json ) > "$PRINCIPLES_JSON" 2>"$PRINCIPLES_STDERR" || PRINCIPLES_EXIT=$?
|
|
90
149
|
|
|
91
150
|
if [ -s "$PRINCIPLES_STDERR" ]; then
|
|
92
151
|
sed 's/^/ /' "$PRINCIPLES_STDERR"
|
|
@@ -97,6 +156,7 @@ else
|
|
|
97
156
|
echo "⚠️ Warning: Principles checker execution failed"
|
|
98
157
|
echo "⏭️ SKIPPED - Principles check (execution issue)"
|
|
99
158
|
GATE_4_STATUS="SKIP"
|
|
159
|
+
GATE_4_REASON="checker_execution_error"
|
|
100
160
|
rm -f "$PRINCIPLES_JSON"
|
|
101
161
|
else
|
|
102
162
|
# Check severity levels. The reporter emits JSON.stringify(out, null, 2),
|
|
@@ -116,7 +176,7 @@ else
|
|
|
116
176
|
echo " - SOLID principle violations"
|
|
117
177
|
echo " - architectural violations"
|
|
118
178
|
( cd "$PRINCIPLES_BASE" 2>/dev/null || exit 2
|
|
119
|
-
|
|
179
|
+
run_tsx "$PRINCIPLES_DIR/index.ts" --files ${PRINCIPLES_ARGS[@]+"${PRINCIPLES_ARGS[@]}"} --format console )
|
|
120
180
|
GATE_4_STATUS="FAIL"
|
|
121
181
|
exit 1
|
|
122
182
|
fi
|
|
@@ -128,18 +188,21 @@ else
|
|
|
128
188
|
fi
|
|
129
189
|
fi
|
|
130
190
|
else
|
|
131
|
-
echo "ℹ️ npx not available - skipping principles check"
|
|
132
|
-
echo "⏭️ SKIPPED - Principles check (no Node.js/
|
|
191
|
+
echo "ℹ️ npx/tsx not available - skipping principles check"
|
|
192
|
+
echo "⏭️ SKIPPED - Principles check (no Node.js/tsx)"
|
|
133
193
|
GATE_4_STATUS="SKIP"
|
|
194
|
+
GATE_4_REASON="tool_not_installed"
|
|
134
195
|
fi
|
|
135
196
|
else
|
|
136
197
|
echo "ℹ️ Principles checker not found in project - skipping"
|
|
137
198
|
echo "⏭️ SKIPPED - Principles check (checker not in project)"
|
|
138
199
|
GATE_4_STATUS="SKIP"
|
|
200
|
+
GATE_4_REASON="checker_not_found"
|
|
139
201
|
fi
|
|
140
202
|
else
|
|
141
203
|
echo "⏭️ SKIPPED - Principles check (no matching source files changed)"
|
|
142
204
|
GATE_4_STATUS="SKIP"
|
|
205
|
+
GATE_4_REASON="no_matching_files_changed"
|
|
143
206
|
fi
|
|
144
207
|
fi
|
|
145
208
|
record_gate_audit "gate-4" "principles" "$GATE_4_STATUS" "${WARNING_COUNT:-0}" "$GATE_4_START"
|
package/adapters/gate-7.sh
CHANGED
|
@@ -10,10 +10,14 @@ GATE_7_START=$(gate_start_ms)
|
|
|
10
10
|
|
|
11
11
|
# Check if any IaC files are changed
|
|
12
12
|
IAC_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E "\.(tf|yaml|yml)$|Dockerfile" || true)
|
|
13
|
+
# #536: scanned_files makes a trivial PASS (nothing to scan) auditable in the
|
|
14
|
+
# quality report instead of reading like a covered security check.
|
|
15
|
+
GATE_7_SCANNED_FILES=0
|
|
13
16
|
if [ -z "$IAC_FILES" ]; then
|
|
14
17
|
echo "✅ PASSED - No IaC files detected in changes."
|
|
15
18
|
GATE_7_STATUS="PASS"
|
|
16
19
|
else
|
|
20
|
+
GATE_7_SCANNED_FILES=$(printf '%s\n' "$IAC_FILES" | sed -n '/./p' | wc -l | tr -d '[:space:]')
|
|
17
21
|
# Run IaC adapter. Resolution follows the documented 3-tier convention
|
|
18
22
|
# (adapter-common.sh): global ~/.config/xp-gate/adapters -> project
|
|
19
23
|
# githooks/adapters -> script dir. The old cwd-only test silently SKIPped
|
package/adapters/go.sh
CHANGED
|
File without changes
|
package/adapters/iac.sh
CHANGED
|
File without changes
|
package/adapters/java.sh
CHANGED
|
File without changes
|
package/adapters/kotlin.sh
CHANGED
|
File without changes
|
package/adapters/objectivec.sh
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/adapters/powershell.sh
CHANGED
|
@@ -31,12 +31,20 @@ _detect_pwsh() {
|
|
|
31
31
|
|
|
32
32
|
_find_powershell_files() {
|
|
33
33
|
local name_pattern="$1"
|
|
34
|
+
# #538: gitignored drill/review artifact dirs hold hundreds of non-product
|
|
35
|
+
# .ps1 files (admin drills, Delphi review copies). Collecting them turned
|
|
36
|
+
# Gate 1 into an 800+ warning wall and made the pwsh gates unusable.
|
|
34
37
|
find . \
|
|
35
38
|
\( -type d \( \
|
|
36
39
|
-name .git -o \
|
|
37
40
|
-name node_modules -o \
|
|
38
41
|
-name dist -o \
|
|
39
42
|
-name coverage -o \
|
|
43
|
+
-name .worktrees -o \
|
|
44
|
+
-name .delphi-work -o \
|
|
45
|
+
-name .workbuddy -o \
|
|
46
|
+
-name .sprint-state -o \
|
|
47
|
+
-name wrc-drill -o \
|
|
40
48
|
-path './plugins/claude-code/skills' -o \
|
|
41
49
|
-path './plugins/opencode/skills' -o \
|
|
42
50
|
-path './plugins/qoder/skills' -o \
|
package/adapters/python.sh
CHANGED
|
File without changes
|
package/adapters/shell.sh
CHANGED
|
File without changes
|
package/adapters/swift.sh
CHANGED
|
File without changes
|
package/adapters/typescript.sh
CHANGED
|
File without changes
|
package/bin/xp-gate.js
CHANGED
|
File without changes
|
package/gate-10.sh
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
GATE_10_START=$(gate_start_ms)
|
|
11
11
|
|
|
12
12
|
GATE_10_STATUS=""
|
|
13
|
+
# #536: audit trail for "did this commit actually get scanned?". The report
|
|
14
|
+
# surfaces reason + scanned_files so a non-scan never reads as a clean PASS.
|
|
15
|
+
GATE_10_REASON=""
|
|
16
|
+
GATE_10_SCANNED_FILES=0
|
|
13
17
|
|
|
14
18
|
# Semgrep availability check
|
|
15
19
|
SEMGREP_CMD=""
|
|
@@ -25,6 +29,7 @@ if [ -z "$SEMGREP_CMD" ]; then
|
|
|
25
29
|
echo " Pre-cache rules: semgrep --config=p/security-audit"
|
|
26
30
|
echo " Gate 10: SAST Security (WARN, semgrep not installed)"
|
|
27
31
|
GATE_10_STATUS="WARN"
|
|
32
|
+
GATE_10_REASON="tool_not_installed"
|
|
28
33
|
else
|
|
29
34
|
# Get staged files filtered to Semgrep-supported languages
|
|
30
35
|
SEMGREP_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E '\.(ts|tsx|js|jsx|py|go|java|c|cpp|cs|rb|php|scala|swift)$' || true)
|
|
@@ -32,6 +37,7 @@ else
|
|
|
32
37
|
if [ -z "$SEMGREP_FILES" ]; then
|
|
33
38
|
echo " ⏭️ SKIPPED - SAST (no supported language files changed)"
|
|
34
39
|
GATE_10_STATUS="SKIP"
|
|
40
|
+
GATE_10_REASON="no_supported_files"
|
|
35
41
|
else
|
|
36
42
|
# One path per line into an array. Quoting the newline-joined list would hand
|
|
37
43
|
# semgrep ONE argument containing newlines -> "Invalid scanning root" once
|
|
@@ -66,6 +72,7 @@ EOF
|
|
|
66
72
|
echo " Install node (https://nodejs.org); without it SAST findings cannot be classified"
|
|
67
73
|
echo " ⏭️ SKIPPED - SAST (node not installed)"
|
|
68
74
|
GATE_10_STATUS="SKIP"
|
|
75
|
+
GATE_10_REASON="tool_not_installed"
|
|
69
76
|
else
|
|
70
77
|
|
|
71
78
|
# Run semgrep with JSON output. stdout and stderr are captured SEPARATELY:
|
|
@@ -138,6 +145,7 @@ console.log("");});
|
|
|
138
145
|
fi
|
|
139
146
|
echo " ⏭️ SKIPPED - SAST (semgrep output unparseable)"
|
|
140
147
|
GATE_10_STATUS="SKIP"
|
|
148
|
+
GATE_10_REASON="output_unparseable"
|
|
141
149
|
elif [ "$V_RESULTS" -eq 0 ] && { [ "$V_ERRORS" -gt 0 ] || [ "$SEMGREP_EXIT" -ne 0 ]; }; then
|
|
142
150
|
# Nothing was scanned to a verdict: semgrep either failed outright
|
|
143
151
|
# (nonzero exit) or completed but reported scan errors for every input
|
|
@@ -151,6 +159,7 @@ console.log("");});
|
|
|
151
159
|
fi
|
|
152
160
|
echo " ⏭️ SKIPPED - SAST (semgrep runtime error)"
|
|
153
161
|
GATE_10_STATUS="SKIP"
|
|
162
|
+
GATE_10_REASON="semgrep_runtime_error"
|
|
154
163
|
elif [ "$V_BLOCKING" -gt 0 ]; then
|
|
155
164
|
echo ""
|
|
156
165
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
@@ -164,8 +173,10 @@ console.log("");});
|
|
|
164
173
|
echo "$FINDING_DETAILS"
|
|
165
174
|
echo " Run 'semgrep scan --config=p/security-audit' to review all findings."
|
|
166
175
|
GATE_10_STATUS="FAIL"
|
|
176
|
+
GATE_10_SCANNED_FILES=${#SEMGREP_ARGS[@]}
|
|
167
177
|
exit 1
|
|
168
178
|
else
|
|
179
|
+
GATE_10_SCANNED_FILES=${#SEMGREP_ARGS[@]}
|
|
169
180
|
echo ""
|
|
170
181
|
echo " ✅ PASSED - No blocking vulnerabilities (files scanned: ${#SEMGREP_ARGS[@]})"
|
|
171
182
|
if [ "$V_WARNINGS" -gt 0 ]; then
|
package/gate-3.sh
CHANGED
|
@@ -7,15 +7,21 @@
|
|
|
7
7
|
2>&1 echo ""
|
|
8
8
|
2>&1 echo "→ Gate 3: Cyclomatic complexity..."
|
|
9
9
|
GATE_3_START=$(gate_start_ms)
|
|
10
|
+
# #537: every non-run records WHY so the report can say why a gate is SKIP.
|
|
11
|
+
GATE_3_REASON=""
|
|
10
12
|
|
|
11
13
|
if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
12
14
|
echo "⏭️ SKIPPED - Complexity (documentation project)."
|
|
13
15
|
GATE_3_STATUS="SKIP"
|
|
16
|
+
GATE_3_REASON="documentation_project"
|
|
14
17
|
|
|
15
18
|
elif [ "$PROJECT_LANG" = "powershell" ]; then
|
|
16
19
|
echo "ℹ️ No PowerShell Clean Code / SOLID tool available"
|
|
17
20
|
echo "⏭️ SKIPPED - Complexity (no PowerShell tool)"
|
|
18
21
|
GATE_3_STATUS="SKIP"
|
|
22
|
+
# #537: a PowerShell-primary repo was invisible to this dimension while the
|
|
23
|
+
# score implied full coverage — say exactly why nothing ran.
|
|
24
|
+
GATE_3_REASON="no_powershell_tool"
|
|
19
25
|
|
|
20
26
|
else
|
|
21
27
|
# S2 (#507): three-level CCN threshold override --
|
|
@@ -105,12 +111,14 @@ else
|
|
|
105
111
|
else
|
|
106
112
|
echo "⏭️ SKIPPED - Complexity (no source files to check)."
|
|
107
113
|
GATE_3_STATUS="SKIP"
|
|
114
|
+
GATE_3_REASON="no_source_files_changed"
|
|
108
115
|
fi
|
|
109
116
|
else
|
|
110
117
|
echo "⚠️ WARN - lizard not installed, complexity check not performed"
|
|
111
118
|
echo " Install with: pip install --user lizard"
|
|
112
119
|
echo " Gate 3: Complexity check (WARN, tool not available)"
|
|
113
120
|
GATE_3_STATUS="WARN"
|
|
121
|
+
GATE_3_REASON="tool_not_installed"
|
|
114
122
|
fi
|
|
115
123
|
fi
|
|
116
124
|
record_gate_audit "gate-3" "complexity" "$GATE_3_STATUS" "${CC_WARNINGS:-0}" "$GATE_3_START" "ccn_threshold=${CCN_THRESHOLD:-5}"
|
package/gate-4.sh
CHANGED
|
@@ -54,10 +54,14 @@ java_coverage_note() {
|
|
|
54
54
|
GATE_4_START=$(gate_start_ms)
|
|
55
55
|
|
|
56
56
|
GATE_4_STATUS=""
|
|
57
|
+
# #537: every non-run records WHY (no files / tool missing / unsupported lang)
|
|
58
|
+
# so the quality report can distinguish "not applicable" from "not covered".
|
|
59
|
+
GATE_4_REASON=""
|
|
57
60
|
|
|
58
61
|
if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
59
62
|
echo "⏭️ SKIPPED - Principles check (documentation project)."
|
|
60
63
|
GATE_4_STATUS="SKIP"
|
|
64
|
+
GATE_4_REASON="documentation_project"
|
|
61
65
|
|
|
62
66
|
else
|
|
63
67
|
# Get source files to check against principles
|
|
@@ -152,6 +156,7 @@ else
|
|
|
152
156
|
echo "⚠️ Warning: Principles checker execution failed"
|
|
153
157
|
echo "⏭️ SKIPPED - Principles check (execution issue)"
|
|
154
158
|
GATE_4_STATUS="SKIP"
|
|
159
|
+
GATE_4_REASON="checker_execution_error"
|
|
155
160
|
rm -f "$PRINCIPLES_JSON"
|
|
156
161
|
else
|
|
157
162
|
# Check severity levels. The reporter emits JSON.stringify(out, null, 2),
|
|
@@ -186,15 +191,18 @@ else
|
|
|
186
191
|
echo "ℹ️ npx/tsx not available - skipping principles check"
|
|
187
192
|
echo "⏭️ SKIPPED - Principles check (no Node.js/tsx)"
|
|
188
193
|
GATE_4_STATUS="SKIP"
|
|
194
|
+
GATE_4_REASON="tool_not_installed"
|
|
189
195
|
fi
|
|
190
196
|
else
|
|
191
197
|
echo "ℹ️ Principles checker not found in project - skipping"
|
|
192
198
|
echo "⏭️ SKIPPED - Principles check (checker not in project)"
|
|
193
199
|
GATE_4_STATUS="SKIP"
|
|
200
|
+
GATE_4_REASON="checker_not_found"
|
|
194
201
|
fi
|
|
195
202
|
else
|
|
196
203
|
echo "⏭️ SKIPPED - Principles check (no matching source files changed)"
|
|
197
204
|
GATE_4_STATUS="SKIP"
|
|
205
|
+
GATE_4_REASON="no_matching_files_changed"
|
|
198
206
|
fi
|
|
199
207
|
fi
|
|
200
208
|
record_gate_audit "gate-4" "principles" "$GATE_4_STATUS" "${WARNING_COUNT:-0}" "$GATE_4_START"
|
package/gate-7.sh
CHANGED
|
@@ -10,10 +10,14 @@ GATE_7_START=$(gate_start_ms)
|
|
|
10
10
|
|
|
11
11
|
# Check if any IaC files are changed
|
|
12
12
|
IAC_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E "\.(tf|yaml|yml)$|Dockerfile" || true)
|
|
13
|
+
# #536: scanned_files makes a trivial PASS (nothing to scan) auditable in the
|
|
14
|
+
# quality report instead of reading like a covered security check.
|
|
15
|
+
GATE_7_SCANNED_FILES=0
|
|
13
16
|
if [ -z "$IAC_FILES" ]; then
|
|
14
17
|
echo "✅ PASSED - No IaC files detected in changes."
|
|
15
18
|
GATE_7_STATUS="PASS"
|
|
16
19
|
else
|
|
20
|
+
GATE_7_SCANNED_FILES=$(printf '%s\n' "$IAC_FILES" | sed -n '/./p' | wc -l | tr -d '[:space:]')
|
|
17
21
|
# Run IaC adapter. Resolution follows the documented 3-tier convention
|
|
18
22
|
# (adapter-common.sh): global ~/.config/xp-gate/adapters -> project
|
|
19
23
|
# githooks/adapters -> script dir. The old cwd-only test silently SKIPped
|
package/gate-8.sh
CHANGED
|
File without changes
|
package/gate-9.sh
CHANGED
|
File without changes
|
package/hooks/adapter-common.sh
CHANGED
|
File without changes
|
package/hooks/gate-10.sh
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
GATE_10_START=$(gate_start_ms)
|
|
11
11
|
|
|
12
12
|
GATE_10_STATUS=""
|
|
13
|
+
# #536: audit trail for "did this commit actually get scanned?". The report
|
|
14
|
+
# surfaces reason + scanned_files so a non-scan never reads as a clean PASS.
|
|
15
|
+
GATE_10_REASON=""
|
|
16
|
+
GATE_10_SCANNED_FILES=0
|
|
13
17
|
|
|
14
18
|
# Semgrep availability check
|
|
15
19
|
SEMGREP_CMD=""
|
|
@@ -25,6 +29,7 @@ if [ -z "$SEMGREP_CMD" ]; then
|
|
|
25
29
|
echo " Pre-cache rules: semgrep --config=p/security-audit"
|
|
26
30
|
echo " Gate 10: SAST Security (WARN, semgrep not installed)"
|
|
27
31
|
GATE_10_STATUS="WARN"
|
|
32
|
+
GATE_10_REASON="tool_not_installed"
|
|
28
33
|
else
|
|
29
34
|
# Get staged files filtered to Semgrep-supported languages
|
|
30
35
|
SEMGREP_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E '\.(ts|tsx|js|jsx|py|go|java|c|cpp|cs|rb|php|scala|swift)$' || true)
|
|
@@ -32,6 +37,7 @@ else
|
|
|
32
37
|
if [ -z "$SEMGREP_FILES" ]; then
|
|
33
38
|
echo " ⏭️ SKIPPED - SAST (no supported language files changed)"
|
|
34
39
|
GATE_10_STATUS="SKIP"
|
|
40
|
+
GATE_10_REASON="no_supported_files"
|
|
35
41
|
else
|
|
36
42
|
# One path per line into an array. Quoting the newline-joined list would hand
|
|
37
43
|
# semgrep ONE argument containing newlines -> "Invalid scanning root" once
|
|
@@ -66,6 +72,7 @@ EOF
|
|
|
66
72
|
echo " Install node (https://nodejs.org); without it SAST findings cannot be classified"
|
|
67
73
|
echo " ⏭️ SKIPPED - SAST (node not installed)"
|
|
68
74
|
GATE_10_STATUS="SKIP"
|
|
75
|
+
GATE_10_REASON="tool_not_installed"
|
|
69
76
|
else
|
|
70
77
|
|
|
71
78
|
# Run semgrep with JSON output. stdout and stderr are captured SEPARATELY:
|
|
@@ -138,6 +145,7 @@ console.log("");});
|
|
|
138
145
|
fi
|
|
139
146
|
echo " ⏭️ SKIPPED - SAST (semgrep output unparseable)"
|
|
140
147
|
GATE_10_STATUS="SKIP"
|
|
148
|
+
GATE_10_REASON="output_unparseable"
|
|
141
149
|
elif [ "$V_RESULTS" -eq 0 ] && { [ "$V_ERRORS" -gt 0 ] || [ "$SEMGREP_EXIT" -ne 0 ]; }; then
|
|
142
150
|
# Nothing was scanned to a verdict: semgrep either failed outright
|
|
143
151
|
# (nonzero exit) or completed but reported scan errors for every input
|
|
@@ -151,6 +159,7 @@ console.log("");});
|
|
|
151
159
|
fi
|
|
152
160
|
echo " ⏭️ SKIPPED - SAST (semgrep runtime error)"
|
|
153
161
|
GATE_10_STATUS="SKIP"
|
|
162
|
+
GATE_10_REASON="semgrep_runtime_error"
|
|
154
163
|
elif [ "$V_BLOCKING" -gt 0 ]; then
|
|
155
164
|
echo ""
|
|
156
165
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
@@ -164,8 +173,10 @@ console.log("");});
|
|
|
164
173
|
echo "$FINDING_DETAILS"
|
|
165
174
|
echo " Run 'semgrep scan --config=p/security-audit' to review all findings."
|
|
166
175
|
GATE_10_STATUS="FAIL"
|
|
176
|
+
GATE_10_SCANNED_FILES=${#SEMGREP_ARGS[@]}
|
|
167
177
|
exit 1
|
|
168
178
|
else
|
|
179
|
+
GATE_10_SCANNED_FILES=${#SEMGREP_ARGS[@]}
|
|
169
180
|
echo ""
|
|
170
181
|
echo " ✅ PASSED - No blocking vulnerabilities (files scanned: ${#SEMGREP_ARGS[@]})"
|
|
171
182
|
if [ "$V_WARNINGS" -gt 0 ]; then
|
package/hooks/gate-3.sh
CHANGED
|
@@ -7,15 +7,21 @@
|
|
|
7
7
|
2>&1 echo ""
|
|
8
8
|
2>&1 echo "→ Gate 3: Cyclomatic complexity..."
|
|
9
9
|
GATE_3_START=$(gate_start_ms)
|
|
10
|
+
# #537: every non-run records WHY so the report can say why a gate is SKIP.
|
|
11
|
+
GATE_3_REASON=""
|
|
10
12
|
|
|
11
13
|
if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
12
14
|
echo "⏭️ SKIPPED - Complexity (documentation project)."
|
|
13
15
|
GATE_3_STATUS="SKIP"
|
|
16
|
+
GATE_3_REASON="documentation_project"
|
|
14
17
|
|
|
15
18
|
elif [ "$PROJECT_LANG" = "powershell" ]; then
|
|
16
19
|
echo "ℹ️ No PowerShell Clean Code / SOLID tool available"
|
|
17
20
|
echo "⏭️ SKIPPED - Complexity (no PowerShell tool)"
|
|
18
21
|
GATE_3_STATUS="SKIP"
|
|
22
|
+
# #537: a PowerShell-primary repo was invisible to this dimension while the
|
|
23
|
+
# score implied full coverage — say exactly why nothing ran.
|
|
24
|
+
GATE_3_REASON="no_powershell_tool"
|
|
19
25
|
|
|
20
26
|
else
|
|
21
27
|
# S2 (#507): three-level CCN threshold override --
|
|
@@ -105,12 +111,14 @@ else
|
|
|
105
111
|
else
|
|
106
112
|
echo "⏭️ SKIPPED - Complexity (no source files to check)."
|
|
107
113
|
GATE_3_STATUS="SKIP"
|
|
114
|
+
GATE_3_REASON="no_source_files_changed"
|
|
108
115
|
fi
|
|
109
116
|
else
|
|
110
117
|
echo "⚠️ WARN - lizard not installed, complexity check not performed"
|
|
111
118
|
echo " Install with: pip install --user lizard"
|
|
112
119
|
echo " Gate 3: Complexity check (WARN, tool not available)"
|
|
113
120
|
GATE_3_STATUS="WARN"
|
|
121
|
+
GATE_3_REASON="tool_not_installed"
|
|
114
122
|
fi
|
|
115
123
|
fi
|
|
116
124
|
record_gate_audit "gate-3" "complexity" "$GATE_3_STATUS" "${CC_WARNINGS:-0}" "$GATE_3_START" "ccn_threshold=${CCN_THRESHOLD:-5}"
|
package/hooks/gate-4.sh
CHANGED
|
@@ -54,10 +54,14 @@ java_coverage_note() {
|
|
|
54
54
|
GATE_4_START=$(gate_start_ms)
|
|
55
55
|
|
|
56
56
|
GATE_4_STATUS=""
|
|
57
|
+
# #537: every non-run records WHY (no files / tool missing / unsupported lang)
|
|
58
|
+
# so the quality report can distinguish "not applicable" from "not covered".
|
|
59
|
+
GATE_4_REASON=""
|
|
57
60
|
|
|
58
61
|
if [ "$PROJECT_LANG" = "documentation-only" ]; then
|
|
59
62
|
echo "⏭️ SKIPPED - Principles check (documentation project)."
|
|
60
63
|
GATE_4_STATUS="SKIP"
|
|
64
|
+
GATE_4_REASON="documentation_project"
|
|
61
65
|
|
|
62
66
|
else
|
|
63
67
|
# Get source files to check against principles
|
|
@@ -152,6 +156,7 @@ else
|
|
|
152
156
|
echo "⚠️ Warning: Principles checker execution failed"
|
|
153
157
|
echo "⏭️ SKIPPED - Principles check (execution issue)"
|
|
154
158
|
GATE_4_STATUS="SKIP"
|
|
159
|
+
GATE_4_REASON="checker_execution_error"
|
|
155
160
|
rm -f "$PRINCIPLES_JSON"
|
|
156
161
|
else
|
|
157
162
|
# Check severity levels. The reporter emits JSON.stringify(out, null, 2),
|
|
@@ -186,15 +191,18 @@ else
|
|
|
186
191
|
echo "ℹ️ npx/tsx not available - skipping principles check"
|
|
187
192
|
echo "⏭️ SKIPPED - Principles check (no Node.js/tsx)"
|
|
188
193
|
GATE_4_STATUS="SKIP"
|
|
194
|
+
GATE_4_REASON="tool_not_installed"
|
|
189
195
|
fi
|
|
190
196
|
else
|
|
191
197
|
echo "ℹ️ Principles checker not found in project - skipping"
|
|
192
198
|
echo "⏭️ SKIPPED - Principles check (checker not in project)"
|
|
193
199
|
GATE_4_STATUS="SKIP"
|
|
200
|
+
GATE_4_REASON="checker_not_found"
|
|
194
201
|
fi
|
|
195
202
|
else
|
|
196
203
|
echo "⏭️ SKIPPED - Principles check (no matching source files changed)"
|
|
197
204
|
GATE_4_STATUS="SKIP"
|
|
205
|
+
GATE_4_REASON="no_matching_files_changed"
|
|
198
206
|
fi
|
|
199
207
|
fi
|
|
200
208
|
record_gate_audit "gate-4" "principles" "$GATE_4_STATUS" "${WARNING_COUNT:-0}" "$GATE_4_START"
|