@boyingliu01/xp-gate 0.33.1 → 0.33.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/gate-3.sh +48 -7
  2. package/gate-8.sh +32 -3
  3. package/gate-9.sh +14 -2
  4. package/gates/gate-7.ts +31 -10
  5. package/hooks/gate-3.sh +48 -7
  6. package/hooks/gate-8.sh +32 -3
  7. package/hooks/gate-9.sh +14 -2
  8. package/hooks/pre-commit +51 -2
  9. package/lib/mattpocock-license-verdict.json +1 -1
  10. package/mock-policy/AGENTS.md +2 -2
  11. package/mutation/AGENTS.md +2 -2
  12. package/package.json +1 -1
  13. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  14. package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
  15. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
  16. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
  17. package/plugins/dsh/package.json +1 -1
  18. package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
  19. package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
  20. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
  21. package/plugins/opencode/package.json +1 -1
  22. package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
  23. package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
  24. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
  25. package/plugins/qoder/plugin.json +1 -1
  26. package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
  27. package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
  28. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
  29. package/principles/AGENTS.md +2 -2
  30. package/skills/delphi-review/AGENTS.md +2 -2
  31. package/skills/sprint-flow/AGENTS.md +2 -2
  32. package/skills/test-specification-alignment/AGENTS.md +2 -2
package/gate-3.sh CHANGED
@@ -172,8 +172,19 @@ elif [ "$PROJECT_LANG" = "powershell" ]; then
172
172
  const entries = baseline.functions || baseline.entries || baseline;
173
173
  if (Array.isArray(entries)) {
174
174
  for (const e of entries) {
175
- if (!e || !e.file || !e.name) continue;
176
- known.set(norm(e.file) + "::" + e.name, Number(e.ccn) || 0);
175
+ // #632: accept the key the baseline WRITER actually emits.
176
+ // tools/Update-ComplexityBaseline.ps1 (the tool this gate
177
+ // points at in its own fix-it message) writes "function",
178
+ // while this reader only knew "name" -- so every entry was
179
+ // dropped, "known" stayed empty, and every function above
180
+ // the threshold became a brand-new function. The ratchet
181
+ // could never pass. Both spellings are read: this is a
182
+ // reader, and the writer vocabulary is not ours to
183
+ // redefine from here.
184
+ if (!e || !e.file) continue;
185
+ const fnName = e.name || e.function;
186
+ if (!fnName) continue;
187
+ known.set(norm(e.file) + "::" + fnName, Number(e.ccn) || 0);
177
188
  }
178
189
  }
179
190
  // The measurement emits ABSOLUTE paths (pwsh resolved them), while
@@ -529,13 +540,43 @@ else
529
540
  # this, a crashing or mis-invoked lizard yields 0 warnings and the gate
530
541
  # reports PASSED on zero analysed functions -- the same silent-success
531
542
  # failure this issue is about.
543
+ #
544
+ # #666: "no rows" has TWO causes and they must not share a verdict. A
545
+ # module of pure constants legitimately has zero functions, and lizard
546
+ # emits no row for it at all (measured: `lizard pure_data.py --csv` prints
547
+ # nothing and exits 0). That was reported as `lizard_output_unparsable`
548
+ # /WARN, which is the message for a BROKEN lizard -- so a healthy run on a
549
+ # data-only diff was indistinguishable from a tooling fault. The two are
550
+ # separated by a POSITIVE probe: ask lizard to describe a fixture known to
551
+ # contain a function. If it produces rows, lizard works and the input
552
+ # simply had no functions (SKIP, `no_functions_in_input`); if it does not,
553
+ # the tool really is broken (WARN, `lizard_output_unparsable`).
532
554
  if ! printf '%s\n' "$CC_CSV_FLAT" | awk -F, 'NF > 1 && $2 ~ /^[0-9]+$/ { found = 1; exit }
533
555
  END { exit(found ? 0 : 1) }'; then
534
- echo "⚠️ WARN - lizard produced no parsable results; complexity not verified"
535
- GATE_3_STATUS="WARN"
536
- # Consumed by the sourcing parent (pre-commit report JSON), not here.
537
- # shellcheck disable=SC2034
538
- GATE_3_REASON="lizard_output_unparsable"
556
+ _lizard_probe_dir=$(mktemp -d 2>/dev/null || echo "${TMPDIR:-/tmp}")
557
+ _lizard_probe_file="$_lizard_probe_dir/xp-gate-lizard-probe.py"
558
+ printf 'def _xp_gate_probe(x):\n if x:\n return 1\n return 0\n' > "$_lizard_probe_file" 2>/dev/null
559
+ _lizard_probe_rows=0
560
+ if [ -f "$_lizard_probe_file" ]; then
561
+ _lizard_probe_rows=$(printf '%s\n' "$_lizard_probe_file" \
562
+ | lizard_batches --csv 2>/dev/null \
563
+ | awk -F, 'NF > 1 && $2 ~ /^[0-9]+$/ { c++ } END { print c + 0 }')
564
+ fi
565
+ rm -rf "$_lizard_probe_dir" 2>/dev/null || true
566
+ if [ "${_lizard_probe_rows:-0}" -gt 0 ]; then
567
+ echo "⏭️ SKIPPED - Complexity: the changed source files define no functions (pure data module)."
568
+ echo " Verified: lizard parsed a probe fixture normally, so this is an empty result set, not a broken tool."
569
+ GATE_3_STATUS="SKIP"
570
+ # Consumed by the sourcing parent (pre-commit report JSON), not here.
571
+ # shellcheck disable=SC2034
572
+ GATE_3_REASON="no_functions_in_input"
573
+ else
574
+ echo "⚠️ WARN - lizard produced no parsable results; complexity not verified"
575
+ GATE_3_STATUS="WARN"
576
+ # Consumed by the sourcing parent (pre-commit report JSON), not here.
577
+ # shellcheck disable=SC2034
578
+ GATE_3_REASON="lizard_output_unparsable"
579
+ fi
539
580
  elif [ "$CC_WARNINGS" -gt 0 ]; then
540
581
  echo "$CC_OUTPUT"
541
582
  echo ""
package/gate-8.sh CHANGED
@@ -59,10 +59,24 @@ fi
59
59
  # revisions and reports "0 commits scanned" on a pre-commit run -- so a staged
60
60
  # secret was never seen and Gate 8 always passed. `git --staged` scans
61
61
  # `git diff --cached`, which is exactly the pre-commit payload (#449).
62
+ #
63
+ # #616: `--all` is a different question, and the index is the wrong place to ask
64
+ # it. Under `xp-gate check . --all` the user asked "is the whole tree clean", the
65
+ # index is usually empty, `git --staged` legitimately found nothing, and that
66
+ # nothing was counted as PASSED -- a secret sitting in a tracked file was
67
+ # invisible to the one command meant to find it. The runner already exports
68
+ # GATE_SCOPE (#589); this fragment just has to read it. `dir --no-git` scans the
69
+ # filesystem so tracked-but-unmodified files are covered.
62
70
  GITLEAKS_REPORT="${TMPDIR:-/tmp}/gitleaks-report.json"
63
71
  rm -f "$GITLEAKS_REPORT"
64
- GITLEAKS_OUTPUT=$($GITLEAKS_CMD git --staged --redact --no-banner $GITLEAKS_CONFIG --report-format=json --report-path="$GITLEAKS_REPORT" 2>&1)
65
- GITLEAKS_EXIT=$?
72
+ if [ "${GATE_SCOPE:-diff}" = "all" ]; then
73
+ echo " Scope: --all (scanning the working tree, not the index)"
74
+ GITLEAKS_OUTPUT=$($GITLEAKS_CMD dir --no-git --redact --no-banner $GITLEAKS_CONFIG --report-format=json --report-path="$GITLEAKS_REPORT" 2>&1)
75
+ GITLEAKS_EXIT=$?
76
+ else
77
+ GITLEAKS_OUTPUT=$($GITLEAKS_CMD git --staged --redact --no-banner $GITLEAKS_CONFIG --report-format=json --report-path="$GITLEAKS_REPORT" 2>&1)
78
+ GITLEAKS_EXIT=$?
79
+ fi
66
80
 
67
81
  if [ "$GITLEAKS_EXIT" -eq 0 ]; then
68
82
  # Decide SKIP from the REPORT ARTIFACT, not from log wording. Parsing stdout
@@ -183,7 +197,22 @@ fi
183
197
  # if-condition (errexit-exempt); a non-zero hook exit lands in the else branch.
184
198
  if command -v detect-secrets-hook >/dev/null 2>&1 && [ -f ".secrets.baseline" ]; then
185
199
  DS_TMP="$(dirname "$(mktemp -u)")/ds-output.$$"
186
- if git diff --cached --name-only --diff-filter=ACM -z \
200
+ # #616: same scope split as the gitleaks call above. Under `--all` the index is
201
+ # usually empty, so piping `git diff --cached -z` into xargs fed the hook zero
202
+ # files and it reported success without reading anything.
203
+ if [ "${GATE_SCOPE:-diff}" = "all" ]; then
204
+ if git ls-files -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline > "$DS_TMP" 2>&1; then
205
+ echo " ✅ detect-secrets PASSED - no new secrets beyond baseline (scope: --all)."
206
+ else
207
+ cat "$DS_TMP"
208
+ echo ""
209
+ echo " ❌ BLOCKED - detect-secrets found secrets not in .secrets.baseline."
210
+ echo " If these are false positives, regenerate the baseline (cross-platform):"
211
+ echo " detect-secrets scan --update .secrets.baseline && git add .secrets.baseline"
212
+ rm -f "$DS_TMP"
213
+ exit 1
214
+ fi
215
+ elif git diff --cached --name-only --diff-filter=ACM -z \
187
216
  | xargs -0 detect-secrets-hook --baseline .secrets.baseline > "$DS_TMP" 2>&1; then
188
217
  echo " ✅ detect-secrets PASSED - no new secrets beyond baseline."
189
218
  else
package/gate-9.sh CHANGED
@@ -32,8 +32,20 @@ if [ -z "$SEMGREP_CMD" ]; then
32
32
  echo " Gate 9: SAST Security (WARN, semgrep not installed)"
33
33
  GATE_9_STATUS="WARN"
34
34
  else
35
- # Get staged files filtered to Semgrep-supported languages
36
- 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)
35
+ # Get staged files filtered to Semgrep-supported languages.
36
+ #
37
+ # #616: under `--all` the index is usually empty, so the staged diff yielded no
38
+ # files and the gate reported SKIPPED -- "no supported language files changed"
39
+ # -- for a run whose whole purpose was to scan the tree. Same fail-open shape
40
+ # #589 fixed in Gate 3, one layer down: the CLI says the tree was checked and
41
+ # the fragment checks a diff that does not exist. The runner exports GATE_SCOPE;
42
+ # read it and enumerate tracked files instead.
43
+ if [ "${GATE_SCOPE:-diff}" = "all" ]; then
44
+ echo " Scope: --all (scanning tracked files, not the index)"
45
+ SEMGREP_FILES=$(git ls-files 2>/dev/null | grep -E '\.(ts|tsx|js|jsx|py|go|java|c|cpp|cs|rb|php|scala|swift)$' || true)
46
+ else
47
+ 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)
48
+ fi
37
49
 
38
50
  if [ -z "$SEMGREP_FILES" ]; then
39
51
  echo " ⏭️ SKIPPED - SAST (no supported language files changed)"
package/gates/gate-7.ts CHANGED
@@ -27,16 +27,13 @@ export function runGate7(input: GateInput): GateOutput {
27
27
  return { status: 'PASS', messages, exitCode: 0 };
28
28
  }
29
29
 
30
- // Check for IaC adapter script (bash path for pre-commit context)
31
- const adapterPath = findIaCAdapter(cwd);
32
- if (!adapterPath) {
33
- messages.push('ℹ️ SKIP - IaC adapter not found');
34
- messages.push('⏭️ SKIPPED - IaC security (no IaC adapter found)');
35
- recordAudit('gate-7', 'iac-security', 'SKIP', 0, startMs);
36
- return { status: 'SKIP', messages, exitCode: 0 };
37
- }
38
-
39
- // Run checkov if available (preferred tool)
30
+ // checkov is the PREFERRED engine (see the module header and the fallback
31
+ // message below), so it is tried FIRST. #650: the adapter lookup used to run
32
+ // before this and SKIP the gate whenever a consumer project had no vendored
33
+ // `githooks/adapters/iac.sh` -- even with checkov installed and on PATH. A
34
+ // security gate that skips because an optional helper script is absent, while
35
+ // the real scanner is available, reports "nothing to check" about code that was
36
+ // never checked. The bash adapter is the FALLBACK, so it is consulted after.
40
37
  const checkov = isToolAvailable('checkov', cwd);
41
38
  if (checkov.available) {
42
39
  return runCheckov(checkov.path, checkov.via, iacFiles, cwd, startMs, messages);
@@ -46,6 +43,30 @@ export function runGate7(input: GateInput): GateOutput {
46
43
  messages.push('⚠️ checkov not installed — falling back to bash IaC adapter');
47
44
  messages.push(' Install: pip install checkov');
48
45
 
46
+ return runBashAdapter(iacFiles, cwd, startMs, messages);
47
+ }
48
+
49
+ /**
50
+ * Fallback path: hand the IaC files to the project's bash adapter.
51
+ *
52
+ * Extracted from `runGate7` so the primary (checkov) path stays readable and the
53
+ * entry point stays inside the long-function limit. A missing adapter is a SKIP,
54
+ * never a PASS: no scan ran, so there is no evidence to report as clean.
55
+ */
56
+ function runBashAdapter(
57
+ iacFiles: string[],
58
+ cwd: string,
59
+ startMs: number,
60
+ messages: string[]
61
+ ): GateOutput {
62
+ const adapterPath = findIaCAdapter(cwd);
63
+ if (!adapterPath) {
64
+ messages.push('ℹ️ SKIP - IaC adapter not found');
65
+ messages.push('⏭️ SKIPPED - IaC security (no IaC adapter found)');
66
+ recordAudit('gate-7', 'iac-security', 'SKIP', 0, startMs);
67
+ return { status: 'SKIP', messages, exitCode: 0 };
68
+ }
69
+
49
70
  const result = runTool('bash', [adapterPath, ...iacFiles], { cwd });
50
71
  const combinedOutput = result.stdout + result.stderr;
51
72
 
package/hooks/gate-3.sh CHANGED
@@ -172,8 +172,19 @@ elif [ "$PROJECT_LANG" = "powershell" ]; then
172
172
  const entries = baseline.functions || baseline.entries || baseline;
173
173
  if (Array.isArray(entries)) {
174
174
  for (const e of entries) {
175
- if (!e || !e.file || !e.name) continue;
176
- known.set(norm(e.file) + "::" + e.name, Number(e.ccn) || 0);
175
+ // #632: accept the key the baseline WRITER actually emits.
176
+ // tools/Update-ComplexityBaseline.ps1 (the tool this gate
177
+ // points at in its own fix-it message) writes "function",
178
+ // while this reader only knew "name" -- so every entry was
179
+ // dropped, "known" stayed empty, and every function above
180
+ // the threshold became a brand-new function. The ratchet
181
+ // could never pass. Both spellings are read: this is a
182
+ // reader, and the writer vocabulary is not ours to
183
+ // redefine from here.
184
+ if (!e || !e.file) continue;
185
+ const fnName = e.name || e.function;
186
+ if (!fnName) continue;
187
+ known.set(norm(e.file) + "::" + fnName, Number(e.ccn) || 0);
177
188
  }
178
189
  }
179
190
  // The measurement emits ABSOLUTE paths (pwsh resolved them), while
@@ -529,13 +540,43 @@ else
529
540
  # this, a crashing or mis-invoked lizard yields 0 warnings and the gate
530
541
  # reports PASSED on zero analysed functions -- the same silent-success
531
542
  # failure this issue is about.
543
+ #
544
+ # #666: "no rows" has TWO causes and they must not share a verdict. A
545
+ # module of pure constants legitimately has zero functions, and lizard
546
+ # emits no row for it at all (measured: `lizard pure_data.py --csv` prints
547
+ # nothing and exits 0). That was reported as `lizard_output_unparsable`
548
+ # /WARN, which is the message for a BROKEN lizard -- so a healthy run on a
549
+ # data-only diff was indistinguishable from a tooling fault. The two are
550
+ # separated by a POSITIVE probe: ask lizard to describe a fixture known to
551
+ # contain a function. If it produces rows, lizard works and the input
552
+ # simply had no functions (SKIP, `no_functions_in_input`); if it does not,
553
+ # the tool really is broken (WARN, `lizard_output_unparsable`).
532
554
  if ! printf '%s\n' "$CC_CSV_FLAT" | awk -F, 'NF > 1 && $2 ~ /^[0-9]+$/ { found = 1; exit }
533
555
  END { exit(found ? 0 : 1) }'; then
534
- echo "⚠️ WARN - lizard produced no parsable results; complexity not verified"
535
- GATE_3_STATUS="WARN"
536
- # Consumed by the sourcing parent (pre-commit report JSON), not here.
537
- # shellcheck disable=SC2034
538
- GATE_3_REASON="lizard_output_unparsable"
556
+ _lizard_probe_dir=$(mktemp -d 2>/dev/null || echo "${TMPDIR:-/tmp}")
557
+ _lizard_probe_file="$_lizard_probe_dir/xp-gate-lizard-probe.py"
558
+ printf 'def _xp_gate_probe(x):\n if x:\n return 1\n return 0\n' > "$_lizard_probe_file" 2>/dev/null
559
+ _lizard_probe_rows=0
560
+ if [ -f "$_lizard_probe_file" ]; then
561
+ _lizard_probe_rows=$(printf '%s\n' "$_lizard_probe_file" \
562
+ | lizard_batches --csv 2>/dev/null \
563
+ | awk -F, 'NF > 1 && $2 ~ /^[0-9]+$/ { c++ } END { print c + 0 }')
564
+ fi
565
+ rm -rf "$_lizard_probe_dir" 2>/dev/null || true
566
+ if [ "${_lizard_probe_rows:-0}" -gt 0 ]; then
567
+ echo "⏭️ SKIPPED - Complexity: the changed source files define no functions (pure data module)."
568
+ echo " Verified: lizard parsed a probe fixture normally, so this is an empty result set, not a broken tool."
569
+ GATE_3_STATUS="SKIP"
570
+ # Consumed by the sourcing parent (pre-commit report JSON), not here.
571
+ # shellcheck disable=SC2034
572
+ GATE_3_REASON="no_functions_in_input"
573
+ else
574
+ echo "⚠️ WARN - lizard produced no parsable results; complexity not verified"
575
+ GATE_3_STATUS="WARN"
576
+ # Consumed by the sourcing parent (pre-commit report JSON), not here.
577
+ # shellcheck disable=SC2034
578
+ GATE_3_REASON="lizard_output_unparsable"
579
+ fi
539
580
  elif [ "$CC_WARNINGS" -gt 0 ]; then
540
581
  echo "$CC_OUTPUT"
541
582
  echo ""
package/hooks/gate-8.sh CHANGED
@@ -59,10 +59,24 @@ fi
59
59
  # revisions and reports "0 commits scanned" on a pre-commit run -- so a staged
60
60
  # secret was never seen and Gate 8 always passed. `git --staged` scans
61
61
  # `git diff --cached`, which is exactly the pre-commit payload (#449).
62
+ #
63
+ # #616: `--all` is a different question, and the index is the wrong place to ask
64
+ # it. Under `xp-gate check . --all` the user asked "is the whole tree clean", the
65
+ # index is usually empty, `git --staged` legitimately found nothing, and that
66
+ # nothing was counted as PASSED -- a secret sitting in a tracked file was
67
+ # invisible to the one command meant to find it. The runner already exports
68
+ # GATE_SCOPE (#589); this fragment just has to read it. `dir --no-git` scans the
69
+ # filesystem so tracked-but-unmodified files are covered.
62
70
  GITLEAKS_REPORT="${TMPDIR:-/tmp}/gitleaks-report.json"
63
71
  rm -f "$GITLEAKS_REPORT"
64
- GITLEAKS_OUTPUT=$($GITLEAKS_CMD git --staged --redact --no-banner $GITLEAKS_CONFIG --report-format=json --report-path="$GITLEAKS_REPORT" 2>&1)
65
- GITLEAKS_EXIT=$?
72
+ if [ "${GATE_SCOPE:-diff}" = "all" ]; then
73
+ echo " Scope: --all (scanning the working tree, not the index)"
74
+ GITLEAKS_OUTPUT=$($GITLEAKS_CMD dir --no-git --redact --no-banner $GITLEAKS_CONFIG --report-format=json --report-path="$GITLEAKS_REPORT" 2>&1)
75
+ GITLEAKS_EXIT=$?
76
+ else
77
+ GITLEAKS_OUTPUT=$($GITLEAKS_CMD git --staged --redact --no-banner $GITLEAKS_CONFIG --report-format=json --report-path="$GITLEAKS_REPORT" 2>&1)
78
+ GITLEAKS_EXIT=$?
79
+ fi
66
80
 
67
81
  if [ "$GITLEAKS_EXIT" -eq 0 ]; then
68
82
  # Decide SKIP from the REPORT ARTIFACT, not from log wording. Parsing stdout
@@ -183,7 +197,22 @@ fi
183
197
  # if-condition (errexit-exempt); a non-zero hook exit lands in the else branch.
184
198
  if command -v detect-secrets-hook >/dev/null 2>&1 && [ -f ".secrets.baseline" ]; then
185
199
  DS_TMP="$(dirname "$(mktemp -u)")/ds-output.$$"
186
- if git diff --cached --name-only --diff-filter=ACM -z \
200
+ # #616: same scope split as the gitleaks call above. Under `--all` the index is
201
+ # usually empty, so piping `git diff --cached -z` into xargs fed the hook zero
202
+ # files and it reported success without reading anything.
203
+ if [ "${GATE_SCOPE:-diff}" = "all" ]; then
204
+ if git ls-files -z | xargs -0 detect-secrets-hook --baseline .secrets.baseline > "$DS_TMP" 2>&1; then
205
+ echo " ✅ detect-secrets PASSED - no new secrets beyond baseline (scope: --all)."
206
+ else
207
+ cat "$DS_TMP"
208
+ echo ""
209
+ echo " ❌ BLOCKED - detect-secrets found secrets not in .secrets.baseline."
210
+ echo " If these are false positives, regenerate the baseline (cross-platform):"
211
+ echo " detect-secrets scan --update .secrets.baseline && git add .secrets.baseline"
212
+ rm -f "$DS_TMP"
213
+ exit 1
214
+ fi
215
+ elif git diff --cached --name-only --diff-filter=ACM -z \
187
216
  | xargs -0 detect-secrets-hook --baseline .secrets.baseline > "$DS_TMP" 2>&1; then
188
217
  echo " ✅ detect-secrets PASSED - no new secrets beyond baseline."
189
218
  else
package/hooks/gate-9.sh CHANGED
@@ -32,8 +32,20 @@ if [ -z "$SEMGREP_CMD" ]; then
32
32
  echo " Gate 9: SAST Security (WARN, semgrep not installed)"
33
33
  GATE_9_STATUS="WARN"
34
34
  else
35
- # Get staged files filtered to Semgrep-supported languages
36
- 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)
35
+ # Get staged files filtered to Semgrep-supported languages.
36
+ #
37
+ # #616: under `--all` the index is usually empty, so the staged diff yielded no
38
+ # files and the gate reported SKIPPED -- "no supported language files changed"
39
+ # -- for a run whose whole purpose was to scan the tree. Same fail-open shape
40
+ # #589 fixed in Gate 3, one layer down: the CLI says the tree was checked and
41
+ # the fragment checks a diff that does not exist. The runner exports GATE_SCOPE;
42
+ # read it and enumerate tracked files instead.
43
+ if [ "${GATE_SCOPE:-diff}" = "all" ]; then
44
+ echo " Scope: --all (scanning tracked files, not the index)"
45
+ SEMGREP_FILES=$(git ls-files 2>/dev/null | grep -E '\.(ts|tsx|js|jsx|py|go|java|c|cpp|cs|rb|php|scala|swift)$' || true)
46
+ else
47
+ 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)
48
+ fi
37
49
 
38
50
  if [ -z "$SEMGREP_FILES" ]; then
39
51
  echo " ⏭️ SKIPPED - SAST (no supported language files changed)"
package/hooks/pre-commit CHANGED
@@ -3495,6 +3495,29 @@ else
3495
3495
  GATE_5_STATUS="PASS"
3496
3496
  fi
3497
3497
  fi
3498
+
3499
+ # #665: a subset run is exempt from the GLOBAL coverage floor by design (#473),
3500
+ # but the exemption was never published, so the persisted report showed
3501
+ # `actual.coverage = 29` beside `thresholds.lines = 82` with `status = PASS` and
3502
+ # `reason = ""`. The verdict was correct; the RECORD was unreadable, and a record
3503
+ # is all `xp-gate audit` and the tech-debt trackers (#636, #638) ever see. Two
3504
+ # opposite situations -- "coverage really was 29% and the gate passed it" and
3505
+ # "a subset run, global floor not applicable" -- rendered identically.
3506
+ #
3507
+ # Derived from PARTIAL_TEST_RUN at ONE place instead of being set by hand at each
3508
+ # of the six branches that mark a partial run: a reason written at one call site
3509
+ # and forgotten at another is the drift that produced the empty reason here.
3510
+ if [ "${PARTIAL_TEST_RUN:-false}" = "true" ]; then
3511
+ GATE_5_COVERAGE_APPLIES_TO="subset"
3512
+ # Never overwrite a reason the branch itself recorded (docs_only_change,
3513
+ # no_code_files_changed, ...) -- those explain a different verdict.
3514
+ if [ -z "${GATE_5_DETAIL:-}" ] && [ "$GATE_5_STATUS" = "PASS" ]; then
3515
+ GATE_5_DETAIL="partial_test_run_coverage_not_applicable"
3516
+ fi
3517
+ else
3518
+ GATE_5_COVERAGE_APPLIES_TO="project"
3519
+ fi
3520
+
3498
3521
  record_gate_audit "gate-5" "tests-coverage" "$GATE_5_STATUS" "0" "$GATE_5_START" "${GATE_5_DETAIL:-}"
3499
3522
 
3500
3523
  # ============================================================================
@@ -3619,7 +3642,26 @@ else
3619
3642
  if [ -f "arch-go.yaml" ] || [ -f "arch-go.yml" ]; then
3620
3643
  if require_tool "arch-go" "Gate 6" "go install github.com/arch-go/arch-go@latest"; then
3621
3644
  echo " Running arch-go for Go..."
3645
+ # #630: #602 replaced the vacuous Java arm with a status-based verdict,
3646
+ # but the Go arm kept the identical fail-open shape: it piped arch-go
3647
+ # output into `head` and then printed "Go architecture validation
3648
+ # completed" unconditionally. A pipeline's status is `head`'s -- 0 for
3649
+ # ANY non-empty output -- so every arch-go result, contract violations
3650
+ # included, printed "completed" and the commit landed. A gate that
3651
+ # cannot fail is not a gate. Capture the checker's own status and
3652
+ # block on it, as the Python arm above and run_java_build_integrity do.
3622
3653
  arch-go check 2>&1 | head -20
3654
+ # The pipeline's own status is head's, so the checker's verdict has
3655
+ # to come from PIPESTATUS[0] (#630). This is the same reading #602
3656
+ # installed for Java via run_java_build_integrity.
3657
+ _go_arch_rc="${PIPESTATUS[0]}"
3658
+ if [ "$_go_arch_rc" -ne 0 ]; then
3659
+ echo ""
3660
+ echo "❌ BLOCKED - Go architecture contract violations (arch-go, exit $_go_arch_rc)"
3661
+ echo "Fix the layering violation(s) above before committing."
3662
+ GATE_6_REASON_BY_LANG["$CURRENT_LANG"]="architecture_tests_failed"
3663
+ exit 1
3664
+ fi
3623
3665
  echo " ✅ Go architecture validation completed."
3624
3666
  else
3625
3667
  echo " ❌ BLOCKED - arch-go not installed"
@@ -4583,7 +4625,7 @@ generate_quality_report() {
4583
4625
  "reason": "${GATE_5_DETAIL:-}",
4584
4626
  "thresholds": { "lines": ${COVERAGE_THRESHOLD}, "functions": null, "branches": null, "statements": null },
4585
4627
  "threshold_source": "${COVERAGE_THRESHOLD_SOURCE}",
4586
- "actual": { "coverage": "${COV_PCT}" },
4628
+ "actual": { "coverage": "${COV_PCT}", "applies_to": "${GATE_5_COVERAGE_APPLIES_TO:-project}" },
4587
4629
  "failedTests": $FAILED_TESTS_JSON
4588
4630
  },
4589
4631
  "gate6_arch_boyscout": {
@@ -4681,7 +4723,14 @@ ENDJSON
4681
4723
  printf " %-45s %s\n" "Gate 2: Duplicate Code" "${GATE_2_STATUS:-SKIP}"
4682
4724
  printf " %-45s %s (warnings: ${CC_WARNINGS:-0})\n" "Gate 3: Complexity" "${GATE_3_STATUS:-SKIP}"
4683
4725
  printf " %-45s %s (warnings: ${WARNING_COUNT:-0})\n" "Gate 4: Principles" "${GATE_4_STATUS:-SKIP}"
4684
- printf " %-45s %s (coverage: ${COV_PCT}%%)\n" "Gate 5: Tests + Coverage" "${GATE_5_STATUS:-SKIP}"
4726
+ # #665: a subset run's percentage describes a SUBSET, not the project, and the
4727
+ # global floor was not applied. Printing the bare number next to a PASS reads as
4728
+ # "29% passed an 82% floor". Say which population the number came from.
4729
+ if [ "${GATE_5_COVERAGE_APPLIES_TO:-project}" = "subset" ]; then
4730
+ printf " %-45s %s (coverage: ${COV_PCT}%%, subset run -- global floor not checked)\n" "Gate 5: Tests + Coverage" "${GATE_5_STATUS:-SKIP}"
4731
+ else
4732
+ printf " %-45s %s (coverage: ${COV_PCT}%%)\n" "Gate 5: Tests + Coverage" "${GATE_5_STATUS:-SKIP}"
4733
+ fi
4685
4734
  printf " %-45s %s\n" "Gate 6: Architecture + Boy Scout" "${GATE_6_STATUS:-SKIP}"
4686
4735
  printf " %-45s %s\n" "Gate 7: IaC Security" "${GATE_7_STATUS:-SKIP}"
4687
4736
  printf " %-45s %s\n" "Gate 8: Secret Scanning" "${GATE_8_STATUS:-SKIP}"
@@ -2,6 +2,6 @@
2
2
  "verdict": "PASS",
3
3
  "license_type": "MIT",
4
4
  "sha256": "0e7ac423bf2c6e223b7c5b156f8cf72da49d748e56a1641402c31f22ad07dbb5",
5
- "checked_at": "2026-10-30T12:42:58.890Z",
5
+ "checked_at": "2026-10-30T13:23:44.380Z",
6
6
  "source": "network"
7
7
  }
@@ -1,9 +1,9 @@
1
1
  # SRC/MOCK-POLICY KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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.33.1",
3
+ "version": "0.33.2",
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.33.1",
3
+ "version": "0.33.2",
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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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.33.1",
3
+ "version": "0.33.2",
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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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.33.1",
3
+ "version": "0.33.2",
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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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.33.1",
3
+ "version": "0.33.2",
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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
  # PRINCIPLES CHECKER MODULE
2
2
 
3
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Clean Code & SOLID principles checker — **Gate 4** of pre-commit. 14 rules × 9 language adapters, SARIF 2.1.0 output. Houses the **Boy Scout Rule** enforcement engine (Gate 6) and warning-baseline storage.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.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
3
  **Generated:** 2026-10-30
4
- **Commit:** e713c2d1
4
+ **Commit:** b45021b6
5
5
  **Branch:** main
6
- **Version:** 0.33.1.0
6
+ **Version:** 0.33.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.