@boyingliu01/xp-gate 0.20.1 → 0.20.2
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/java.sh +373 -27
- package/gate-3.sh +51 -3
- package/gate-4.sh +32 -1
- package/hooks/gate-3.sh +51 -3
- package/hooks/gate-4.sh +32 -1
- package/hooks/pre-commit +275 -36
- package/lib/__tests__/doctor-spawn-capability.test.js +116 -0
- package/lib/__tests__/doctor-tui-skill-freshness.test.js +49 -10
- package/lib/doctor-tui.js +21 -3
- package/lib/doctor.js +111 -8
- package/lib/gate-audit.ts +3 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/admin-template-guidelines/SKILL.md +1 -0
- package/plugins/claude-code/skills/batch-grill-me/SKILL.md +1 -0
- package/plugins/claude-code/skills/clipboard-vision/SKILL.md +1 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/claude-code/skills/domain-modeling/SKILL.md +1 -0
- package/plugins/claude-code/skills/grill-with-docs/SKILL.md +1 -0
- package/plugins/claude-code/skills/grilling/SKILL.md +1 -0
- package/plugins/claude-code/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/plugins/claude-code/skills/ralph-loop/SKILL.md +1 -0
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-driven-development/SKILL.md +1 -0
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/SKILL.md +1 -0
- package/plugins/claude-code/skills/to-issues/SKILL.md +1 -0
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +1 -0
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +1 -0
- package/plugins/dsh/skills/clipboard-vision/SKILL.md +1 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/dsh/skills/domain-modeling/SKILL.md +1 -0
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +1 -0
- package/plugins/dsh/skills/grilling/SKILL.md +1 -0
- package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/plugins/dsh/skills/ralph-loop/SKILL.md +1 -0
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-driven-development/SKILL.md +1 -0
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-specification-alignment/SKILL.md +1 -0
- package/plugins/dsh/skills/to-issues/SKILL.md +1 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/admin-template-guidelines/SKILL.md +1 -0
- package/plugins/opencode/skills/batch-grill-me/SKILL.md +1 -0
- package/plugins/opencode/skills/clipboard-vision/SKILL.md +1 -0
- package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/opencode/skills/domain-modeling/SKILL.md +1 -0
- package/plugins/opencode/skills/grill-with-docs/SKILL.md +1 -0
- package/plugins/opencode/skills/grilling/SKILL.md +1 -0
- package/plugins/opencode/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/plugins/opencode/skills/ralph-loop/SKILL.md +1 -0
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-driven-development/SKILL.md +1 -0
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/SKILL.md +1 -0
- package/plugins/opencode/skills/to-issues/SKILL.md +1 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/principles/adapters/__tests__/java.test.ts +317 -0
- package/principles/adapters/java.ts +325 -13
- package/skills/admin-template-guidelines/SKILL.md +1 -0
- package/skills/batch-grill-me/SKILL.md +1 -0
- package/skills/clipboard-vision/SKILL.md +1 -0
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/domain-modeling/SKILL.md +1 -0
- package/skills/grill-with-docs/SKILL.md +1 -0
- package/skills/grilling/SKILL.md +1 -0
- package/skills/improve-codebase-architecture/SKILL.md +1 -0
- package/skills/ralph-loop/SKILL.md +1 -0
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/test-driven-development/SKILL.md +1 -0
- package/skills/test-specification-alignment/AGENTS.md +2 -2
- package/skills/test-specification-alignment/SKILL.md +1 -0
- package/skills/to-issues/SKILL.md +1 -0
package/adapters/java.sh
CHANGED
|
@@ -1,10 +1,280 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# Java adapter -- GATE 1 static analysis (#507 S1, DR-001/DR-002)
|
|
4
|
+
#
|
|
5
|
+
# Primary engine: wc-java-lint (`check --staged --format json`), fail-closed:
|
|
6
|
+
# exit 0 -> PASS (clean)
|
|
7
|
+
# exit 1 -> FAIL (violations; never downgraded in soft mode)
|
|
8
|
+
# exit >=2 -> BLOCK (execution error / timeout / unusable JSON)
|
|
9
|
+
# JSON without the required whitelist fields (file/rule/severity) counts as
|
|
10
|
+
# unusable -> BLOCK; unknown extra fields are tolerated (additive evolution).
|
|
11
|
+
# Escape valves:
|
|
12
|
+
# XP_GATE_WC_JAVA_LINT=soft downgrades BLOCK-class outcomes (exit>=2,
|
|
13
|
+
# timeout, unusable JSON) to non-blocking WARN (exit 3); exit-1 stays FAIL.
|
|
14
|
+
# XP_GATE_WC_JAVA_LINT=report downgrades exit-1 violations to non-blocking
|
|
15
|
+
# WARN (rollout/grace mode for legacy repos; #507 Delphi round-1 C-MAJOR-3).
|
|
16
|
+
# An exit-0-with-objects response is a lying tool and is NEVER downgraded.
|
|
17
|
+
# A SKIP/WARN reason crosses the subshell boundary as an `XP_G1_REASON: ...`
|
|
18
|
+
# stdout line (export cannot: the caller captures command-substitution output).
|
|
19
|
+
# Fallback: tool not on PATH -> legacy checkstyle/pmd + p3c/whalecloud chain;
|
|
20
|
+
# checkstyle AND pmd both missing -> explicit WARN and verdict must NOT be
|
|
21
|
+
# PASS (exit 3, recorded in the gate-1 audit detail).
|
|
22
|
+
#
|
|
23
|
+
# Verdict contract with pre-commit's `java)` branch:
|
|
24
|
+
# 0 = PASS, 1 = FAIL(violations -> commit blocked), 2 = BLOCK(fail-closed),
|
|
25
|
+
# 3 = WARN(non-blocking). pre-commit scores a WARN IN the denominator so it
|
|
26
|
+
# forbids an overall PASS -- Delphi round-2 A-MAJOR-1: mapping these paths
|
|
27
|
+
# to SKIP instead let unverified Java (no engine installed, or findings
|
|
28
|
+
# suppressed in report mode) ride out of the denominator to a fake 10/10.
|
|
29
|
+
# ============================================================================
|
|
2
30
|
|
|
3
31
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
32
|
# Plugin directory
|
|
5
33
|
PLUGIN_DIR="$SCRIPT_DIR/plugins/p3c-java"
|
|
6
34
|
WHALECLOUD_PLUGIN_DIR="$SCRIPT_DIR/plugins/whalecloud-java"
|
|
7
35
|
|
|
36
|
+
WC_JAVA_LINT_DEFAULT_TIMEOUT=180
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Discovery (#507 AC-507-01-05): explicit multi-form support. MSYS2/Git Bash
|
|
40
|
+
# `command -v` resolves .exe/.cmd only when the extension is spelled out, so
|
|
41
|
+
# each form is probed verbatim.
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
wc_java_lint_bin() {
|
|
44
|
+
local candidate
|
|
45
|
+
for candidate in wc-java-lint wc-java-lint.exe wc-java-lint.cmd; do
|
|
46
|
+
if command -v "$candidate" >/dev/null 2>&1; then
|
|
47
|
+
command -v "$candidate"
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
done
|
|
51
|
+
return 1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Timeout (#507 AC-507-01-09): .xp-gate/wc-java-lint-timeout (positive
|
|
56
|
+
# integer seconds) > default 180. Invalid values WARN and fall back -- an
|
|
57
|
+
# unreadable override must not silently disable the timeout.
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
resolve_wc_java_lint_timeout() {
|
|
60
|
+
local file=".xp-gate/wc-java-lint-timeout" val=""
|
|
61
|
+
if [ -f "$file" ]; then
|
|
62
|
+
val=$(tr -d '[:space:]' < "$file")
|
|
63
|
+
fi
|
|
64
|
+
if [ -z "$val" ]; then
|
|
65
|
+
echo "$WC_JAVA_LINT_DEFAULT_TIMEOUT"
|
|
66
|
+
return 0
|
|
67
|
+
fi
|
|
68
|
+
case "$val" in
|
|
69
|
+
''|*[!0-9]*|0)
|
|
70
|
+
echo "WARN - invalid $file value '${val}' (expected a positive integer), falling back to ${WC_JAVA_LINT_DEFAULT_TIMEOUT}s" >&2
|
|
71
|
+
echo "$WC_JAVA_LINT_DEFAULT_TIMEOUT"
|
|
72
|
+
;;
|
|
73
|
+
*) echo "$val" ;;
|
|
74
|
+
esac
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# Staged .java file set (#507 AC-507-01-10): the ONLY files handed to the
|
|
79
|
+
# tool in explicit-list mode. Polyglot commits keep their TS/Python files out
|
|
80
|
+
# of the Java engine entirely.
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
java_staged_files() {
|
|
83
|
+
# Harness/test injection: XP_GATE_JAVA_STAGED_FILES supplies a raw file
|
|
84
|
+
# list (newline-separated) so tests can exercise the .java filter without
|
|
85
|
+
# a git repo. The filter still applies -- the tool never sees non-.java.
|
|
86
|
+
if [ -n "${XP_GATE_JAVA_STAGED_FILES:-}" ]; then
|
|
87
|
+
printf '%s\n' "$XP_GATE_JAVA_STAGED_FILES" | grep -E '\.java$' || true
|
|
88
|
+
return 0
|
|
89
|
+
fi
|
|
90
|
+
git diff --cached --name-only --diff-filter=d 2>/dev/null \
|
|
91
|
+
| grep -E '\.java$' || true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
# JSON whitelist validation (#507 AC-507-01-07, DR-002).
|
|
96
|
+
# Contract shape: a JSON array of FLAT violation objects, each carrying
|
|
97
|
+
# file/rule/severity (line optional). Checks are grep-based by design:
|
|
98
|
+
# - STRING LITERALS ARE STRIPPED BEFORE COUNTING (#507 Delphi round-1
|
|
99
|
+
# A-MAJOR-1): violation messages routinely contain braces ("'{' is not
|
|
100
|
+
# preceded...") or the words file/rule/severity -- counting raw text made
|
|
101
|
+
# perfectly valid output false-BLOCK. After stripping, only structural
|
|
102
|
+
# braces and structural keys remain.
|
|
103
|
+
# - unknown extra fields add occurrences of their own keys, never remove
|
|
104
|
+
# the required ones -> tolerated (additive evolution);
|
|
105
|
+
# - any object missing a required key desynchronizes the per-key occurrence
|
|
106
|
+
# counts from the object count -> unusable -> BLOCK;
|
|
107
|
+
# - a truncated response (no closing bracket) is unusable -> BLOCK.
|
|
108
|
+
# Prints the violation (object) count on success; returns 1 if unusable.
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
wc_java_json_violation_count() {
|
|
111
|
+
local json_file="$1"
|
|
112
|
+
[ -s "$json_file" ] || return 1
|
|
113
|
+
|
|
114
|
+
# Array-shaped: opens with [ and closes with ]. (A bare [] is valid clean
|
|
115
|
+
# output; an object wrapper is outside the pinned contract.)
|
|
116
|
+
[ "$(head -c 1 "$json_file")" = "[" ] || return 1
|
|
117
|
+
local tail
|
|
118
|
+
tail=$(tr -d '[:space:]' < "$json_file" | tail -c 1)
|
|
119
|
+
[ "$tail" = "]" ] || return 1
|
|
120
|
+
|
|
121
|
+
# Strip string VALUE literals (`: "..."` -> `: ""`). Keys must survive
|
|
122
|
+
# (they are the tokens being counted); values must vanish (violation
|
|
123
|
+
# messages routinely contain braces or the words file/rule/severity --
|
|
124
|
+
# #507 Delphi round-1 A-MAJOR-1). JSON strings cannot contain raw newlines
|
|
125
|
+
# (they are \n escapes), so line-wise sed is safe. The leftmost-match rule
|
|
126
|
+
# consumes an entire value string in one go, including any `: "` sequences
|
|
127
|
+
# inside it. Broken JSON masks badly -> counts desynchronize -> BLOCK,
|
|
128
|
+
# which is the correct fail-closed direction.
|
|
129
|
+
# POSIX ERE via -E: BSD sed (macOS is a supported CI platform) does not
|
|
130
|
+
# support the GNU BRE `\|` alternation extension -- under BSD sed the GNU
|
|
131
|
+
# variant either fails to strip values (count desync -> BLOCK, annoying but
|
|
132
|
+
# safe) or, worst case, errors to empty counts and disarms the lying-tool
|
|
133
|
+
# BLOCK below (fail-open). Delphi round-2 A-MAJOR-2.
|
|
134
|
+
local structural obj_count file_count rule_count sev_count
|
|
135
|
+
structural=$(sed -E 's/:([[:space:]]*)"([^"\\]|\\.)*"/:\1""/g' "$json_file" 2>/dev/null)
|
|
136
|
+
obj_count=$(grep -o '{' <<< "$structural" | wc -l | tr -d '[:space:]')
|
|
137
|
+
file_count=$(grep -o '"file"' <<< "$structural" | wc -l | tr -d '[:space:]')
|
|
138
|
+
rule_count=$(grep -o '"rule"' <<< "$structural" | wc -l | tr -d '[:space:]')
|
|
139
|
+
sev_count=$(grep -o '"severity"' <<< "$structural" | wc -l | tr -d '[:space:]')
|
|
140
|
+
[ "$obj_count" -eq "$file_count" ] || return 1
|
|
141
|
+
[ "$obj_count" -eq "$rule_count" ] || return 1
|
|
142
|
+
[ "$obj_count" -eq "$sev_count" ] || return 1
|
|
143
|
+
|
|
144
|
+
echo "$obj_count"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Distinct staged-file names from a violations JSON (reporting only).
|
|
148
|
+
wc_java_json_files() {
|
|
149
|
+
grep -o '"file"[[:space:]]*:[[:space:]]*"[^"]*"' "$1" 2>/dev/null \
|
|
150
|
+
| sed 's/.*"\([^"]*\)"$/\1/' | sort -u | head -10
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
# Primary engine (#507 AC-507-01-01/02/06/09).
|
|
155
|
+
# Prints a human-readable report on stdout; returns the verdict code.
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
run_wc_java_lint() {
|
|
158
|
+
local bin="" timeout_s out_file err_file exit_code count
|
|
159
|
+
|
|
160
|
+
if ! bin=$(wc_java_lint_bin); then
|
|
161
|
+
return 9 # not installed -> caller falls back to the legacy chain
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
timeout_s=$(resolve_wc_java_lint_timeout)
|
|
165
|
+
out_file=$(mktemp "${TMPDIR:-/tmp}/wc-java-lint.XXXXXX")
|
|
166
|
+
err_file="${out_file}.err"
|
|
167
|
+
|
|
168
|
+
# Primary contract call: the tool inspects the index itself.
|
|
169
|
+
# `|| exit_code=$?` keeps a non-zero verdict from killing the host when
|
|
170
|
+
# this module is sourced into a `set -e` consumer (repo convention,
|
|
171
|
+
# cf. gate-4.sh's `|| PRINCIPLES_EXIT=$?`).
|
|
172
|
+
exit_code=0
|
|
173
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
174
|
+
timeout "$timeout_s" "$bin" check --staged --format json >"$out_file" 2>"$err_file" || exit_code=$?
|
|
175
|
+
else
|
|
176
|
+
echo " ⚠️ 'timeout' command not available - running wc-java-lint without a timeout guard" >&2
|
|
177
|
+
"$bin" check --staged --format json >"$out_file" 2>"$err_file" || exit_code=$?
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Usage-error fallback (#507 AC-507-01-06): an older build without --staged
|
|
181
|
+
# answers with a usage message and exit>=2. Retry with the explicit staged
|
|
182
|
+
# .java list; any OTHER exit>=2 stays an execution error.
|
|
183
|
+
if [ "$exit_code" -ge 2 ] \
|
|
184
|
+
&& grep -qiE 'unknown option|unrecognized option|^usage' "$err_file" "$out_file" 2>/dev/null; then
|
|
185
|
+
local staged_args=()
|
|
186
|
+
while IFS= read -r jf; do
|
|
187
|
+
[ -n "$jf" ] && staged_args+=("$jf")
|
|
188
|
+
done <<< "$(java_staged_files)"
|
|
189
|
+
if [ "${#staged_args[@]}" -gt 0 ]; then
|
|
190
|
+
exit_code=0
|
|
191
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
192
|
+
timeout "$timeout_s" "$bin" check --format json "${staged_args[@]}" >"$out_file" 2>"$err_file" || exit_code=$?
|
|
193
|
+
else
|
|
194
|
+
"$bin" check --format json "${staged_args[@]}" >"$out_file" 2>"$err_file" || exit_code=$?
|
|
195
|
+
fi
|
|
196
|
+
fi
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
if [ -s "$err_file" ]; then
|
|
200
|
+
sed 's/^/ /' "$err_file"
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
case "$exit_code" in
|
|
204
|
+
0)
|
|
205
|
+
if ! count=$(wc_java_json_violation_count "$out_file"); then
|
|
206
|
+
echo "❌ wc-java-lint output is not a usable violations JSON (fail-closed, #507 DR-001)"
|
|
207
|
+
rm -f "$out_file" "$err_file"
|
|
208
|
+
if [ "${XP_GATE_WC_JAVA_LINT:-}" = "soft" ]; then
|
|
209
|
+
# Unusable output IS an execution-class failure (the tool exists but
|
|
210
|
+
# its response cannot be trusted) -- same class as exit>=2, covered
|
|
211
|
+
# by the escape valve (#507 Delphi round-1 B-MAJOR-5).
|
|
212
|
+
echo "⚠️ XP_GATE_WC_JAVA_LINT=soft: unusable JSON downgraded to WARN (non-blocking, in scoring denominator)"
|
|
213
|
+
echo "XP_G1_REASON: wc-java-lint unusable JSON (soft downgrade)"
|
|
214
|
+
return 3
|
|
215
|
+
fi
|
|
216
|
+
return 2
|
|
217
|
+
fi
|
|
218
|
+
if [ "$count" -gt 0 ]; then
|
|
219
|
+
# Contract says exit 0 means clean; objects + 0 is a LYING tool, not a
|
|
220
|
+
# broken one. It claims success while reporting violations -- there is
|
|
221
|
+
# no trustworthy interpretation, so soft/report must NOT downgrade it
|
|
222
|
+
# (deliberately outside the escape valve; #507 Delphi round-1 B-MAJOR-5).
|
|
223
|
+
echo "❌ wc-java-lint exited 0 but reported $count violation object(s) - inconsistent response"
|
|
224
|
+
rm -f "$out_file" "$err_file"
|
|
225
|
+
return 2
|
|
226
|
+
fi
|
|
227
|
+
rm -f "$out_file" "$err_file"
|
|
228
|
+
echo "✅ wc-java-lint: clean"
|
|
229
|
+
return 0
|
|
230
|
+
;;
|
|
231
|
+
1)
|
|
232
|
+
if count=$(wc_java_json_violation_count "$out_file"); then
|
|
233
|
+
echo "❌ wc-java-lint: $count violation(s) in:"
|
|
234
|
+
wc_java_json_files "$out_file" | sed 's/^/ /'
|
|
235
|
+
else
|
|
236
|
+
echo "❌ wc-java-lint reported violations but the JSON was unusable"
|
|
237
|
+
fi
|
|
238
|
+
rm -f "$out_file" "$err_file"
|
|
239
|
+
if [ "${XP_GATE_WC_JAVA_LINT:-}" = "report" ]; then
|
|
240
|
+
# Rollout/grace mode (#507 Delphi round-1 C-MAJOR-3): a legacy repo
|
|
241
|
+
# adopting wc-java-lint carries pre-existing violations; hard-blocking
|
|
242
|
+
# every commit gives no migration path. report mode degrades REAL
|
|
243
|
+
# violations to non-blocking WARN -- pre-commit scores WARN in the
|
|
244
|
+
# denominator and an overall WARN verdict forbids a PASS (Delphi
|
|
245
|
+
# round-2 A-MAJOR-1: SKIP would have produced a fake 10/0 for a
|
|
246
|
+
# repo whose only Java check is suppressed), so teams burn down
|
|
247
|
+
# findings before switching back to enforcing.
|
|
248
|
+
echo "⚠️ XP_GATE_WC_JAVA_LINT=report: violations downgraded to WARN (rollout mode)"
|
|
249
|
+
echo "⚠️ Java code quality NOT enforcing - burn down the findings, then unset report mode"
|
|
250
|
+
echo "XP_G1_REASON: wc-java-lint report mode: ${count:-unknown} violation(s) suppressed"
|
|
251
|
+
return 3
|
|
252
|
+
fi
|
|
253
|
+
return 1
|
|
254
|
+
;;
|
|
255
|
+
*)
|
|
256
|
+
rm -f "$out_file" "$err_file"
|
|
257
|
+
if [ "$exit_code" -eq 124 ]; then
|
|
258
|
+
echo "❌ wc-java-lint timed out after ${timeout_s}s"
|
|
259
|
+
else
|
|
260
|
+
echo "❌ wc-java-lint failed with exit code $exit_code"
|
|
261
|
+
fi
|
|
262
|
+
if [ "${XP_GATE_WC_JAVA_LINT:-}" = "soft" ]; then
|
|
263
|
+
echo "⚠️ XP_GATE_WC_JAVA_LINT=soft: execution failure downgraded to WARN (non-blocking, #507 DR-001 escape valve)"
|
|
264
|
+
echo "⚠️ Java static analysis NOT performed - the engine exists but is broken; do not treat this as a clean run"
|
|
265
|
+
# The reason crosses the command-substitution subshell boundary via a
|
|
266
|
+
# prefixed stdout line (export does NOT: the caller captures this
|
|
267
|
+
# function's output, so the subshell env is discarded -- #507 Delphi
|
|
268
|
+
# round-1 B-MAJOR-1). pre-commit parses and strips the line.
|
|
269
|
+
echo "XP_G1_REASON: wc-java-lint broken (soft downgrade)"
|
|
270
|
+
return 3
|
|
271
|
+
fi
|
|
272
|
+
return 2
|
|
273
|
+
;;
|
|
274
|
+
esac
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
8
278
|
_is_whalecloud_enabled() {
|
|
9
279
|
# Check if whalecloud-java plugin is enabled
|
|
10
280
|
[ -d "$WHALECLOUD_PLUGIN_DIR" ] && \
|
|
@@ -22,40 +292,74 @@ _detect_java_build() {
|
|
|
22
292
|
fi
|
|
23
293
|
}
|
|
24
294
|
|
|
25
|
-
|
|
26
|
-
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
# Legacy fallback (#507 AC-507-01-03): only reached when wc-java-lint is not
|
|
297
|
+
# installed. Prints one WARN per missing plugin dir; if neither checkstyle
|
|
298
|
+
# nor pmd is available the verdict must NOT be PASS (exit 3, audit warn).
|
|
299
|
+
# ---------------------------------------------------------------------------
|
|
300
|
+
run_legacy_analysis() {
|
|
301
|
+
local build_system lint_tools=0 worst=0 rc=0
|
|
302
|
+
|
|
27
303
|
build_system=$(_detect_java_build)
|
|
28
304
|
|
|
29
|
-
if [
|
|
30
|
-
|
|
31
|
-
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
|
32
|
-
echo "❌ Maven compilation failed"
|
|
33
|
-
return 1
|
|
34
|
-
fi
|
|
35
|
-
elif [ "$build_system" = "gradle" ]; then
|
|
36
|
-
gradle compileJava --quiet 2>&1 | grep -i "error\|fail" | sed -n '1,5p; 5q'
|
|
37
|
-
if [ "${PIPESTATUS[0]}" -ne 0 ]; then
|
|
38
|
-
echo "❌ Gradle compilation failed"
|
|
39
|
-
return 1
|
|
40
|
-
fi
|
|
305
|
+
if [ ! -d "$PLUGIN_DIR" ]; then
|
|
306
|
+
echo "⚠️ p3c-java plugin directory missing: $PLUGIN_DIR"
|
|
41
307
|
fi
|
|
308
|
+
if [ ! -d "$WHALECLOUD_PLUGIN_DIR" ]; then
|
|
309
|
+
echo "⚠️ whalecloud-java plugin directory missing: $WHALECLOUD_PLUGIN_DIR"
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
# Every engine's verdict is AGGREGATED (#507 blind review MAJOR-1): a tool
|
|
313
|
+
# that reports violations must never degrade to a silent PASS -- that was
|
|
314
|
+
# the exact anti-pattern this sprint exists to remove.
|
|
315
|
+
local out_file
|
|
316
|
+
out_file=$(mktemp "${TMPDIR:-/tmp}/java-legacy.XXXXXX")
|
|
42
317
|
|
|
43
318
|
# CheckStyle with Google style (legacy fallback)
|
|
44
319
|
if command -v checkstyle &>/dev/null; then
|
|
45
|
-
|
|
320
|
+
lint_tools=$((lint_tools + 1))
|
|
321
|
+
rc=0
|
|
322
|
+
checkstyle -c /google_checks.xml . >"$out_file" 2>&1 || rc=$?
|
|
323
|
+
sed -n '1,20p' "$out_file"
|
|
324
|
+
[ "$rc" -ne 0 ] && worst=1
|
|
46
325
|
fi
|
|
47
326
|
|
|
48
327
|
# PMD error detection (legacy fallback)
|
|
49
328
|
if command -v pmd &>/dev/null; then
|
|
50
|
-
|
|
329
|
+
lint_tools=$((lint_tools + 1))
|
|
330
|
+
rc=0
|
|
331
|
+
pmd check -d . -R category/java/errorprone.xml >"$out_file" 2>&1 || rc=$?
|
|
332
|
+
sed -n '1,20p' "$out_file"
|
|
333
|
+
[ "$rc" -ne 0 ] && worst=1
|
|
334
|
+
fi
|
|
335
|
+
rm -f "$out_file"
|
|
336
|
+
|
|
337
|
+
if [ "$lint_tools" -eq 0 ]; then
|
|
338
|
+
echo "⚠️ No Java static-analysis engine available: wc-java-lint, checkstyle and pmd all missing"
|
|
339
|
+
echo "⚠️ Java code quality NOT verified - verdict is WARN, not PASS (#507 AC-507-01-03)"
|
|
340
|
+
echo " Install wc-java-lint (preferred) or checkstyle/pmd to restore verification"
|
|
341
|
+
_XP_JAVA_LEGACY_WARN=1
|
|
342
|
+
echo "XP_G1_REASON: legacy tools unavailable (warn)"
|
|
51
343
|
fi
|
|
52
344
|
|
|
53
345
|
# p3c-pmd check (Alibaba coding guidelines) — primary Java quality gate
|
|
54
|
-
|
|
346
|
+
# (`-Dpmd.failOnViolation=true` makes violations exit non-zero).
|
|
347
|
+
rc=0
|
|
348
|
+
_run_p3c_check "$build_system" || rc=$?
|
|
349
|
+
[ "$rc" -ne 0 ] && worst=1
|
|
55
350
|
|
|
56
351
|
# WhaleCloud Java Coding Standards — overlay on top of p3c-pmd
|
|
57
|
-
|
|
352
|
+
rc=0
|
|
353
|
+
_run_whalecloud_check "$build_system" || rc=$?
|
|
354
|
+
[ "$rc" -ne 0 ] && worst=1
|
|
58
355
|
|
|
356
|
+
if [ "$worst" -ne 0 ]; then
|
|
357
|
+
echo " ❌ legacy Java analysis found violations"
|
|
358
|
+
return 1
|
|
359
|
+
fi
|
|
360
|
+
if [ "${_XP_JAVA_LEGACY_WARN:-0}" = "1" ]; then
|
|
361
|
+
return 3
|
|
362
|
+
fi
|
|
59
363
|
return 0
|
|
60
364
|
}
|
|
61
365
|
|
|
@@ -128,6 +432,21 @@ run_p3c_check() {
|
|
|
128
432
|
_run_p3c_check "$(_detect_java_build)"
|
|
129
433
|
}
|
|
130
434
|
|
|
435
|
+
# Legacy maven/gradle invocations are network-facing (plugin/rule resolution
|
|
436
|
+
# against remote repositories). Without a guard a cold cache can hang
|
|
437
|
+
# pre-commit for minutes (#507 Delphi round-1 C-MAJOR-2; round-2 extended the
|
|
438
|
+
# same guard to the Gradle legacy lint calls). Same timeout file as the
|
|
439
|
+
# primary engine: .xp-gate/wc-java-lint-timeout. Degrades safely when
|
|
440
|
+
# `timeout` is unavailable (rc 127 must never be mistaken for a tool verdict).
|
|
441
|
+
_legacy_build_run() {
|
|
442
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
443
|
+
timeout "$(resolve_wc_java_lint_timeout)" "$@"
|
|
444
|
+
else
|
|
445
|
+
echo " ⚠️ 'timeout' command not available - legacy build-tool call runs unguarded" >&2
|
|
446
|
+
"$@"
|
|
447
|
+
fi
|
|
448
|
+
}
|
|
449
|
+
|
|
131
450
|
_run_p3c_check() {
|
|
132
451
|
local build_system="$1"
|
|
133
452
|
|
|
@@ -135,13 +454,20 @@ _run_p3c_check() {
|
|
|
135
454
|
|
|
136
455
|
if [ "$build_system" = "maven" ]; then
|
|
137
456
|
if grep -q '<id>xp-gate-p3c</id>' pom.xml 2>/dev/null; then
|
|
138
|
-
# Profile already installed — use it
|
|
139
|
-
|
|
457
|
+
# Profile already installed — use it (timeout-guarded: legacy maven
|
|
458
|
+
# resolves plugins/rulesets over the network, a cold cache must not
|
|
459
|
+
# hang pre-commit unbounded -- #507 Delphi round-1 C-MAJOR-2)
|
|
460
|
+
_legacy_build_run mvn pmd:check -P xp-gate-p3c -Dpmd.failOnViolation=true 2>&1 | tail -30
|
|
140
461
|
return "${PIPESTATUS[0]}"
|
|
141
462
|
else
|
|
142
|
-
# Profile not installed
|
|
143
|
-
|
|
144
|
-
|
|
463
|
+
# Profile not installed. The inline ruleset paths (/rulesets/java/ali-*)
|
|
464
|
+
# resolve INSIDE the p3c-pmd jar — without that dependency in the pom,
|
|
465
|
+
# maven cannot resolve them and exits non-zero, which BLOCKed the commit
|
|
466
|
+
# on every real project without p3c configured (#507 Delphi round-1
|
|
467
|
+
# C-MAJOR-1). "Not configured" is an opt-out, not a violation: only run
|
|
468
|
+
# inline when the pom actually declares p3c-pmd.
|
|
469
|
+
if grep -q 'p3c-pmd' pom.xml 2>/dev/null; then
|
|
470
|
+
_legacy_build_run mvn pmd:check \
|
|
145
471
|
-Dpmd.rulesets="/rulesets/java/ali-comment.xml,/rulesets/java/ali-concurrent.xml,/rulesets/java/ali-constant.xml,/rulesets/java/ali-exception.xml,/rulesets/java/ali-flowcontrol.xml,/rulesets/java/ali-naming.xml,/rulesets/java/ali-oop.xml,/rulesets/java/ali-orm.xml,/rulesets/java/ali-other.xml,/rulesets/java/ali-set.xml" \
|
|
146
472
|
-Dpmd.failOnViolation=true \
|
|
147
473
|
-DprintFailingErrors=true \
|
|
@@ -158,7 +484,8 @@ _run_p3c_check() {
|
|
|
158
484
|
echo " ✅ p3c-pmd check passed"
|
|
159
485
|
return 0
|
|
160
486
|
else
|
|
161
|
-
echo " ℹ️
|
|
487
|
+
echo " ℹ️ p3c-pmd not configured in pom.xml — skipping (opt-in, not a violation)"
|
|
488
|
+
echo " To enable: bash $PLUGIN_DIR/scripts/install-maven-p3c.sh"
|
|
162
489
|
return 0
|
|
163
490
|
fi
|
|
164
491
|
fi
|
|
@@ -166,7 +493,7 @@ _run_p3c_check() {
|
|
|
166
493
|
elif [ "$build_system" = "gradle" ]; then
|
|
167
494
|
if grep -q 'xp-gateP3cCheck\|p3c-pmd' build.gradle 2>/dev/null || \
|
|
168
495
|
grep -q 'xp-gateP3cCheck\|p3c-pmd' build.gradle.kts 2>/dev/null; then
|
|
169
|
-
gradle xp-gateP3cCheck --quiet 2>&1 | tail -20
|
|
496
|
+
_legacy_build_run gradle xp-gateP3cCheck --quiet 2>&1 | tail -20
|
|
170
497
|
return "${PIPESTATUS[0]}"
|
|
171
498
|
else
|
|
172
499
|
echo " ℹ️ p3c-pmd not configured in Gradle build"
|
|
@@ -190,7 +517,8 @@ _run_whalecloud_check() {
|
|
|
190
517
|
|
|
191
518
|
if [ "$build_system" = "maven" ]; then
|
|
192
519
|
if grep -q '<id>xp-gate-whalecloud-java</id>' pom.xml 2>/dev/null; then
|
|
193
|
-
|
|
520
|
+
# timeout-guarded, same rationale as the p3c branch (C-MAJOR-2)
|
|
521
|
+
_legacy_build_run mvn pmd:check checkstyle:check spotbugs:check \
|
|
194
522
|
-P xp-gate-whalecloud-java -Dpmd.failOnViolation=true \
|
|
195
523
|
2>&1 | tail -30
|
|
196
524
|
return "${PIPESTATUS[0]}"
|
|
@@ -203,7 +531,7 @@ _run_whalecloud_check() {
|
|
|
203
531
|
elif [ "$build_system" = "gradle" ]; then
|
|
204
532
|
if grep -q 'xp-gateWhalecloudCheck' build.gradle 2>/dev/null || \
|
|
205
533
|
grep -q 'xp-gateWhalecloudCheck' build.gradle.kts 2>/dev/null; then
|
|
206
|
-
gradle xp-gateWhalecloudCheck --quiet 2>&1 | tail -20
|
|
534
|
+
_legacy_build_run gradle xp-gateWhalecloudCheck --quiet 2>&1 | tail -20
|
|
207
535
|
return "${PIPESTATUS[0]}"
|
|
208
536
|
else
|
|
209
537
|
echo " ⚠️ whalecloud-java not configured in Gradle build"
|
|
@@ -218,3 +546,21 @@ _run_whalecloud_check() {
|
|
|
218
546
|
run_whalecloud_check() {
|
|
219
547
|
_run_whalecloud_check "$(_detect_java_build)"
|
|
220
548
|
}
|
|
549
|
+
|
|
550
|
+
# ---------------------------------------------------------------------------
|
|
551
|
+
# GATE 1 entry (#507 S1). wc-java-lint first; legacy chain only on discovery
|
|
552
|
+
# miss. The old direct mvn/gradle compile here moved to Build Integrity (S5).
|
|
553
|
+
# ---------------------------------------------------------------------------
|
|
554
|
+
run_static_analysis() {
|
|
555
|
+
local rc=0
|
|
556
|
+
# `|| rc=$?` (not a bare call): under a set -e consumer a non-zero verdict
|
|
557
|
+
# would otherwise abort the host hook instead of returning the verdict code.
|
|
558
|
+
run_wc_java_lint || rc=$?
|
|
559
|
+
if [ "$rc" -eq 9 ]; then
|
|
560
|
+
echo "ℹ️ wc-java-lint not found - falling back to legacy Java analysis"
|
|
561
|
+
rc=0
|
|
562
|
+
run_legacy_analysis || rc=$?
|
|
563
|
+
return "$rc"
|
|
564
|
+
fi
|
|
565
|
+
return "$rc"
|
|
566
|
+
}
|
package/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
|
|
@@ -17,7 +18,45 @@ elif [ "$PROJECT_LANG" = "powershell" ]; then
|
|
|
17
18
|
GATE_3_STATUS="SKIP"
|
|
18
19
|
|
|
19
20
|
else
|
|
20
|
-
|
|
21
|
+
# S2 (#507): three-level CCN threshold override --
|
|
22
|
+
# .xp-gate/ccn-threshold (positive integer) > XP_GATE_CCN_THRESHOLD > 5.
|
|
23
|
+
# Invalid values WARN on stderr and fall back to the default; the resolved
|
|
24
|
+
# value reaches the gate-3 audit record via the --detail argument below.
|
|
25
|
+
# Implemented with bash builtins only (no tr/grep spawns): every process
|
|
26
|
+
# spawn is expensive on Windows under AV filter drivers.
|
|
27
|
+
resolve_ccn_threshold() {
|
|
28
|
+
local default=5 val
|
|
29
|
+
if [ -f ".xp-gate/ccn-threshold" ]; then
|
|
30
|
+
val=$(<".xp-gate/ccn-threshold")
|
|
31
|
+
val="${val//[[:space:]]/}"
|
|
32
|
+
case "$val" in
|
|
33
|
+
''|*[!0-9]*|0)
|
|
34
|
+
echo "WARN - invalid .xp-gate/ccn-threshold value '${val}' (expected a positive integer), falling back to ${default}" >&2
|
|
35
|
+
;;
|
|
36
|
+
*)
|
|
37
|
+
echo "$val"
|
|
38
|
+
return 0
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
echo "$default"
|
|
42
|
+
return 0
|
|
43
|
+
fi
|
|
44
|
+
val=${XP_GATE_CCN_THRESHOLD:-}
|
|
45
|
+
if [ -n "$val" ]; then
|
|
46
|
+
case "$val" in
|
|
47
|
+
''|*[!0-9]*|0)
|
|
48
|
+
echo "WARN - invalid XP_GATE_CCN_THRESHOLD value '${val}' (expected a positive integer), falling back to ${default}" >&2
|
|
49
|
+
;;
|
|
50
|
+
*)
|
|
51
|
+
echo "$val"
|
|
52
|
+
return 0
|
|
53
|
+
;;
|
|
54
|
+
esac
|
|
55
|
+
fi
|
|
56
|
+
echo "$default"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
CCN_THRESHOLD=$(resolve_ccn_threshold)
|
|
21
60
|
|
|
22
61
|
# Check lizard availability
|
|
23
62
|
LIZARD_CMD=""
|
|
@@ -37,7 +76,16 @@ else
|
|
|
37
76
|
echo "Checking complexity for source files..."
|
|
38
77
|
|
|
39
78
|
# Run lizard with CCN threshold
|
|
40
|
-
|
|
79
|
+
# One path per line, into an array. Unquoted $CC_FILES relied on word
|
|
80
|
+
# splitting, which also splits any path containing a space -- handing
|
|
81
|
+
# lizard two bogus paths and checking neither (#457 defect class, same
|
|
82
|
+
# fix as gate-4's PRINCIPLES_ARGS). Quoting the scalar would instead
|
|
83
|
+
# collapse the list into one argument. An array does both.
|
|
84
|
+
CC_FILES_ARGS=()
|
|
85
|
+
while IFS= read -r _cc_file; do
|
|
86
|
+
[ -n "$_cc_file" ] && CC_FILES_ARGS+=("$_cc_file")
|
|
87
|
+
done <<< "$CC_FILES"
|
|
88
|
+
CC_OUTPUT=$("$LIZARD_PATH" -C "$CCN_THRESHOLD" ${CC_FILES_ARGS[@]+"${CC_FILES_ARGS[@]}"} 2>&1 || true)
|
|
41
89
|
|
|
42
90
|
# Parse warning count from the summary table: "Warning cnt 8"
|
|
43
91
|
# Use anchored grep to avoid matching lizard table headers (e.g. "Rt" column)
|
|
@@ -65,4 +113,4 @@ else
|
|
|
65
113
|
GATE_3_STATUS="WARN"
|
|
66
114
|
fi
|
|
67
115
|
fi
|
|
68
|
-
record_gate_audit "gate-3" "complexity" "$GATE_3_STATUS" "${CC_WARNINGS:-0}" "$GATE_3_START"
|
|
116
|
+
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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# shellcheck shell=bash
|
|
1
2
|
# ============================================================================
|
|
2
3
|
# GATE 4: Principles Checker (Clean Code + SOLID)
|
|
3
4
|
# Reuses existing principles checker logic
|
|
@@ -23,6 +24,31 @@ if ! declare -F run_tsx >/dev/null 2>&1; then
|
|
|
23
24
|
}
|
|
24
25
|
fi
|
|
25
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
|
+
|
|
26
52
|
2>&1 echo ""
|
|
27
53
|
2>&1 echo "→ Gate 4: Principles checker (Clean Code + SOLID)..."
|
|
28
54
|
GATE_4_START=$(gate_start_ms)
|
|
@@ -72,7 +98,12 @@ else
|
|
|
72
98
|
|
|
73
99
|
if [ -n "$PRINCIPLES_DIR" ]; then
|
|
74
100
|
echo "Checking Clean Code + SOLID principles..."
|
|
75
|
-
|
|
101
|
+
|
|
102
|
+
# AC-507-04-02: say what a Java commit is actually covered for, instead
|
|
103
|
+
# of implying a full 15-rule check (#507). Printed before the run so it
|
|
104
|
+
# appears on both the PASS and the FAIL path.
|
|
105
|
+
java_coverage_note ${PRINCIPLES_ARGS[@]+"${PRINCIPLES_ARGS[@]}"}
|
|
106
|
+
|
|
76
107
|
if command -v npx > /dev/null 2>&1 || [ -f "$PRINCIPLES_BASE/node_modules/tsx/dist/cli.mjs" ]; then
|
|
77
108
|
# Run principles checker and store results.
|
|
78
109
|
#
|