@boyingliu01/xp-gate 0.27.4 → 0.27.5

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