@boyingliu01/xp-gate 0.31.2 → 0.31.4
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/gate-3.sh +244 -9
- package/hooks/gate-3.sh +244 -9
- package/hooks/lib/Measure-PowerShellComplexity.ps1 +186 -0
- package/hooks/pre-commit +152 -13
- 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/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/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/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/principles/__tests__/baseline-entry-total-warnings.test.ts +76 -0
- package/principles/adapters/__tests__/cpp-code-block.test.ts +123 -0
- package/principles/adapters/__tests__/cpp-scanner-equivalence.test.ts +67 -0
- package/principles/adapters/__tests__/typescript-block-comment-lines.test.ts +53 -0
- package/principles/adapters/__tests__/typescript-masking.test.ts +96 -0
- package/principles/adapters/cpp.ts +102 -50
- package/principles/adapters/typescript.ts +503 -174
- package/principles/baseline.ts +58 -70
- 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
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Measure cyclomatic complexity of PowerShell functions via the AST.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
lizard cannot parse PowerShell, so a PowerShell-primary repository had a
|
|
7
|
+
permanently unmeasured complexity dimension (#537). The platform, however,
|
|
8
|
+
CAN answer the question: PowerShell's own AST yields an exact decision-point
|
|
9
|
+
count per function, which is the same quantity lizard measures for other
|
|
10
|
+
languages.
|
|
11
|
+
|
|
12
|
+
This script is the measurement half; gate-3.sh is the verdict half. Keeping
|
|
13
|
+
them apart means the gate does not have to know a project's refactor history
|
|
14
|
+
and the script does not have to know about baselines.
|
|
15
|
+
|
|
16
|
+
Output is line-oriented and stable, because gate-3.sh parses it:
|
|
17
|
+
|
|
18
|
+
functions_measured=<n> total functions inspected
|
|
19
|
+
max_ccn=<n> highest CCN seen
|
|
20
|
+
over_threshold=<n> functions with CCN > -Threshold
|
|
21
|
+
OVER <file>:<line> <name> ccn=<n> one per over-threshold function
|
|
22
|
+
FUNC <file>:<line> <name> ccn=<n> one per function when -List is set
|
|
23
|
+
|
|
24
|
+
.PARAMETER Path
|
|
25
|
+
Files or directories to analyse. Directories are walked for *.ps1/*.psm1.
|
|
26
|
+
|
|
27
|
+
.PARAMETER Threshold
|
|
28
|
+
Reporting threshold. Functions above it are emitted as OVER lines. This does
|
|
29
|
+
NOT filter FUNC lines, which is what makes a ratchet able to compare against
|
|
30
|
+
a baseline collected at a different threshold -- see #598.
|
|
31
|
+
|
|
32
|
+
.PARAMETER List
|
|
33
|
+
Emit a FUNC line for every function, not just the ones over the threshold.
|
|
34
|
+
A ratchet needs the full population: comparing only OVER lines against a
|
|
35
|
+
baseline that also stores only over-threshold entries is how a brand-new
|
|
36
|
+
well-factored function (CCN 2-4) got reported as a "regression".
|
|
37
|
+
|
|
38
|
+
.PARAMETER Strict
|
|
39
|
+
Treat a parse failure as a hard error (exit 3) instead of skipping the file.
|
|
40
|
+
|
|
41
|
+
.EXAMPLE
|
|
42
|
+
./Measure-PowerShellComplexity.ps1 -Path ./src -Threshold 5
|
|
43
|
+
|
|
44
|
+
.EXAMPLE
|
|
45
|
+
./Measure-PowerShellComplexity.ps1 -Path ./src -Threshold 5 -List
|
|
46
|
+
|
|
47
|
+
.NOTES
|
|
48
|
+
Exit codes:
|
|
49
|
+
0 measured (findings are reported, not signalled by exit code)
|
|
50
|
+
2 no analyzable files
|
|
51
|
+
3 parse/tooling failure (fail closed)
|
|
52
|
+
#>
|
|
53
|
+
[CmdletBinding()]
|
|
54
|
+
param(
|
|
55
|
+
[Parameter(Mandatory = $false)]
|
|
56
|
+
[string[]] $Path = @('.'),
|
|
57
|
+
|
|
58
|
+
[Parameter(Mandatory = $false)]
|
|
59
|
+
[ValidateRange(1, 1000)]
|
|
60
|
+
[int] $Threshold = 5,
|
|
61
|
+
|
|
62
|
+
[Parameter(Mandatory = $false)]
|
|
63
|
+
[switch] $List,
|
|
64
|
+
|
|
65
|
+
[Parameter(Mandatory = $false)]
|
|
66
|
+
[switch] $Strict
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
Set-StrictMode -Version Latest
|
|
70
|
+
$ErrorActionPreference = 'Stop'
|
|
71
|
+
|
|
72
|
+
# --- Collect files -----------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
$files = New-Object System.Collections.Generic.List[string]
|
|
75
|
+
$missing = 0
|
|
76
|
+
|
|
77
|
+
foreach ($p in $Path) {
|
|
78
|
+
if (-not (Test-Path -LiteralPath $p)) { $missing++; continue }
|
|
79
|
+
$item = Get-Item -LiteralPath $p
|
|
80
|
+
if ($item.PSIsContainer) {
|
|
81
|
+
Get-ChildItem -LiteralPath $item.FullName -Recurse -File -Include '*.ps1', '*.psm1' |
|
|
82
|
+
ForEach-Object { $files.Add($_.FullName) }
|
|
83
|
+
}
|
|
84
|
+
elseif ($item.Extension -in @('.ps1', '.psm1')) {
|
|
85
|
+
$files.Add($item.FullName)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if ($files.Count -eq 0) {
|
|
90
|
+
if ($Strict -and $missing -gt 0) {
|
|
91
|
+
[Console]::Error.WriteLine("ERR no analyzable PowerShell files ($missing path(s) did not exist)")
|
|
92
|
+
exit 3
|
|
93
|
+
}
|
|
94
|
+
Write-Output 'functions_measured=0'
|
|
95
|
+
Write-Output 'max_ccn=0'
|
|
96
|
+
Write-Output 'over_threshold=0'
|
|
97
|
+
exit 2
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# --- Count decision points ---------------------------------------------------
|
|
101
|
+
#
|
|
102
|
+
# CCN = 1 + number of decision points, which is the definition lizard uses.
|
|
103
|
+
# Decision points in PowerShell: if / elseif, the loop keywords, catch, the
|
|
104
|
+
# ternary and null-coalescing operators, and each -and/-or in a condition.
|
|
105
|
+
# Declaring a function is not itself a decision, so every function starts at 1.
|
|
106
|
+
|
|
107
|
+
function Get-DecisionCount {
|
|
108
|
+
param([System.Management.Automation.Language.Ast] $AstBody)
|
|
109
|
+
|
|
110
|
+
$count = 0
|
|
111
|
+
|
|
112
|
+
$count += @($AstBody.FindAll({ param($n) $n -is [System.Management.Automation.Language.IfStatementAst] }, $true)).Count
|
|
113
|
+
$count += @($AstBody.FindAll({ param($n) $n -is [System.Management.Automation.Language.LoopStatementAst] }, $true)).Count
|
|
114
|
+
$count += @($AstBody.FindAll({ param($n) $n -is [System.Management.Automation.Language.TrapStatementAst] }, $true)).Count
|
|
115
|
+
$count += @($AstBody.FindAll({ param($n) $n -is [System.Management.Automation.Language.TernaryExpressionAst] }, $true)).Count
|
|
116
|
+
|
|
117
|
+
# Binary expressions whose operator short-circuits count as decisions, which
|
|
118
|
+
# is what makes `if ($a -and $b -or $c)` score higher than `if ($a)`.
|
|
119
|
+
foreach ($bin in $AstBody.FindAll({ param($n) $n -is [System.Management.Automation.Language.BinaryExpressionAst] }, $true)) {
|
|
120
|
+
$op = $null
|
|
121
|
+
try { $op = $bin.Operator } catch { continue }
|
|
122
|
+
if ($op -eq 'And' -or $op -eq 'Or') { $count++ }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# catch blocks: FindAll over class-based ASTs is not available for them, so
|
|
126
|
+
# walk TryStatementAst nodes and count their CatchClauses.
|
|
127
|
+
foreach ($try in $AstBody.FindAll({ param($n) $n -is [System.Management.Automation.Language.TryStatementAst] }, $true)) {
|
|
128
|
+
$count += @($try.CatchClauses).Count
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return $count
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
$measured = 0
|
|
135
|
+
$maxCcn = 0
|
|
136
|
+
$over = 0
|
|
137
|
+
$parseErrors = New-Object System.Collections.Generic.List[string]
|
|
138
|
+
|
|
139
|
+
foreach ($file in $files) {
|
|
140
|
+
$tokens = $null
|
|
141
|
+
$errors = $null
|
|
142
|
+
$ast = $null
|
|
143
|
+
try {
|
|
144
|
+
$ast = [System.Management.Automation.Language.Parser]::ParseFile($file, [ref]$tokens, [ref]$errors)
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
$parseErrors.Add("$file : $($_.Exception.Message)")
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if ($errors -and $errors.Count -gt 0) {
|
|
152
|
+
$first = ($errors | Select-Object -First 1)
|
|
153
|
+
$parseErrors.Add("$file : $($first.Message)")
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
$funcs = $ast.FindAll({ param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true)
|
|
158
|
+
foreach ($f in $funcs) {
|
|
159
|
+
$ccn = 1 + (Get-DecisionCount -AstBody $f.Body)
|
|
160
|
+
$measured++
|
|
161
|
+
if ($ccn -gt $maxCcn) { $maxCcn = $ccn }
|
|
162
|
+
|
|
163
|
+
if ($ccn -gt $Threshold) { $over++ }
|
|
164
|
+
|
|
165
|
+
if ($List -or $ccn -gt $Threshold) {
|
|
166
|
+
$line = $f.Extent.StartLineNumber
|
|
167
|
+
$kind = if ($ccn -gt $Threshold) { 'OVER' } elseif ($List) { 'FUNC' } else { $null }
|
|
168
|
+
if ($kind) {
|
|
169
|
+
Write-Output ('{0} {1}:{2} {3} ccn={4}' -f $kind, $file, $line, $f.Name, $ccn)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if ($parseErrors.Count -gt 0 -and $Strict) {
|
|
176
|
+
foreach ($pe in $parseErrors) { [Console]::Error.WriteLine("ERR parse: $pe") }
|
|
177
|
+
exit 3
|
|
178
|
+
}
|
|
179
|
+
elseif ($parseErrors.Count -gt 0) {
|
|
180
|
+
foreach ($pe in $parseErrors) { [Console]::Error.WriteLine("WARN parse skipped: $pe") }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Write-Output "functions_measured=$measured"
|
|
184
|
+
Write-Output "max_ccn=$maxCcn"
|
|
185
|
+
Write-Output "over_threshold=$over"
|
|
186
|
+
exit 0
|
package/hooks/pre-commit
CHANGED
|
@@ -2130,6 +2130,13 @@ source "$GATE_DIR/gate-4.sh"
|
|
|
2130
2130
|
2>&1 echo ""
|
|
2131
2131
|
2>&1 echo "→ Gate 5: Tests & coverage..."
|
|
2132
2132
|
GATE_5_START=$(gate_start_ms)
|
|
2133
|
+
# #554 follow-up: explicit "nothing decided yet" sentinel. A branch inside the
|
|
2134
|
+
# else-block may record its own verdict (the no-code early skip records SKIP);
|
|
2135
|
+
# the epilogue must not overwrite it with PASS. Using "" rather than leaving the
|
|
2136
|
+
# variable unset keeps the epilogue's decision readable, and keeps every render
|
|
2137
|
+
# site's own default (`${GATE_5_STATUS:-SKIP}`) honest for the paths that never
|
|
2138
|
+
# assign at all.
|
|
2139
|
+
GATE_5_STATUS=""
|
|
2133
2140
|
|
|
2134
2141
|
if [ "$PROJECT_LANG" = "documentation-only" ] || [ "$DOCS_ONLY_COMMIT" = true ]; then
|
|
2135
2142
|
echo "⏭️ SKIPPED - Tests & coverage (documentation-only commit — no code changed to test)."
|
|
@@ -2491,10 +2498,19 @@ else
|
|
|
2491
2498
|
CODE_EXTENSIONS="\.(ts|tsx|js|jsx|py|go|java|kt|kts|cpp|cc|cxx|c|swift|m|mm|dart|sh|ps1)$"
|
|
2492
2499
|
CHANGED_CODE_FILES=$(echo "$CHANGED_FILES" | grep -E "$CODE_EXTENSIONS" || true)
|
|
2493
2500
|
if [ -z "$CHANGED_CODE_FILES" ]; then
|
|
2494
|
-
|
|
2501
|
+
# #554: this branch DID stand down deliberately, but "PASS" is a claim that
|
|
2502
|
+
# a check ran and found nothing. Nothing ran here: no test executed, no
|
|
2503
|
+
# coverage number exists. Recording PASS inflated the pass rate and made an
|
|
2504
|
+
# unmeasured gate indistinguishable from a measured clean one -- the same
|
|
2505
|
+
# defect #554 fixed on the docs-only branch, on the branch that fix did not
|
|
2506
|
+
# cover (measured on the v0.31.2.0 release commit: journal said
|
|
2507
|
+
# gate5_tests.status=PASS while the summary printed "coverage: N/A%").
|
|
2508
|
+
echo "⏭️ SKIPPED - No code files changed (only docs/config/VERSION) — test run not needed."
|
|
2495
2509
|
echo " Changed files: $(echo "$CHANGED_FILES" | tr '\n' ' ')"
|
|
2496
2510
|
TESTS_EXIT_CODE=0
|
|
2497
2511
|
TESTS_SKIPPED=true
|
|
2512
|
+
GATE_5_STATUS="SKIP"
|
|
2513
|
+
GATE_5_DETAIL="no_code_files_changed"
|
|
2498
2514
|
GATE_5_END=$(gate_start_ms)
|
|
2499
2515
|
GATE_5_ELAPSED=$((GATE_5_END - GATE_5_START))
|
|
2500
2516
|
echo " ⏱️ Gate 5: ${GATE_5_ELAPSED}ms (skipped — no code changes)"
|
|
@@ -2992,19 +3008,53 @@ else
|
|
|
2992
3008
|
# JaCoCo writes target/site/jacoco/ (Maven) or build/reports/jacoco/test/
|
|
2993
3009
|
# (Gradle), i.e. neither path probed by the default branch below, so Java
|
|
2994
3010
|
# needs its own branch to be measurable at all.
|
|
3011
|
+
#
|
|
3012
|
+
# #603: a MULTI-MODULE Maven repo writes one report per module --
|
|
3013
|
+
# whaleaudit-core/target/site/jacoco/jacoco.csv, whaleaudit-demo/... --
|
|
3014
|
+
# and nothing at the repo root. Probing only the root paths therefore
|
|
3015
|
+
# concluded "No JaCoCo report found, so the 80% floor cannot be verified"
|
|
3016
|
+
# and BLOCKED every Java commit of a project that had a complete report
|
|
3017
|
+
# sitting one directory down. A gate that cannot see the report is not
|
|
3018
|
+
# enforcing a floor, it is enforcing an accident of build layout.
|
|
3019
|
+
# Fix: probe the root paths first (single-module projects, and the
|
|
3020
|
+
# historical behaviour), then fall back to the module reports and
|
|
3021
|
+
# aggregate across them, so the floor covers every class that was built.
|
|
2995
3022
|
jacoco_csv=""
|
|
3023
|
+
jacoco_all=""
|
|
2996
3024
|
for jacoco_candidate in \
|
|
2997
3025
|
"target/site/jacoco/jacoco.csv" \
|
|
2998
3026
|
"build/reports/jacoco/test/jacocoTestReport.csv"; do
|
|
2999
3027
|
[ -f "$jacoco_candidate" ] && { jacoco_csv="$jacoco_candidate"; break; }
|
|
3000
3028
|
done
|
|
3029
|
+
if [ -z "$jacoco_csv" ]; then
|
|
3030
|
+
# Module reports. -mindepth-free walk so ./module/... is found; sorted
|
|
3031
|
+
# for a stable, reproducible aggregate order.
|
|
3032
|
+
jacoco_all=$(find . -path ./target -prune -o \
|
|
3033
|
+
-type f \( -name 'jacoco.csv' -path '*/target/site/jacoco/*' \) -print 2>/dev/null \
|
|
3034
|
+
| sort | tr '\n' ' ')
|
|
3035
|
+
# First module report doubles as the "report exists" witness for the
|
|
3036
|
+
# messages below; the aggregate sums over every one of them.
|
|
3037
|
+
jacoco_csv=$(echo "$jacoco_all" | awk '{print $1}')
|
|
3038
|
+
fi
|
|
3039
|
+
if [ -n "$jacoco_csv" ] && [ -n "$jacoco_all" ]; then
|
|
3040
|
+
_jc_n=$(echo "$jacoco_all" | awk '{print NF}')
|
|
3041
|
+
echo " ℹ️ Multi-module JaCoCo: aggregating $_jc_n module report(s)."
|
|
3042
|
+
fi
|
|
3001
3043
|
|
|
3002
3044
|
if [ -n "$jacoco_csv" ]; then
|
|
3003
3045
|
# Aggregate INSTRUCTION_MISSED / INSTRUCTION_COVERED over every class row.
|
|
3004
3046
|
# Columns are located by header name because their position is not part of
|
|
3005
3047
|
# the CSV's stable contract.
|
|
3048
|
+
#
|
|
3049
|
+
# #603: with module reports, each file repeats the header row, so the
|
|
3050
|
+
# header detection is per-file (FNR == 1) rather than per-stream
|
|
3051
|
+
# (NR == 1). Otherwise module 2's header is summed as data, or worse,
|
|
3052
|
+
# the single header found in module 1 leaves the columns unset for it.
|
|
3053
|
+
_jc_inputs="$jacoco_csv"
|
|
3054
|
+
[ -n "$jacoco_all" ] && _jc_inputs="$jacoco_all"
|
|
3006
3055
|
COVERAGE_PERCENT=$(awk -F, '
|
|
3007
|
-
|
|
3056
|
+
FNR == 1 {
|
|
3057
|
+
missed_col = 0; covered_col = 0;
|
|
3008
3058
|
for (i = 1; i <= NF; i++) {
|
|
3009
3059
|
if ($i == "INSTRUCTION_MISSED") missed_col = i;
|
|
3010
3060
|
if ($i == "INSTRUCTION_COVERED") covered_col = i;
|
|
@@ -3020,7 +3070,7 @@ else
|
|
|
3020
3070
|
if (total > 0) printf "%.2f", (covered / total) * 100;
|
|
3021
3071
|
else print "";
|
|
3022
3072
|
}
|
|
3023
|
-
'
|
|
3073
|
+
' $_jc_inputs 2>/dev/null) || COVERAGE_PERCENT=""
|
|
3024
3074
|
|
|
3025
3075
|
if [ -n "$COVERAGE_PERCENT" ]; then
|
|
3026
3076
|
# Compare on the unrounded value so 79.5% cannot pass as 80%.
|
|
@@ -3312,7 +3362,20 @@ else
|
|
|
3312
3362
|
# "Tests + Coverage: PASS (coverage: N/A)" for a commit where no test ran
|
|
3313
3363
|
# (#554) -- a SKIP promoted to PASS inflates the pass rate and hides that the
|
|
3314
3364
|
# gate never executed.
|
|
3315
|
-
|
|
3365
|
+
#
|
|
3366
|
+
# #554 follow-up: the same promotion was still happening ONE LEVEL IN. The
|
|
3367
|
+
# early-skip branch above (no source extension among the changed files, e.g. a
|
|
3368
|
+
# version bump) sets GATE_5_STATUS="SKIP", and this unguarded assignment
|
|
3369
|
+
# overwrote it on the way out of the same else-block. A branch that records
|
|
3370
|
+
# its own verdict must not be re-decided by the block epilogue: default to
|
|
3371
|
+
# PASS only when the sentinel is still untouched.
|
|
3372
|
+
#
|
|
3373
|
+
# Not written as a `:-PASS` expansion: AC-599-03 forbids a PASS default in
|
|
3374
|
+
# any `GATE_N_STATUS:-DEFAULT` expansion, because that is how an unassigned
|
|
3375
|
+
# gate came to render as "passed". An explicit comparison keeps both rules.
|
|
3376
|
+
if [ -z "$GATE_5_STATUS" ]; then
|
|
3377
|
+
GATE_5_STATUS="PASS"
|
|
3378
|
+
fi
|
|
3316
3379
|
fi
|
|
3317
3380
|
record_gate_audit "gate-5" "tests-coverage" "$GATE_5_STATUS" "0" "$GATE_5_START" "${GATE_5_DETAIL:-}"
|
|
3318
3381
|
|
|
@@ -3455,18 +3518,77 @@ else
|
|
|
3455
3518
|
fi
|
|
3456
3519
|
;;
|
|
3457
3520
|
"java")
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3521
|
+
# #602: this branch used to be a guaranteed PASS no matter what happened.
|
|
3522
|
+
# 1) The test-dir probe hardcoded `src/test/java/architecture` at the
|
|
3523
|
+
# repo root. A multi-module Maven repo (core/ + demo/) puts the arch
|
|
3524
|
+
# tests under <module>/src/test/java/..., so the probe missed them
|
|
3525
|
+
# and the gate took the SKIP path -- reporting architecture as
|
|
3526
|
+
# "not configured" for a repo that HAS such tests.
|
|
3527
|
+
# 2) Even when it did run, `mvn ... | head -20` yields head's exit
|
|
3528
|
+
# status (0) as the pipeline result, so the `||` fallback never
|
|
3529
|
+
# fired and `echo "✅ ... completed"` printed unconditionally. A
|
|
3530
|
+
# failing architecture test was reported as success. Verified:
|
|
3531
|
+
# `false | head -20` exits 0; with `set -o pipefail` it exits 1.
|
|
3532
|
+
# Fix: discover the test source roots instead of guessing one path, and
|
|
3533
|
+
# judge the build by PIPESTATUS[0] (the pattern run_java_build_integrity
|
|
3534
|
+
# already uses), so the verdict comes from the build, not from `head`.
|
|
3535
|
+
_java_arch_roots=""
|
|
3536
|
+
if [ -f "pom.xml" ] || [ -x "./mvnw" ]; then
|
|
3537
|
+
# Any */src/test/java dir (module-relative root), including the repo
|
|
3538
|
+
# root itself, which find prints as ./src/test/java -> root ".".
|
|
3539
|
+
while IFS= read -r _root; do
|
|
3540
|
+
[ -n "$_root" ] && _java_arch_roots="${_java_arch_roots} ${_root}"
|
|
3541
|
+
done <<JAVAARCHROOTS
|
|
3542
|
+
$(find . -path ./target -prune -o -type d -path '*/src/test/java' -print 2>/dev/null \
|
|
3543
|
+
| sed 's|^\./||; s|/src/test/java$||; s|^src/test/java$|.|')
|
|
3544
|
+
JAVAARCHROOTS
|
|
3545
|
+
elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
|
|
3546
|
+
_java_arch_roots="."
|
|
3547
|
+
fi
|
|
3548
|
+
|
|
3549
|
+
# Only the roots that actually contain an *Architecture* test count.
|
|
3550
|
+
_java_arch_hit=""
|
|
3551
|
+
for _r in $_java_arch_roots; do
|
|
3552
|
+
_probe_dir="$_r/src/test/java"
|
|
3553
|
+
[ "$_r" = "." ] && _probe_dir="src/test/java"
|
|
3554
|
+
if find "$_probe_dir" -name '*Architecture*' -type f 2>/dev/null | head -1 | grep -q .; then
|
|
3555
|
+
_java_arch_hit="${_java_arch_hit} ${_r}"
|
|
3556
|
+
fi
|
|
3557
|
+
done
|
|
3558
|
+
|
|
3559
|
+
if [ -n "$_java_arch_hit" ]; then
|
|
3560
|
+
echo " Running Java architecture tests (roots:$_java_arch_hit)..."
|
|
3561
|
+
_java_arch_rc=0
|
|
3562
|
+
if [ -f "pom.xml" ] || [ -x "./mvnw" ]; then
|
|
3563
|
+
_mvn_bin="mvn"
|
|
3564
|
+
[ -x "./mvnw" ] && _mvn_bin="./mvnw"
|
|
3565
|
+
# -pl <module> -am runs only the module that holds the tests (plus
|
|
3566
|
+
# what it needs); quoting the whole root list into one -Dtest works
|
|
3567
|
+
# for Surefire because it accepts comma-separated patterns.
|
|
3568
|
+
for _r in $_java_arch_hit; do
|
|
3569
|
+
if [ "$_r" = "." ]; then
|
|
3570
|
+
"$_mvn_bin" -q test -Dtest='*Architecture*' 2>&1 | tail -20
|
|
3571
|
+
else
|
|
3572
|
+
"$_mvn_bin" -q -pl "$_r" -am test -Dtest='*Architecture*' 2>&1 | tail -20
|
|
3573
|
+
fi
|
|
3574
|
+
_rc="${PIPESTATUS[0]}"
|
|
3575
|
+
[ "$_rc" -ne 0 ] && _java_arch_rc="$_rc"
|
|
3576
|
+
done
|
|
3577
|
+
elif [ -x "./gradlew" ]; then
|
|
3578
|
+
./gradlew test --tests '*Architecture*' 2>&1 | tail -20
|
|
3579
|
+
_java_arch_rc="${PIPESTATUS[0]}"
|
|
3580
|
+
fi
|
|
3581
|
+
|
|
3582
|
+
if [ "$_java_arch_rc" -ne 0 ]; then
|
|
3583
|
+
echo ""
|
|
3584
|
+
echo "❌ BLOCKED - Java architecture test(s) failed (exit $_java_arch_rc)"
|
|
3585
|
+
echo "Fix the layering violation(s) above before committing."
|
|
3586
|
+
GATE_6_REASON_BY_LANG["$CURRENT_LANG"]="architecture_tests_failed"
|
|
3587
|
+
exit 1
|
|
3466
3588
|
fi
|
|
3467
3589
|
echo " ✅ Java architecture validation completed."
|
|
3468
3590
|
else
|
|
3469
|
-
echo " ℹ️ No
|
|
3591
|
+
echo " ℹ️ No *Architecture* test sources found under */src/test/java - skipping"
|
|
3470
3592
|
echo " ⏭️ SKIPPED - Architecture validation (no tests)"
|
|
3471
3593
|
# #537: no test files means Java architecture was NOT measured.
|
|
3472
3594
|
if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
|
|
@@ -3518,6 +3640,22 @@ else
|
|
|
3518
3640
|
else
|
|
3519
3641
|
GATE_6_REASON="$_g6_seen"
|
|
3520
3642
|
fi
|
|
3643
|
+
|
|
3644
|
+
# #602 (status): a language that was really measured and passed leaves NO skip
|
|
3645
|
+
# reason behind (see the #537 comment in the typescript arm: "archlint
|
|
3646
|
+
# actually ran here, so there is no skip reason"). The verdict was never
|
|
3647
|
+
# upgraded to match, so Gate 6 reported SKIP even when every language in
|
|
3648
|
+
# the project had been validated and passed -- a measured dimension
|
|
3649
|
+
# rendered as unmeasured. That is the mirror image of the bug #537 fixed,
|
|
3650
|
+
# and the report could not tell the two apart.
|
|
3651
|
+
#
|
|
3652
|
+
# Upgrade to PASS only when the measurement actually happened for EVERY
|
|
3653
|
+
# language: no language may carry a skip reason. A single skipped language
|
|
3654
|
+
# keeps the gate non-PASS, so "some languages measured" cannot be scored
|
|
3655
|
+
# as a full success.
|
|
3656
|
+
if [ -z "$GATE_6_LANG_REASONS" ] && [ -n "$PROJECT_LANGS" ]; then
|
|
3657
|
+
GATE_6_STATUS="PASS"
|
|
3658
|
+
fi
|
|
3521
3659
|
else
|
|
3522
3660
|
# Architecture config is REQUIRED — BLOCK if missing
|
|
3523
3661
|
if [ -f "$PROJECT_ROOT/.architecture-skip" ]; then
|
|
@@ -4237,6 +4375,7 @@ generate_quality_report() {
|
|
|
4237
4375
|
"gate5_tests": {
|
|
4238
4376
|
"name": "Tests + Coverage",
|
|
4239
4377
|
"status": "${GATE_5_STATUS:-SKIP}",
|
|
4378
|
+
"reason": "${GATE_5_DETAIL:-}",
|
|
4240
4379
|
"thresholds": { "lines": ${COVERAGE_THRESHOLD}, "functions": null, "branches": null, "statements": null },
|
|
4241
4380
|
"threshold_source": "${COVERAGE_THRESHOLD_SOURCE}",
|
|
4242
4381
|
"actual": { "coverage": "${COV_PCT}" },
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Mock layering policy enforcement — Gate M3 of pre-push hook. Ensures integration tests use real implementations for internal dependencies, mock external dependencies, and annotate pending mocks with removal plans. Combines project scope scanning, mock decision engine, and per-file validation into a single pipeline.
|
package/mutation/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MUTATION KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**Gate M** (incremental mutation testing) + **Gate M2** helpers (test-layer detection used by `src/mock-policy/`). Pre-push quality gate. TypeScript-only; uses Stryker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.4",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/plugins/dsh/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boyingliu01/dsh-plugin-xp-gate",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.4",
|
|
4
4
|
"description": "XP-Gate deterministic quality gates + AI workflow skills as a DeepSeek Harness (DSH) native plugin: gate-check / gate-principles / gate-arch tools + 12 SKILL.md skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.4",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
|
-
**Generated:** 2026-10-
|
|
4
|
-
**Commit:**
|
|
3
|
+
**Generated:** 2026-10-26
|
|
4
|
+
**Commit:** dfe3bf53
|
|
5
5
|
**Branch:** main
|
|
6
|
-
**Version:** 0.31.
|
|
6
|
+
**Version:** 0.31.4.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
|