@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.
Files changed (36) hide show
  1. package/gate-3.sh +244 -9
  2. package/hooks/gate-3.sh +244 -9
  3. package/hooks/lib/Measure-PowerShellComplexity.ps1 +186 -0
  4. package/hooks/pre-commit +152 -13
  5. package/lib/mattpocock-license-verdict.json +1 -1
  6. package/mock-policy/AGENTS.md +3 -3
  7. package/mutation/AGENTS.md +3 -3
  8. package/package.json +1 -1
  9. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  10. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  11. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  12. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  13. package/plugins/dsh/package.json +1 -1
  14. package/plugins/dsh/skills/delphi-review/AGENTS.md +3 -3
  15. package/plugins/dsh/skills/sprint-flow/AGENTS.md +3 -3
  16. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +3 -3
  17. package/plugins/opencode/package.json +1 -1
  18. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  19. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  20. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  21. package/plugins/qoder/plugin.json +1 -1
  22. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  23. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  24. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  25. package/principles/AGENTS.md +3 -3
  26. package/principles/__tests__/baseline-entry-total-warnings.test.ts +76 -0
  27. package/principles/adapters/__tests__/cpp-code-block.test.ts +123 -0
  28. package/principles/adapters/__tests__/cpp-scanner-equivalence.test.ts +67 -0
  29. package/principles/adapters/__tests__/typescript-block-comment-lines.test.ts +53 -0
  30. package/principles/adapters/__tests__/typescript-masking.test.ts +96 -0
  31. package/principles/adapters/cpp.ts +102 -50
  32. package/principles/adapters/typescript.ts +503 -174
  33. package/principles/baseline.ts +58 -70
  34. package/skills/delphi-review/AGENTS.md +3 -3
  35. package/skills/sprint-flow/AGENTS.md +3 -3
  36. 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
- echo "✅ PASSED - No code files changed (only docs/config/VERSION), skipping test run."
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
- NR == 1 {
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
- ' "$jacoco_csv" 2>/dev/null) || COVERAGE_PERCENT=""
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
- GATE_5_STATUS="PASS"
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
- if [ -f "src/test/java/architecture" ] || [ -d "src/test/java/architecture" ]; then
3459
- echo " Running Java architecture tests..."
3460
- if [ -f "pom.xml" ]; then
3461
- mvn test -Dtest=architecture.* 2>&1 | head -20 || mvn test -Dtest=**Architecture** 2>&1 | head -20 || echo "⚠️ Java architecture tests completed"
3462
- elif [ -f "build.gradle" ]; then
3463
- if [ -f "./gradlew" ]; then
3464
- ./gradlew test --tests "*Architecture*" 2>&1 | head -20 || echo "⚠️ Java architecture tests completed"
3465
- fi
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 architecture test files found - skipping"
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}" },
@@ -2,6 +2,6 @@
2
2
  "verdict": "PASS",
3
3
  "license_type": "MIT",
4
4
  "sha256": "0e7ac423bf2c6e223b7c5b156f8cf72da49d748e56a1641402c31f22ad07dbb5",
5
- "checked_at": "2026-10-25T15:15:20.118Z",
5
+ "checked_at": "2026-10-26T09:56:14.218Z",
6
6
  "source": "network"
7
7
  }
@@ -1,9 +1,9 @@
1
1
  # SRC/MOCK-POLICY KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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.
@@ -1,9 +1,9 @@
1
1
  # SRC/MUTATION KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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": "@boyingliu01/xp-gate",
3
- "version": "0.31.2",
3
+ "version": "0.31.4",
4
4
  "description": "AI-driven development workflow: 6 quality gates + Delphi review + Sprint Flow",
5
5
  "bin": {
6
6
  "xp-gate": "bin/xp-gate.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.31.2",
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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": "@boyingliu01/dsh-plugin-xp-gate",
3
- "version": "0.31.2",
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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": "@boyingliu01/opencode-plugin",
3
- "version": "0.31.2",
3
+ "version": "0.31.4",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "XP-Gate quality gates + AI workflow skills + Sprint Flow TUI sidebar for OpenCode",
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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.2",
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-25
4
- **Commit:** 1ab53eff
3
+ **Generated:** 2026-10-26
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.2.0
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.