@boyingliu01/xp-gate 0.20.0 → 0.20.1

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 (34) hide show
  1. package/adapters/gate-8.sh +115 -73
  2. package/adapters/powershell.sh +60 -5
  3. package/adapters/python.sh +38 -4
  4. package/build-integrity/__tests__/import-resolver.test.ts +59 -0
  5. package/build-integrity/gate-10.ts +17 -2
  6. package/gate-8.sh +115 -73
  7. package/hooks/gate-8.sh +115 -73
  8. package/hooks/pre-commit +130 -27
  9. package/lib/mattpocock-license-verdict.json +1 -1
  10. package/mock-policy/AGENTS.md +4 -4
  11. package/mutation/AGENTS.md +4 -4
  12. package/package.json +2 -2
  13. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  14. package/plugins/claude-code/skills/delphi-review/AGENTS.md +4 -4
  15. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
  16. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
  17. package/plugins/dsh/package.json +1 -1
  18. package/plugins/dsh/skills/delphi-review/AGENTS.md +4 -4
  19. package/plugins/dsh/skills/sprint-flow/AGENTS.md +4 -4
  20. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +4 -4
  21. package/plugins/opencode/package.json +1 -1
  22. package/plugins/opencode/skills/delphi-review/AGENTS.md +4 -4
  23. package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
  24. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
  25. package/plugins/qoder/plugin.json +1 -1
  26. package/plugins/qoder/skills/delphi-review/AGENTS.md +4 -4
  27. package/plugins/qoder/skills/sprint-flow/AGENTS.md +4 -4
  28. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
  29. package/principles/AGENTS.md +4 -4
  30. package/principles/boy-scout.ts +36 -6
  31. package/scripts/sync-package-content.js +13 -6
  32. package/skills/delphi-review/AGENTS.md +4 -4
  33. package/skills/sprint-flow/AGENTS.md +4 -4
  34. package/skills/test-specification-alignment/AGENTS.md +4 -4
package/hooks/pre-commit CHANGED
@@ -459,10 +459,25 @@ record_gate_audit "gate-0" "version-consistency" "$GATE_0_STATUS" "0" "$GATE_0_S
459
459
 
460
460
  echo ""
461
461
 
462
- # Get list of changed files
463
- CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
464
-
465
- if [ -z "$CHANGED_FILES" ]; then
462
+ # Get list of changed files.
463
+ #
464
+ # Byte-identical mirrors (src/npm-package/**) are governed by CI Mirror Parity,
465
+ # not by re-linting. Leaving them in this scan set double-counted every
466
+ # lint/complexity signal and made jscpd report the source-vs-mirror pair as a
467
+ # 100% whole-file clone, which then tripped Gate 6's "<=5 warnings must clear
468
+ # to zero" rule on ANY edit to a mirrored file -- a warning that is
469
+ # structurally impossible to clear. (Gate 6's MIRROR_EXCLUDE and #494's test
470
+ # selection already exclude mirrors for the same reason.)
471
+ #
472
+ # IMPORTANT: the exclusion applies to the LINT SCAN CORPUS only. The early
473
+ # exit below must key on the RAW staged list -- Gate 8 (secret scanning) is
474
+ # unconditional and fail-closed (#499), and gating it behind a path-filtered
475
+ # list would let a mirror-only commit land with zero secret scanning and no
476
+ # CI backstop.
477
+ RAW_CHANGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
478
+ CHANGED_FILES=$(printf '%s\n' "$RAW_CHANGED_FILES" | grep -v '^src/npm-package/' || true)
479
+
480
+ if [ -z "$RAW_CHANGED_FILES" ]; then
466
481
  echo "No files changed. Skipping gates."
467
482
  exit 0
468
483
  fi
@@ -857,11 +872,21 @@ has_project_lang() {
857
872
  }
858
873
  # Switch to subdirectory if detected (for single-language subdirectory projects).
859
874
  # Multi-language projects stay at root — each adapter handles its own path.
875
+ #
876
+ # IN_PROJECT_SUBDIR records whether this cd actually happened. Git reports
877
+ # staged paths relative to the repo root; after cd "$PROJECT_SUBDIR" the [ -f ]
878
+ # probes below need subdirectory-relative paths (hence the PROJECT_SUBDIR
879
+ # prefix strips in Gates 5a/5b/5c), but when the hook stays at the root
880
+ # (multi-language projects) stripping the prefix makes every path-based check
881
+ # resolve against the wrong directory — false BLOCKs in Gate 5a, silent no-ops
882
+ # in 5b/5c. Only strip when we really are inside the subdirectory.
860
883
  ORIGINAL_DIR=""
884
+ IN_PROJECT_SUBDIR=0
861
885
  _lang_count=$(echo "$PROJECT_LANGS" | wc -w)
862
886
  if [ -n "$PROJECT_SUBDIR" ] && [ "$PROJECT_SUBDIR" != "." ] && [ "$PROJECT_LANG" != "documentation-only" ] && [ "$_lang_count" -le 1 ]; then
863
887
  ORIGINAL_DIR=$(pwd)
864
888
  cd "$PROJECT_SUBDIR"
889
+ IN_PROJECT_SUBDIR=1
865
890
  echo "Working in: $PROJECT_SUBDIR/"
866
891
  echo ""
867
892
  fi
@@ -891,20 +916,67 @@ else
891
916
  # in githooks/lib/typecheck.sh so a project's own checker (vue-tsc, svelte-check,
892
917
  # a `typecheck` script) is used instead of a hardcoded tsc that cannot resolve
893
918
  # single-file components (#436).
894
- source "${AUDIT_SCRIPT_DIR}/lib/typecheck.sh"
895
- run_typescript_typecheck
896
- TS_CHECK_EXIT=$?
919
+ #
920
+ # Resolve the TS project directory first (#470). The probes below used to run
921
+ # against the repo root only; in multi-language monorepos (e.g. web/ +
922
+ # backend/, where the hook stays at the root — see the cd block above)
923
+ # tsconfig.json / biome.json live in the language subdirectory, so the whole
924
+ # TS half of Gate 1 silently SKIPped ("tsconfig.json not found") while a
925
+ # project config existed. Order: repo root first (unchanged behaviour for
926
+ # plain projects), then the detected language subdirectory, then a shallow
927
+ # search.
928
+ TS_PROJECT_DIR=""
929
+ for _ts_dir in "." "${PROJECT_SUBDIR:-}"; do
930
+ [ -z "$_ts_dir" ] && continue
931
+ if [ -f "$_ts_dir/tsconfig.json" ] || [ -f "$_ts_dir/biome.json" ] || [ -f "$_ts_dir/biome.jsonc" ] \
932
+ || [ -f "$_ts_dir/eslint.config.js" ] || [ -f "$_ts_dir/.eslintrc.json" ] \
933
+ || [ -f "$_ts_dir/.eslintrc.js" ] || [ -f "$_ts_dir/.eslintrc.cjs" ]; then
934
+ TS_PROJECT_DIR="$_ts_dir"
935
+ break
936
+ fi
937
+ done
938
+ if [ -z "$TS_PROJECT_DIR" ]; then
939
+ _TSCONFIG_CAND=$(find . -maxdepth 2 \( -name tsconfig.json -o -name biome.json -o -name biome.jsonc -o -name eslint.config.js -o -name .eslintrc.json -o -name .eslintrc.js -o -name .eslintrc.cjs \) -not -path './.git/*' -not -path './node_modules/*' 2>/dev/null | head -1)
940
+ if [ -n "$_TSCONFIG_CAND" ]; then
941
+ TS_PROJECT_DIR=$(dirname "$_TSCONFIG_CAND")
942
+ fi
943
+ fi
944
+ [ -z "$TS_PROJECT_DIR" ] && TS_PROJECT_DIR="."
945
+ if [ "$TS_PROJECT_DIR" != "." ]; then
946
+ echo " TypeScript project directory: $TS_PROJECT_DIR/"
947
+ # typecheck.sh is cwd-relative: run it from the TS project subdirectory in a
948
+ # subshell so the language loop's next iteration still starts at the root.
949
+ # Exit contract: 0 clean, 1 findings (block), 2 checker unavailable (skip).
950
+ # A cd/source infrastructure failure must NOT read as "type errors": map it
951
+ # to 2 (skip) so 1 stays reserved for real findings (#508 walkthrough).
952
+ ( cd "$TS_PROJECT_DIR" 2>/dev/null || exit 2 \
953
+ ; [ -r "${AUDIT_SCRIPT_DIR}/lib/typecheck.sh" ] || exit 2 \
954
+ ; source "${AUDIT_SCRIPT_DIR}/lib/typecheck.sh" && run_typescript_typecheck )
955
+ TS_CHECK_EXIT=$?
956
+ else
957
+ source "${AUDIT_SCRIPT_DIR}/lib/typecheck.sh"
958
+ run_typescript_typecheck
959
+ TS_CHECK_EXIT=$?
960
+ fi
897
961
  if [ "$TS_CHECK_EXIT" -eq 1 ]; then
898
962
  exit 1
899
963
  fi
900
964
 
901
- # Run ESLint linting if config exists
902
- if [ -f ".eslintrc.json" ] || [ -f ".eslintrc.js" ] || [ -f ".eslintrc.cjs" ] || [ -f "eslint.config.js" ]; then
965
+ # Run ESLint linting if config exists (root or TS project subdirectory)
966
+ if [ -f "$TS_PROJECT_DIR/.eslintrc.json" ] || [ -f "$TS_PROJECT_DIR/.eslintrc.js" ] || [ -f "$TS_PROJECT_DIR/.eslintrc.cjs" ] || [ -f "$TS_PROJECT_DIR/eslint.config.js" ]; then
903
967
  echo "Running ESLint linting..."
904
968
  # Filter to only TS/JS files - ESLint config ignores non-matching files
905
969
  ESLINT_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(ts|tsx|js|jsx)$' || true)
906
970
  if [ -n "$ESLINT_FILES" ]; then
907
- ESLINT_OUTPUT=$(npx eslint $ESLINT_FILES -f json --no-warn-ignored 2>&1)
971
+ # File paths from git are repo-root relative, so they are correct from
972
+ # the root. Legacy .eslintrc.* configs are discovered by walking up from
973
+ # each linted file (found in the subdirectory automatically); flat
974
+ # configs are discovered from the CWD, so point at them explicitly.
975
+ if [ "$TS_PROJECT_DIR" != "." ] && [ -f "$TS_PROJECT_DIR/eslint.config.js" ]; then
976
+ ESLINT_OUTPUT=$(npx eslint --config "$TS_PROJECT_DIR/eslint.config.js" $ESLINT_FILES -f json --no-warn-ignored 2>&1)
977
+ else
978
+ ESLINT_OUTPUT=$(npx eslint $ESLINT_FILES -f json --no-warn-ignored 2>&1)
979
+ fi
908
980
  ESLINT_EXIT=$?
909
981
  if [ "$ESLINT_EXIT" -ne 0 ]; then
910
982
  if [ -f ".xp-gate/lint-baseline.json" ] && command -v node &> /dev/null; then
@@ -951,8 +1023,8 @@ else
951
1023
  fi
952
1024
  else
953
1025
  echo "ℹ️ No ESLint configuration found - Skipping"
954
- if [ -f "package.json" ] && command -v node &> /dev/null; then
955
- HAS_ESLINT=$(node -e "try{const p=JSON.parse(require('fs').readFileSync('package.json','utf8'));const d=p.devDependencies||{};const deps=p.dependencies||{};console.log((d.eslint||deps.eslint)?'yes':'no')}catch{console.log('no')}") && true
1026
+ if [ -f "$TS_PROJECT_DIR/package.json" ] && command -v node &> /dev/null; then
1027
+ HAS_ESLINT=$(node -e "try{const p=JSON.parse(require('fs').readFileSync('$TS_PROJECT_DIR/package.json','utf8'));const d=p.devDependencies||{};const deps=p.dependencies||{};console.log((d.eslint||deps.eslint)?'yes':'no')}catch{console.log('no')}") && true
956
1028
  if [ "$HAS_ESLINT" = "yes" ]; then
957
1029
  echo "⚠️ WARNING: eslint is installed in package.json but no eslint config found"
958
1030
  echo " Either create .eslintrc.* / eslint.config.* or remove eslint from dependencies"
@@ -962,9 +1034,17 @@ else
962
1034
  fi
963
1035
 
964
1036
  # ── Biome lint/format check ──
965
- if [ -f "biome.json" ] || [ -f "biome.jsonc" ]; then
1037
+ # Default the project dir so this branch stays runnable standalone
1038
+ # (the gate-1-biome-noop suite extracts it verbatim); a no-op inside the
1039
+ # hook, where TS_PROJECT_DIR is resolved above.
1040
+ TS_PROJECT_DIR="${TS_PROJECT_DIR:-.}"
1041
+ if [ -f "$TS_PROJECT_DIR/biome.json" ] || [ -f "$TS_PROJECT_DIR/biome.jsonc" ]; then
966
1042
  echo "Running Biome lint/format check..."
967
- BIOME_OUT=$(npx biome check --staged . 2>&1)
1043
+ # Run from the TS project directory so Biome auto-discovers its config and
1044
+ # scopes --staged to that subdirectory (mirrors the single-language subdir
1045
+ # mode, which executes after cd "$PROJECT_SUBDIR"). Empirically verified:
1046
+ # `biome check --staged .` from the subdir checks the staged files under it.
1047
+ BIOME_OUT=$(cd "$TS_PROJECT_DIR" && npx biome check --staged . 2>&1)
968
1048
  BIOME_STATUS=$?
969
1049
  if [ "$BIOME_STATUS" -ne 0 ]; then
970
1050
  # Biome exits 1 with `internalError/io - No files were processed` when the
@@ -989,8 +1069,8 @@ else
989
1069
  echo "✅ PASSED - Biome check."
990
1070
  fi
991
1071
  else
992
- if [ -f "package.json" ] && command -v node &> /dev/null; then
993
- HAS_BIOME=$(node -e "try{const p=JSON.parse(require('fs').readFileSync('package.json','utf8'));const d=p.devDependencies||{};const deps=p.dependencies||{};console.log((d['@biomejs/biome']||deps['@biomejs/biome'])?'yes':'no')}catch{console.log('no')}") && true
1072
+ if [ -f "$TS_PROJECT_DIR/package.json" ] && command -v node &> /dev/null; then
1073
+ HAS_BIOME=$(node -e "try{const p=JSON.parse(require('fs').readFileSync('$TS_PROJECT_DIR/package.json','utf8'));const d=p.devDependencies||{};const deps=p.dependencies||{};console.log((d['@biomejs/biome']||deps['@biomejs/biome'])?'yes':'no')}catch{console.log('no')}") && true
994
1074
  if [ "$HAS_BIOME" = "yes" ]; then
995
1075
  echo ""
996
1076
  echo "⚠️ WARNING: @biomejs/biome is installed in package.json but no biome.json found"
@@ -1480,11 +1560,15 @@ else
1480
1560
  # --- BLOCK path: New .ts/.tsx files without corresponding tests ---
1481
1561
  NEW_TS_FILES=$(git diff --cached --name-only --diff-filter=A | grep -E '\.(ts|tsx)$' | grep -v '\.d\.ts$' | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' | grep -v '__snapshots__' | grep -v 'node_modules/' | grep -v '\.next/' | grep -v '\.nuxt/' | grep -v 'dist/' | grep -v 'build/' | grep -v '.turbo/' | grep -v '.cache/' | grep -Ev '(^|/)vitest\.config\.' | grep -Ev '(^|/)vite\.config\.' | grep -Ev '(^|/)jest\.config\.' | grep -Ev '(^|/)tsconfig\.' | grep -Ev '(^|/)eslint\.' | grep -Ev '(^|/)prettier\.' | grep -Ev '(^|/)tailwind\.' || true)
1482
1562
 
1483
- # Strip PROJECT_SUBDIR prefix from new-TS-file paths when running in a subdirectory.
1484
- # git diff returns repo-root relative paths, but after cd "$PROJECT_SUBDIR"
1485
- # the test-pairing [ -f ] checks below need subdirectory-relative paths.
1563
+ # Strip PROJECT_SUBDIR prefix from new-TS-file paths ONLY when the hook
1564
+ # actually cd'd into the subdirectory (IN_PROJECT_SUBDIR=1; single-language
1565
+ # subdirectory projects). git diff returns repo-root relative paths; after
1566
+ # cd "$PROJECT_SUBDIR" the test-pairing [ -f ] checks below need
1567
+ # subdirectory-relative paths. When the hook stays at the repo root
1568
+ # (multi-language projects) the paths must KEEP their prefix or every
1569
+ # [ -f ] lookup misses and paired files are falsely BLOCKED.
1486
1570
  # (Mirrors the v0.14.31+ Gate 5b fix at lines 1603-1608; Gate 5a was missing it.)
1487
- if [ -n "$PROJECT_SUBDIR" ] && [ "$PROJECT_SUBDIR" != "." ] && [ -n "$NEW_TS_FILES" ]; then
1571
+ if [ "$IN_PROJECT_SUBDIR" = "1" ] && [ -n "$NEW_TS_FILES" ]; then
1488
1572
  NEW_TS_FILES=$(echo "$NEW_TS_FILES" | sed "s|^${PROJECT_SUBDIR}/||")
1489
1573
  fi
1490
1574
 
@@ -1695,10 +1779,13 @@ else
1695
1779
  # each plugin directory).
1696
1780
  CHANGED_TEST_FILES=$(echo "$CHANGED_TEST_FILES" | grep -vE '^plugins/[^/]+/' || true)
1697
1781
 
1698
- # Strip PROJECT_SUBDIR prefix from test file paths when running in subdirectory.
1699
- # git diff returns repo-root relative paths, but after cd "$PROJECT_SUBDIR" (line 728)
1700
- # vitest and [ -f ] checks need subdirectory-relative paths. (v0.14.31+ fix)
1701
- if [ -n "$PROJECT_SUBDIR" ] && [ "$PROJECT_SUBDIR" != "." ] && [ -n "$CHANGED_TEST_FILES" ]; then
1782
+ # Strip PROJECT_SUBDIR prefix from test file paths ONLY when the hook
1783
+ # actually cd'd into the subdirectory (IN_PROJECT_SUBDIR=1). git diff returns
1784
+ # repo-root relative paths; after cd "$PROJECT_SUBDIR" vitest and [ -f ]
1785
+ # checks need subdirectory-relative paths. At the repo root (multi-language
1786
+ # projects) stripping would silently skip the mock-density scan and feed
1787
+ # vitest paths that do not exist. (v0.14.31+ fix, gated on the cd in v0.19.x)
1788
+ if [ "$IN_PROJECT_SUBDIR" = "1" ] && [ -n "$CHANGED_TEST_FILES" ]; then
1702
1789
  CHANGED_TEST_FILES=$(echo "$CHANGED_TEST_FILES" | sed "s|^${PROJECT_SUBDIR}/||")
1703
1790
  fi
1704
1791
 
@@ -1884,14 +1971,20 @@ else
1884
1971
  CHANGED_SRC_FILES=$(echo "$CHANGED_SRC_FILES" | grep -vE '^plugins/[^/]+/' || true)
1885
1972
  # Mirror handling is identical for source files: their test-adjacent
1886
1973
  # paths must never reach vitest as root-project targets (#494).
1974
+ # Selection runs on repo-root relative paths (git diff output), before
1975
+ # any PROJECT_SUBDIR stripping, so the bundled-mirror prefixes match.
1887
1976
  SELECTED_SRC_FILES=$(select_root_suite_targets "$CHANGED_SRC_FILES")
1888
1977
  if [ "$SELECTED_SRC_FILES" != "$CHANGED_SRC_FILES" ]; then
1889
1978
  echo " ↪ Source selection adjusted for bundled mirrors (#494):"
1890
1979
  echo " was: $(echo "$CHANGED_SRC_FILES" | tr '\n' ' ')"
1891
1980
  echo " now: $(echo "$SELECTED_SRC_FILES" | tr '\n' ' ')"
1892
1981
  fi
1893
- # Strip PROJECT_SUBDIR prefix (same as CHANGED_TEST_FILES fix above)
1894
- if [ -n "$PROJECT_SUBDIR" ] && [ "$PROJECT_SUBDIR" != "." ] && [ -n "$SELECTED_SRC_FILES" ]; then
1982
+ # Strip PROJECT_SUBDIR prefix ONLY when the hook cd'd into the
1983
+ # subdirectory (IN_PROJECT_SUBDIR=1; same gate as the CHANGED_TEST_FILES
1984
+ # fix above — at the repo root the paths must stay root-relative or the
1985
+ # related-test lookup and vitest targets resolve against the wrong dir,
1986
+ # #470)
1987
+ if [ "$IN_PROJECT_SUBDIR" = "1" ] && [ -n "$SELECTED_SRC_FILES" ]; then
1895
1988
  SELECTED_SRC_FILES=$(echo "$SELECTED_SRC_FILES" | sed "s|^${PROJECT_SUBDIR}/||")
1896
1989
  fi
1897
1990
  CHANGED_SRC_COUNT=$(echo "$SELECTED_SRC_FILES" | grep -c '.' || true)
@@ -2648,7 +2741,17 @@ else
2648
2741
  echo "❌ BLOCKED - Boy Scout Rule violations detected ($VIOLATION_COUNT)"
2649
2742
  exit 1
2650
2743
  fi
2651
-
2744
+
2745
+ # REQ-4 (#452): the auto-init rewrites a committed file, which used to
2746
+ # surface as an unexplained dirty ` M .warnings-baseline.json` after
2747
+ # every commit that touched a first-time-modified file. Stage it so the
2748
+ # budget update travels with the commit that caused it; when the
2749
+ # baseline is unchanged (the common case) this is a no-op.
2750
+ if [ -n "$(git status --porcelain -- .warnings-baseline.json 2>/dev/null)" ]; then
2751
+ git add .warnings-baseline.json 2>/dev/null || true
2752
+ echo " ℹ️ .warnings-baseline.json auto-updated and staged with this commit."
2753
+ fi
2754
+
2652
2755
  echo " ✅ PASSED - Boy Scout Rule compliance."
2653
2756
  fi
2654
2757
  else
@@ -2,6 +2,6 @@
2
2
  "verdict": "PASS",
3
3
  "license_type": "MIT",
4
4
  "sha256": "0e7ac423bf2c6e223b7c5b156f8cf72da49d748e56a1641402c31f22ad07dbb5",
5
- "checked_at": "2026-10-08T07:48:14.654Z",
5
+ "checked_at": "2026-10-08T19:51:29.290Z",
6
6
  "source": "network"
7
7
  }
@@ -1,9 +1,9 @@
1
1
  # SRC/MOCK-POLICY KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Mock layering policy enforcement — Gate M3 of pre-push hook. Ensures integration tests use real implementations for internal dependencies, mock external dependencies, and annotate pending mocks with removal plans. Combines project scope scanning, mock decision engine, and per-file validation into a single pipeline.
@@ -1,9 +1,9 @@
1
1
  # SRC/MUTATION KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.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.20.0",
3
+ "version": "0.20.1",
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"
@@ -42,7 +42,7 @@
42
42
  "node": ">=18.0.0"
43
43
  },
44
44
  "scripts": {
45
- "version": "0.14.31",
45
+ "version": "0.20.0",
46
46
  "license-check": "node scripts/check-mattpocock-license.cjs",
47
47
  "prepack": "node scripts/check-mattpocock-license.cjs && node scripts/sync-package-content.js",
48
48
  "test": "echo \"Run tests\" && exit 0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
6
6
  "author": {
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.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.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "XP-Gate deterministic quality gates + AI workflow skills as a DeepSeek Harness (DSH) native plugin: gate-check / gate-principles / gate-arch tools + 12 SKILL.md skills",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.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.20.0",
3
+ "version": "0.20.1",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "XP-Gate quality gates + AI workflow skills + Sprint Flow TUI sidebar for OpenCode",
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.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.20.0",
3
+ "version": "0.20.1",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
6
6
  "author": {
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.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
- **Generated:** 2026-10-07
4
- **Commit:** ff55f35b
5
- **Branch:** fix/batch-defects-20261007
6
- **Version:** 0.20.0.0
3
+ **Generated:** 2026-10-08
4
+ **Commit:** unknown
5
+ **Branch:** unknown
6
+ **Version:** 0.20.1.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.
@@ -785,12 +785,44 @@ function reportBaselineUpdate(
785
785
  console.log(` preserved:${preserved.length > 0 ? ` ${list(preserved)}` : ' (none)'}`);
786
786
  }
787
787
 
788
+ /**
789
+ * Record budget entries for files the gate meets for the first time, then save
790
+ * and report the write. Split out of `runEnforcement` to keep both under the
791
+ * long-function threshold; also carries the #452 REQ-2 visibility contract:
792
+ * the report distinguishes "created" from "updated" and names every entry the
793
+ * auto-init introduced, never just a count.
794
+ */
795
+ async function autoInitMissingEntries(
796
+ baseline: Record<string, BaselineEntry>,
797
+ missingBaselineEntries: string[],
798
+ currentWarnings: Record<string, number>,
799
+ baselinePath: string,
800
+ baselineExisted: boolean,
801
+ ): Promise<void> {
802
+ recordAutoInitializedEntries(baseline, missingBaselineEntries, currentWarnings);
803
+ await saveBaseline(baselinePath, baseline);
804
+ const action = baselineExisted ? 'updated' : 'created';
805
+ const noun = missingBaselineEntries.length === 1 ? 'entry' : 'entries';
806
+ console.log(`ℹ️ Baseline ${action}: ${baselinePath}`);
807
+ console.log(` auto-initialized ${noun}: ${missingBaselineEntries.join(', ')}`);
808
+ }
809
+
788
810
  async function runEnforcement(newFiles: string[], modifiedFiles: string[], baselinePath: string): Promise<EnforcementResult> {
789
811
  const allFiles = [...newFiles.filter(f => f.trim()), ...modifiedFiles.filter(f => f.trim())];
790
812
  const currentWarnings = await analyzeWarningsForFiles(allFiles);
791
-
813
+
814
+ // Distinguishing "created" from "updated" is part of the auto-init contract
815
+ // (#452 REQ-2): a silently rewritten baseline is how unrelated entries used
816
+ // to vanish without anyone noticing.
817
+ let baselineExisted = false;
818
+ try {
819
+ await fs.access(baselinePath);
820
+ baselineExisted = true;
821
+ } catch {
822
+ baselineExisted = false;
823
+ }
792
824
  const baseline = await loadBaseline(baselinePath);
793
-
825
+
794
826
  // Check for missing baseline entries for modified files
795
827
  const missingBaselineEntries: string[] = [];
796
828
  for (const file of modifiedFiles) {
@@ -798,13 +830,11 @@ async function runEnforcement(newFiles: string[], modifiedFiles: string[], basel
798
830
  missingBaselineEntries.push(file);
799
831
  }
800
832
  }
801
-
833
+
802
834
  // Files being modified for the first time get their current count recorded, so
803
835
  // the next modification is compared against a real budget instead of nothing.
804
836
  if (missingBaselineEntries.length > 0) {
805
- recordAutoInitializedEntries(baseline, missingBaselineEntries, currentWarnings);
806
- await saveBaseline(baselinePath, baseline);
807
- console.log(`ℹ️ Auto-initialized baseline for ${missingBaselineEntries.length} files`);
837
+ await autoInitMissingEntries(baseline, missingBaselineEntries, currentWarnings, baselinePath, baselineExisted);
808
838
  }
809
839
 
810
840
  const deltaResults: DeltaResult[] = [];
@@ -419,7 +419,7 @@ function checkAdapterDrift(srcRootPath, mirrorRootPath) {
419
419
  if (mismatched.length > 0) {
420
420
  console.error(`[drift-check] ERROR: ${mismatched.length} adapter file(s) have drifted (SHA-256 mismatch):`);
421
421
  for (const f of mismatched) console.error(` - ${f}`);
422
- console.error('[drift-check] Fix: Run `node src/npm-package/scripts/sync-package-content.js` (or `npm pack`) to resync.');
422
+ console.error('[drift-check] Fix: Run `node src/npm-package/scripts/sync-package-content.js --fix` to resync (plain invocation is a check-only gate for CI).');
423
423
  }
424
424
 
425
425
  if (srcOnly.length > 0 || mirrorOnly.length > 0 || mismatched.length > 0) {
@@ -449,11 +449,18 @@ function checkDocsDrift(preCommitPath, prePushPath, readmePath, agentsPath) {
449
449
  function main() {
450
450
  console.error(`[sync] repo root: ${REPO_ROOT}`);
451
451
  console.error(`[sync] package root: ${PKG_ROOT}`);
452
- if (checkDocsDrift() === false) {
453
- process.exit(1);
454
- }
455
- if (checkAdapterDrift() === false) {
456
- process.exit(1);
452
+ // Drift gates run BEFORE the sync on purpose: CI's Mirror Parity job relies
453
+ // on a drifted commit failing here rather than being silently healed by the
454
+ // copy loop. Local repair therefore needs the explicit `--fix` escape hatch
455
+ // (the pre-sync "Fix: Run …" advice used to deadlock against this gate).
456
+ const fixMode = process.argv.includes('--fix');
457
+ if (!fixMode) {
458
+ if (checkDocsDrift() === false) {
459
+ process.exit(1);
460
+ }
461
+ if (checkAdapterDrift() === false) {
462
+ process.exit(1);
463
+ }
457
464
  }
458
465
  const skills = syncSkills();
459
466
  const plugins = syncPlugins();