@boyingliu01/xp-gate 0.31.3 → 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 (33) hide show
  1. package/hooks/pre-commit +120 -11
  2. package/lib/mattpocock-license-verdict.json +1 -1
  3. package/mock-policy/AGENTS.md +2 -2
  4. package/mutation/AGENTS.md +2 -2
  5. package/package.json +1 -1
  6. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  7. package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
  8. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
  9. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
  10. package/plugins/dsh/package.json +1 -1
  11. package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
  12. package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
  13. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
  14. package/plugins/opencode/package.json +1 -1
  15. package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
  16. package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
  17. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
  18. package/plugins/qoder/plugin.json +1 -1
  19. package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
  20. package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
  21. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
  22. package/principles/AGENTS.md +2 -2
  23. package/principles/__tests__/baseline-entry-total-warnings.test.ts +76 -0
  24. package/principles/adapters/__tests__/cpp-code-block.test.ts +123 -0
  25. package/principles/adapters/__tests__/cpp-scanner-equivalence.test.ts +67 -0
  26. package/principles/adapters/__tests__/typescript-block-comment-lines.test.ts +53 -0
  27. package/principles/adapters/__tests__/typescript-masking.test.ts +96 -0
  28. package/principles/adapters/cpp.ts +102 -50
  29. package/principles/adapters/typescript.ts +503 -174
  30. package/principles/baseline.ts +58 -70
  31. package/skills/delphi-review/AGENTS.md +2 -2
  32. package/skills/sprint-flow/AGENTS.md +2 -2
  33. package/skills/test-specification-alignment/AGENTS.md +2 -2
package/hooks/pre-commit CHANGED
@@ -3008,19 +3008,53 @@ else
3008
3008
  # JaCoCo writes target/site/jacoco/ (Maven) or build/reports/jacoco/test/
3009
3009
  # (Gradle), i.e. neither path probed by the default branch below, so Java
3010
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.
3011
3022
  jacoco_csv=""
3023
+ jacoco_all=""
3012
3024
  for jacoco_candidate in \
3013
3025
  "target/site/jacoco/jacoco.csv" \
3014
3026
  "build/reports/jacoco/test/jacocoTestReport.csv"; do
3015
3027
  [ -f "$jacoco_candidate" ] && { jacoco_csv="$jacoco_candidate"; break; }
3016
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
3017
3043
 
3018
3044
  if [ -n "$jacoco_csv" ]; then
3019
3045
  # Aggregate INSTRUCTION_MISSED / INSTRUCTION_COVERED over every class row.
3020
3046
  # Columns are located by header name because their position is not part of
3021
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"
3022
3055
  COVERAGE_PERCENT=$(awk -F, '
3023
- NR == 1 {
3056
+ FNR == 1 {
3057
+ missed_col = 0; covered_col = 0;
3024
3058
  for (i = 1; i <= NF; i++) {
3025
3059
  if ($i == "INSTRUCTION_MISSED") missed_col = i;
3026
3060
  if ($i == "INSTRUCTION_COVERED") covered_col = i;
@@ -3036,7 +3070,7 @@ else
3036
3070
  if (total > 0) printf "%.2f", (covered / total) * 100;
3037
3071
  else print "";
3038
3072
  }
3039
- ' "$jacoco_csv" 2>/dev/null) || COVERAGE_PERCENT=""
3073
+ ' $_jc_inputs 2>/dev/null) || COVERAGE_PERCENT=""
3040
3074
 
3041
3075
  if [ -n "$COVERAGE_PERCENT" ]; then
3042
3076
  # Compare on the unrounded value so 79.5% cannot pass as 80%.
@@ -3484,18 +3518,77 @@ else
3484
3518
  fi
3485
3519
  ;;
3486
3520
  "java")
3487
- if [ -f "src/test/java/architecture" ] || [ -d "src/test/java/architecture" ]; then
3488
- echo " Running Java architecture tests..."
3489
- if [ -f "pom.xml" ]; then
3490
- mvn test -Dtest=architecture.* 2>&1 | head -20 || mvn test -Dtest=**Architecture** 2>&1 | head -20 || echo "⚠️ Java architecture tests completed"
3491
- elif [ -f "build.gradle" ]; then
3492
- if [ -f "./gradlew" ]; then
3493
- ./gradlew test --tests "*Architecture*" 2>&1 | head -20 || echo "⚠️ Java architecture tests completed"
3494
- 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
3495
3588
  fi
3496
3589
  echo " ✅ Java architecture validation completed."
3497
3590
  else
3498
- echo " ℹ️ No architecture test files found - skipping"
3591
+ echo " ℹ️ No *Architecture* test sources found under */src/test/java - skipping"
3499
3592
  echo " ⏭️ SKIPPED - Architecture validation (no tests)"
3500
3593
  # #537: no test files means Java architecture was NOT measured.
3501
3594
  if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
@@ -3547,6 +3640,22 @@ else
3547
3640
  else
3548
3641
  GATE_6_REASON="$_g6_seen"
3549
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
3550
3659
  else
3551
3660
  # Architecture config is REQUIRED — BLOCK if missing
3552
3661
  if [ -f "$PROJECT_ROOT/.architecture-skip" ]; then
@@ -2,6 +2,6 @@
2
2
  "verdict": "PASS",
3
3
  "license_type": "MIT",
4
4
  "sha256": "0e7ac423bf2c6e223b7c5b156f8cf72da49d748e56a1641402c31f22ad07dbb5",
5
- "checked_at": "2026-10-26T05:44:50.695Z",
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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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.3",
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.3",
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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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.3",
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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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.3",
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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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.3",
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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.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,9 +1,9 @@
1
1
  # PRINCIPLES CHECKER MODULE
2
2
 
3
3
  **Generated:** 2026-10-26
4
- **Commit:** 7a6d9971
4
+ **Commit:** dfe3bf53
5
5
  **Branch:** main
6
- **Version:** 0.31.3.0
6
+ **Version:** 0.31.4.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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @test REQ-620
3
+ * @intent Pin the single behavioural difference introduced when the two copies
4
+ * of the per-tool accumulator were merged into buildEntry:
5
+ * `createFromFiles` tested `totalWarnings` for TRUTHINESS, so a caller
6
+ * passing an explicit `0` had it replaced by the sum of the per-tool
7
+ * counts. That is exactly the case where the caller is overriding a
8
+ * stale per-tool number, so the override was silently discarded.
9
+ *
10
+ * `initializeWithAnalyzer` already used `!== undefined`, so the two
11
+ * entry points disagreed about the same input. The merged version uses
12
+ * the `!== undefined` reading for both.
13
+ * @covers AC-620-04
14
+ */
15
+ import { describe, it, expect } from 'vitest';
16
+ import { BaselineStorage } from '../baseline';
17
+
18
+ function storage(): InstanceType<typeof BaselineStorage> {
19
+ // Explicit config: a timeout far above any test's runtime.
20
+ return new BaselineStorage({ maxSize: 100, timeoutMs: 10000, batchSize: 5 });
21
+ }
22
+
23
+ describe('AC-620-04: an explicit totalWarnings wins, including zero', () => {
24
+ it('uses an explicit zero instead of the summed per-tool count', async () => {
25
+ const baseline = await storage().createFromFiles([
26
+ { file: 'a.ts', counts: { eslint: { warnings: 3, errors: 0 }, totalWarnings: 0 } },
27
+ ]);
28
+ expect(baseline['a.ts'].totalWarnings).toBe(0);
29
+ });
30
+
31
+ it('uses an explicit nonzero value instead of the summed count', async () => {
32
+ const baseline = await storage().createFromFiles([
33
+ { file: 'a.ts', counts: { eslint: { warnings: 3, errors: 0 }, totalWarnings: 42 } },
34
+ ]);
35
+ expect(baseline['a.ts'].totalWarnings).toBe(42);
36
+ });
37
+
38
+ it('sums the per-tool counts when no explicit total is given', async () => {
39
+ const baseline = await storage().createFromFiles([
40
+ {
41
+ file: 'a.ts',
42
+ counts: {
43
+ eslint: { warnings: 3, errors: 0 },
44
+ ruff: { warnings: 2, errors: 1 },
45
+ },
46
+ },
47
+ ]);
48
+ expect(baseline['a.ts'].totalWarnings).toBe(5);
49
+ });
50
+
51
+ it('keeps every per-tool count it was given', async () => {
52
+ const baseline = await storage().createFromFiles([
53
+ {
54
+ file: 'a.ts',
55
+ counts: {
56
+ eslint: { warnings: 1, errors: 2 },
57
+ ccn: { warnings: 3, max: 9 },
58
+ shellcheck: { warnings: 4, errors: 5 },
59
+ },
60
+ },
61
+ ]);
62
+ const entry = baseline['a.ts'];
63
+ expect(entry.eslint).toEqual({ warnings: 1, errors: 2 });
64
+ expect(entry.ccn).toEqual({ warnings: 3, max: 9 });
65
+ expect(entry.shellcheck).toEqual({ warnings: 4, errors: 5 });
66
+ // Tools that were not supplied must not appear at all.
67
+ expect(entry.ruff).toBeUndefined();
68
+ expect(entry.golangci).toBeUndefined();
69
+ });
70
+
71
+ it('reports zero warnings for a file with no tool counts', async () => {
72
+ const baseline = await storage().createFromFiles([{ file: 'a.ts', counts: {} }]);
73
+ expect(baseline['a.ts'].totalWarnings).toBe(0);
74
+ expect(baseline['a.ts'].lastAnalyzed).toBeTypeOf('string');
75
+ });
76
+ });
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @test REQ-620
3
+ * @intent Pin the C/C++ code-block scanner's treatment of comments and string
4
+ * literals before its CCN-28 body is split.
5
+ *
6
+ * Every case is built to DEFEAT the fallback path. `extractCodeBlock`
7
+ * falls back to `substring(startPos)` capped at 200 characters, so a
8
+ * short fixture returns "the whole thing" even when the scanner never
9
+ * found a closing brace -- the assertions then pass on a broken scanner.
10
+ * Each source below therefore pads past 200 characters and asserts the
11
+ * exact end position, so a truncated scan cannot look correct.
12
+ *
13
+ * Verified by injecting three real defects (brace tracker seeing into
14
+ * strings, block comments untracked, backslash parity ignored): each one
15
+ * reddens at least one case here.
16
+ * @covers AC-620-03
17
+ */
18
+ import { describe, it, expect, vi } from 'vitest';
19
+ import { CppAdapter } from '../cpp';
20
+
21
+ vi.mock('fs', () => ({
22
+ readFileSync: vi.fn(),
23
+ existsSync: vi.fn(() => true),
24
+ }));
25
+
26
+ import { readFileSync } from 'fs';
27
+
28
+ const MARKER = 'END_OF_FUNCTION';
29
+ /** Padding that keeps the source well past the 200-char fallback cap. */
30
+ const PAD = ' '.repeat(240);
31
+
32
+ function block(src: string, start = 0): string {
33
+ (readFileSync as unknown as { mockReturnValue: (v: string) => void }).mockReturnValue(src);
34
+ const adapter = new CppAdapter('probe.cpp');
35
+ return adapter.extractCodeBlock(start);
36
+ }
37
+
38
+ /**
39
+ * Build `void f() { <body> }` followed by `<MARKER>`, padded past the fallback
40
+ * cap. The padding sits INSIDE the function body so the fallback (which runs to
41
+ * the 200-char cap) would include the marker, while a correct scan stops at the
42
+ * real closing brace and does not.
43
+ */
44
+ function wrap(body: string): { src: string; open: number } {
45
+ const src = `void f() {\n${body}\n${PAD}\n}${MARKER}`;
46
+ return { src, open: src.indexOf('{') };
47
+ }
48
+
49
+ describe('AC-620-03: the C/C++ block scanner ignores braces in comments and strings', () => {
50
+ it('returns exactly the balanced body of a plain function', () => {
51
+ const { src, open } = wrap(' int a = 1;');
52
+ const out = block(src, open);
53
+ expect(out, `tail=${JSON.stringify(out.slice(-60))} len=${out.length}`).toMatch(/}$/);
54
+ });
55
+
56
+ it('does not close on a brace inside a line comment', () => {
57
+ const { src, open } = wrap(' // } not the end\n int a = 1;');
58
+ const out = block(src, open);
59
+ expect(out, `extracted: ${JSON.stringify(out.slice(0, 80))}`).toContain('int a = 1;');
60
+ expect(out).not.toContain(MARKER);
61
+ });
62
+
63
+ it('does not close on a brace inside a block comment', () => {
64
+ const { src, open } = wrap(' /* } not the end */\n int a = 1;');
65
+ const out = block(src, open);
66
+ expect(out).toContain('int a = 1;');
67
+ expect(out).not.toContain(MARKER);
68
+ });
69
+
70
+ it('does not close on a brace inside a string literal', () => {
71
+ const { src, open } = wrap(' const char* s = "}";\n int a = 1;');
72
+ const out = block(src, open);
73
+ expect(out).toContain('int a = 1;');
74
+ expect(out).not.toContain(MARKER);
75
+ });
76
+
77
+ it('keeps scanning past a string that CONTAINS the only early close brace', () => {
78
+ // The string's `}` is the first `}` after the opener. A scanner that counts
79
+ // it ends the block immediately and never reaches the statements below.
80
+ const { src, open } = wrap(' const char* s = "}";\n int markerStatement = 1;\n int tail = 2;');
81
+ const out = block(src, open);
82
+ expect(out, `extracted: ${JSON.stringify(out.slice(0, 120))}`)
83
+ .toContain('int tail = 2;');
84
+ });
85
+
86
+ it('keeps scanning past a char literal that contains a close brace', () => {
87
+ const { src, open } = wrap(" char c = '}';\n int tail = 2;");
88
+ const out = block(src, open);
89
+ expect(out).toContain('int tail = 2;');
90
+ });
91
+
92
+ it('does not close on a brace inside a char literal', () => {
93
+ const { src, open } = wrap(" char c = '}';\n int a = 1;");
94
+ const out = block(src, open);
95
+ expect(out).toContain('int a = 1;');
96
+ expect(out).not.toContain(MARKER);
97
+ });
98
+
99
+ it('treats an escaped quote inside a string as data, not a terminator', () => {
100
+ // `\"` does not close the string, so the `}` after it is still string data.
101
+ const { src, open } = wrap(' const char* s = "a\\"}b";\n int a = 1;');
102
+ const out = block(src, open);
103
+ expect(out).toContain('int a = 1;');
104
+ expect(out).not.toContain(MARKER);
105
+ });
106
+
107
+ it('treats an even run of backslashes as NOT escaping the quote', () => {
108
+ // `\\` is a literal backslash, so the following `"` DOES close the string,
109
+ // and the `}` that ends the line is real structure.
110
+ const { src, open } = wrap(' const char* s = "a\\\\";\n int a = 1;');
111
+ const out = block(src, open);
112
+ expect(out.trimEnd().endsWith('}')).toBe(true);
113
+ expect(out).toContain('int a = 1;');
114
+ expect(out).not.toContain(MARKER);
115
+ });
116
+
117
+ it('falls back when no balanced block is ever found', () => {
118
+ const src = 'void f() {\n int a = 1;' + PAD;
119
+ const out = block(src, 9);
120
+ expect(out.length).toBe(200);
121
+ expect(out.length).toBeLessThan(src.length);
122
+ });
123
+ });