@getarbiter/cli 0.5.0

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 (989) hide show
  1. package/CHANGELOG.md +713 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +31 -0
  4. package/PRIVACY.md +111 -0
  5. package/README.md +237 -0
  6. package/THIRD_PARTY_LICENSES.md +472 -0
  7. package/dist/.src-manifest.json +1 -0
  8. package/dist/capabilities/host-probe.js +3 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +87 -0
  11. package/dist/commands/adopt-policy.js +3 -0
  12. package/dist/commands/configure-interactive.js +3 -0
  13. package/dist/commands/configure.js +5 -0
  14. package/dist/commands/cross-model-review.js +3 -0
  15. package/dist/commands/diff.js +20 -0
  16. package/dist/commands/doc-set.js +3 -0
  17. package/dist/commands/doctor/clean.js +2 -0
  18. package/dist/commands/doctor/fail-open-census.js +12 -0
  19. package/dist/commands/doctor/health.js +11 -0
  20. package/dist/commands/doctor/prove-gates.js +13 -0
  21. package/dist/commands/doctor/recover-lock.js +12 -0
  22. package/dist/commands/doctor/repair-state.js +6 -0
  23. package/dist/commands/doctor/tool-pin-extract.js +4 -0
  24. package/dist/commands/doctor/tool-pins.js +9 -0
  25. package/dist/commands/doctor-interactive.js +2 -0
  26. package/dist/commands/doctor.js +2 -0
  27. package/dist/commands/explain.js +19 -0
  28. package/dist/commands/gate-exec.js +4 -0
  29. package/dist/commands/gold-audit.js +14 -0
  30. package/dist/commands/graph.js +5 -0
  31. package/dist/commands/init/build-arbiter-config.js +1 -0
  32. package/dist/commands/init/generate.js +42 -0
  33. package/dist/commands/init/github-setup.js +5 -0
  34. package/dist/commands/init/maturity-gates.js +10 -0
  35. package/dist/commands/init/resolve-config.js +3 -0
  36. package/dist/commands/init/toolchain.js +10 -0
  37. package/dist/commands/init/types.js +1 -0
  38. package/dist/commands/init.js +17 -0
  39. package/dist/commands/method-interactive.js +4 -0
  40. package/dist/commands/method.js +11 -0
  41. package/dist/commands/obsidian.js +2 -0
  42. package/dist/commands/plugin.js +8 -0
  43. package/dist/commands/pr-merged.js +2 -0
  44. package/dist/commands/retire-policy.js +3 -0
  45. package/dist/commands/review-diff.js +3 -0
  46. package/dist/commands/settings.js +7 -0
  47. package/dist/commands/ship-config.js +2 -0
  48. package/dist/commands/ship-profile.js +2 -0
  49. package/dist/commands/ship-review.js +1 -0
  50. package/dist/commands/ship-tier.js +2 -0
  51. package/dist/commands/ship-train.js +2 -0
  52. package/dist/commands/task-mark.js +2 -0
  53. package/dist/commands/task-note.js +3 -0
  54. package/dist/commands/task-record-red.js +2 -0
  55. package/dist/commands/task-record-tech-debt.js +4 -0
  56. package/dist/commands/task-ship.js +3 -0
  57. package/dist/commands/task-state.js +6 -0
  58. package/dist/commands/task.js +59 -0
  59. package/dist/commands/update.js +60 -0
  60. package/dist/commands/upgrade-level-interactive.js +2 -0
  61. package/dist/commands/upgrade-level.js +7 -0
  62. package/dist/commands/verify-plan.js +8 -0
  63. package/dist/commands/verify-tdd.js +2 -0
  64. package/dist/commands/verify.js +7 -0
  65. package/dist/commands/wizard/coherence.js +1 -0
  66. package/dist/commands/worktree-prune.js +4 -0
  67. package/dist/commands/worktree.js +39 -0
  68. package/dist/compatibility/cross-language-matrix.json +540 -0
  69. package/dist/compatibility/index.d.ts +4 -0
  70. package/dist/compatibility/index.js +2 -0
  71. package/dist/compatibility/matcher.js +1 -0
  72. package/dist/compatibility/matrix.json +27 -0
  73. package/dist/compatibility/parsers.d.ts +38 -0
  74. package/dist/compatibility/parsers.js +1 -0
  75. package/dist/compatibility/probe.d.ts +41 -0
  76. package/dist/compatibility/probe.js +4 -0
  77. package/dist/compatibility/report.js +2 -0
  78. package/dist/compatibility/schema.d.ts +80 -0
  79. package/dist/compatibility/schema.js +1 -0
  80. package/dist/compatibility/skills-matrix.json +98 -0
  81. package/dist/compatibility/skills-validator.d.ts +30 -0
  82. package/dist/compatibility/skills-validator.js +4 -0
  83. package/dist/compatibility/tooling-proposals.json +5 -0
  84. package/dist/config/arbiter-ignore.js +3 -0
  85. package/dist/config/collaboration-mode-defaults.js +1 -0
  86. package/dist/config/diff.js +1 -0
  87. package/dist/config/env-overrides.js +3 -0
  88. package/dist/config/env-registry.js +2 -0
  89. package/dist/config/levels.js +1 -0
  90. package/dist/config/migrations/index.js +2 -0
  91. package/dist/config/migrations/v0-to-v1.js +2 -0
  92. package/dist/config/migrations/v1-to-v2.js +2 -0
  93. package/dist/config/migrations/v2-to-v3.js +2 -0
  94. package/dist/config/migrations/v3-to-v4.js +2 -0
  95. package/dist/config/override-resolver.js +2 -0
  96. package/dist/config/resolve-project-config.js +2 -0
  97. package/dist/config/resolve-project-name.js +2 -0
  98. package/dist/config/schema.d.ts +480 -0
  99. package/dist/config/schema.js +1 -0
  100. package/dist/config/test-pyramid-profiles.js +1 -0
  101. package/dist/config/thresholds.js +2 -0
  102. package/dist/conformance/dimensions.js +3 -0
  103. package/dist/conformance/engine.js +7 -0
  104. package/dist/conformance/gate-proofs.js +4 -0
  105. package/dist/conformance/shared.js +2 -0
  106. package/dist/detectors/axis.js +1 -0
  107. package/dist/detectors/build.js +2 -0
  108. package/dist/detectors/existing.js +2 -0
  109. package/dist/detectors/external-model.js +2 -0
  110. package/dist/detectors/framework.js +2 -0
  111. package/dist/detectors/git.js +11 -0
  112. package/dist/detectors/github.js +2 -0
  113. package/dist/detectors/lanes.js +5 -0
  114. package/dist/detectors/language-hooks.js +117 -0
  115. package/dist/detectors/language.js +2 -0
  116. package/dist/detectors/package-manager.js +2 -0
  117. package/dist/detectors/package.js +2 -0
  118. package/dist/detectors/scaffold-wiring.js +2 -0
  119. package/dist/detectors/workflow-collision.js +4 -0
  120. package/dist/evidence/gate-binding.js +4 -0
  121. package/dist/evidence/git-checks.js +3 -0
  122. package/dist/evidence/load.js +2 -0
  123. package/dist/evidence/provenance.js +2 -0
  124. package/dist/evidence/summary.js +2 -0
  125. package/dist/evidence/tdd-reexecute.js +2 -0
  126. package/dist/evidence/tdd.js +4 -0
  127. package/dist/experimental/index.js +2 -0
  128. package/dist/experimental/parse-argv.js +2 -0
  129. package/dist/experimental/registry.js +2 -0
  130. package/dist/generators/agents-claude.js +2 -0
  131. package/dist/generators/agents-md.js +2 -0
  132. package/dist/generators/anti-drift-validators.js +3 -0
  133. package/dist/generators/anti-proforma.js +2 -0
  134. package/dist/generators/api-e2e.js +2 -0
  135. package/dist/generators/api-middleware.js +2 -0
  136. package/dist/generators/archunit.js +2 -0
  137. package/dist/generators/audit-toolchain.js +2 -0
  138. package/dist/generators/auth.js +2 -0
  139. package/dist/generators/behavioral-tests.js +2 -0
  140. package/dist/generators/boundaries.js +2 -0
  141. package/dist/generators/check-all.js +2 -0
  142. package/dist/generators/check-stack-conformity.js +2 -0
  143. package/dist/generators/ci-five-lane.js +2 -0
  144. package/dist/generators/ci-tier.js +2 -0
  145. package/dist/generators/claude.js +3 -0
  146. package/dist/generators/codex-hooks.js +2 -0
  147. package/dist/generators/codex-known-limitations.js +2 -0
  148. package/dist/generators/codex.js +15 -0
  149. package/dist/generators/commit-footer.js +2 -0
  150. package/dist/generators/compliance-menu.js +2 -0
  151. package/dist/generators/compliance.js +2 -0
  152. package/dist/generators/conformance.js +2 -0
  153. package/dist/generators/contract-testing.js +2 -0
  154. package/dist/generators/coverage.js +2 -0
  155. package/dist/generators/debt-gates.js +24 -0
  156. package/dist/generators/debt-ratchet.js +2 -0
  157. package/dist/generators/derived-class.js +2 -0
  158. package/dist/generators/doc-set.js +4 -0
  159. package/dist/generators/docs.js +2 -0
  160. package/dist/generators/duplication.js +2 -0
  161. package/dist/generators/e2e-constitution.js +2 -0
  162. package/dist/generators/env-template.js +2 -0
  163. package/dist/generators/evidence-retention.js +2 -0
  164. package/dist/generators/feature-matrix.js +2 -0
  165. package/dist/generators/frontend-governance.js +2 -0
  166. package/dist/generators/frontend-quality.js +2 -0
  167. package/dist/generators/gap.js +2 -0
  168. package/dist/generators/gdpr.js +2 -0
  169. package/dist/generators/githooks.js +2 -0
  170. package/dist/generators/github-setup.js +2 -0
  171. package/dist/generators/github.js +2 -0
  172. package/dist/generators/gitignore.js +2 -0
  173. package/dist/generators/global-invariants.js +3 -0
  174. package/dist/generators/go-boundaries.js +2 -0
  175. package/dist/generators/gold-kit.js +2 -0
  176. package/dist/generators/infra.js +2 -0
  177. package/dist/generators/integration-testing.js +7 -0
  178. package/dist/generators/iso27001.js +2 -0
  179. package/dist/generators/iso9001.js +2 -0
  180. package/dist/generators/labels.js +2 -0
  181. package/dist/generators/local-wrapper.js +2 -0
  182. package/dist/generators/modulith.js +2 -0
  183. package/dist/generators/mutation.js +2 -0
  184. package/dist/generators/observability.js +2 -0
  185. package/dist/generators/operations.js +2 -0
  186. package/dist/generators/perf-k6.js +2 -0
  187. package/dist/generators/pharma.js +2 -0
  188. package/dist/generators/playwright-python.js +2 -0
  189. package/dist/generators/playwright-ts.js +2 -0
  190. package/dist/generators/pr-tooling.js +2 -0
  191. package/dist/generators/python-boundaries.js +2 -0
  192. package/dist/generators/quality.js +2 -0
  193. package/dist/generators/registry.js +2 -0
  194. package/dist/generators/regulated.js +2 -0
  195. package/dist/generators/resilience.js +2 -0
  196. package/dist/generators/risk-register.js +2 -0
  197. package/dist/generators/root.js +2 -0
  198. package/dist/generators/rust-boundaries.js +2 -0
  199. package/dist/generators/safety-class.js +2 -0
  200. package/dist/generators/security.js +2 -0
  201. package/dist/generators/seed.js +2 -0
  202. package/dist/generators/self-validation.js +2 -0
  203. package/dist/generators/ship-driver.js +2 -0
  204. package/dist/generators/skill-names.json +17 -0
  205. package/dist/generators/skills.js +2 -0
  206. package/dist/generators/smoke-journeys.js +2 -0
  207. package/dist/generators/solo-exception.js +2 -0
  208. package/dist/generators/ssot.js +2 -0
  209. package/dist/generators/stride-enforcement.js +2 -0
  210. package/dist/generators/suppressions.js +2 -0
  211. package/dist/generators/test-pyramid-manifest.js +3 -0
  212. package/dist/generators/test-taxonomy.js +2 -0
  213. package/dist/generators/wiki.js +2 -0
  214. package/dist/github/branch-protection.js +2 -0
  215. package/dist/github/classify-gh-error.js +2 -0
  216. package/dist/github/labels.js +2 -0
  217. package/dist/github/project-board.js +2 -0
  218. package/dist/graph/builders/adr.js +4 -0
  219. package/dist/graph/builders/ast.js +3 -0
  220. package/dist/graph/builders/canon.js +2 -0
  221. package/dist/graph/builders/evidence.js +2 -0
  222. package/dist/graph/builders/inv.js +1 -0
  223. package/dist/graph/builders/req.js +3 -0
  224. package/dist/graph/builders/test-nodes.js +3 -0
  225. package/dist/graph/builders/utils.js +2 -0
  226. package/dist/graph/load.js +2 -0
  227. package/dist/graph/model.js +2 -0
  228. package/dist/graph/query.js +3 -0
  229. package/dist/graph/store.js +1 -0
  230. package/dist/i18n/en.json +293 -0
  231. package/dist/i18n/index.js +4 -0
  232. package/dist/i18n/types.js +2 -0
  233. package/dist/integrations/companions.js +2 -0
  234. package/dist/integrations/external-review.js +10 -0
  235. package/dist/integrations/plugin-schema.js +2 -0
  236. package/dist/integrations/skill-detector.js +7 -0
  237. package/dist/integrations/skills-matrix.js +2 -0
  238. package/dist/integrations/types.d.ts +23 -0
  239. package/dist/integrations/types.js +2 -0
  240. package/dist/internal/cli-deprecation-registry.js +2 -0
  241. package/dist/internal/deprecate.js +3 -0
  242. package/dist/internal/experimental-warn.js +3 -0
  243. package/dist/invariants/catalog.d.ts +2 -0
  244. package/dist/invariants/catalog.js +1 -0
  245. package/dist/invariants/filter.d.ts +28 -0
  246. package/dist/invariants/filter.js +1 -0
  247. package/dist/invariants/index.d.ts +4 -0
  248. package/dist/invariants/index.js +2 -0
  249. package/dist/invariants/tiers.js +1 -0
  250. package/dist/invariants/types.d.ts +1 -0
  251. package/dist/invariants/types.js +1 -0
  252. package/dist/kit/applicability.js +3 -0
  253. package/dist/kit/audit-report.js +3 -0
  254. package/dist/kit/brownfield-detect.js +4 -0
  255. package/dist/kit/canonical-mapping.json +1 -0
  256. package/dist/kit/catalog.js +2 -0
  257. package/dist/kit/catalog.json +1 -0
  258. package/dist/kit/derived.json +1 -0
  259. package/dist/kit/emit-issues.js +8 -0
  260. package/dist/kit/measure.js +5 -0
  261. package/dist/kit/redaction.js +3 -0
  262. package/dist/kit/schema.js +2 -0
  263. package/dist/kit/taxonomy.js +2 -0
  264. package/dist/kit/thresholds.d.ts +12 -0
  265. package/dist/kit/thresholds.js +2 -0
  266. package/dist/kit/wave-engine.js +1 -0
  267. package/dist/recipes/loader.js +6 -0
  268. package/dist/recipes/schema.js +2 -0
  269. package/dist/risk/classifier.js +2 -0
  270. package/dist/risk/sha-check.js +2 -0
  271. package/dist/state/backups.js +2 -0
  272. package/dist/state/envelope.js +2 -0
  273. package/dist/state/generated-manifest.js +3 -0
  274. package/dist/state/manifest-key.js +2 -0
  275. package/dist/state/migrations/index.js +1 -0
  276. package/dist/state/migrations/v0-to-v1.js +2 -0
  277. package/dist/state/retired-renders.js +2 -0
  278. package/dist/templates/.nvmrc.ejs +1 -0
  279. package/dist/templates/HANDOFF.template.md +45 -0
  280. package/dist/templates/__tests__/contract/error-shape.contract.test.ts.ejs +47 -0
  281. package/dist/templates/agents-md/AGENTS.md.ejs +517 -0
  282. package/dist/templates/api-e2e/README.md.ejs +33 -0
  283. package/dist/templates/api-e2e/go-httptest.go.ejs +43 -0
  284. package/dist/templates/api-e2e/java-restassured.java.ejs +28 -0
  285. package/dist/templates/api-e2e/kotlin-restassured.kt.ejs +30 -0
  286. package/dist/templates/api-e2e/manifest.json.ejs +8 -0
  287. package/dist/templates/api-e2e/postman.collection.json.ejs +30 -0
  288. package/dist/templates/api-e2e/python-httpx.py.ejs +17 -0
  289. package/dist/templates/api-e2e/run.sh.ejs +61 -0
  290. package/dist/templates/api-e2e/ts-supertest.test.ts.ejs +18 -0
  291. package/dist/templates/archunit/AntiCyclicTest.java.ejs +29 -0
  292. package/dist/templates/archunit/AntiProformaExempt.java.ejs +35 -0
  293. package/dist/templates/archunit/AntiProformaTest.java.ejs +104 -0
  294. package/dist/templates/archunit/ArchitectureTest.java.ejs +87 -0
  295. package/dist/templates/archunit/DependencyFlowTest.java.ejs +55 -0
  296. package/dist/templates/archunit/DomainPurityTest.java.ejs +47 -0
  297. package/dist/templates/archunit/NamingConventionsTest.java.ejs +54 -0
  298. package/dist/templates/archunit/NoH2ArchTest.java.ejs +30 -0
  299. package/dist/templates/archunit/NoMockMvcTest.java.ejs +71 -0
  300. package/dist/templates/archunit/PortsIndependenceTest.java.ejs +55 -0
  301. package/dist/templates/archunit/RestAssuredArchTest.java.ejs +32 -0
  302. package/dist/templates/archunit/RestAssuredBaseIT.java.ejs +66 -0
  303. package/dist/templates/archunit/TestCoverageArchTest.java.ejs +29 -0
  304. package/dist/templates/archunit/arch-test-deps-maven.md.ejs +42 -0
  305. package/dist/templates/archunit/arch-test-deps.gradle.ejs +14 -0
  306. package/dist/templates/audit/gdpr/check-gdpr-controls.mjs.ejs +102 -0
  307. package/dist/templates/audit/gdpr/controls-to-gates.md.ejs +42 -0
  308. package/dist/templates/audit/gdpr/data-flow-map.md.ejs +30 -0
  309. package/dist/templates/audit/gdpr/dpia.md.ejs +45 -0
  310. package/dist/templates/audit/generic/audit-gate-rules.md.ejs +24 -0
  311. package/dist/templates/audit/generic/audit-trail-policy.md.ejs +38 -0
  312. package/dist/templates/audit/iso27001/iso27001-controls-gate-map.md.ejs +60 -0
  313. package/dist/templates/auth/setup.md.ejs +661 -0
  314. package/dist/templates/behavioral-tests/ExampleBehavioralTest.java.ejs +67 -0
  315. package/dist/templates/behavioral-tests/TESTING_POLICY.md.ejs +116 -0
  316. package/dist/templates/behavioral-tests/bdd/ExampleBddIT.java.ejs +19 -0
  317. package/dist/templates/behavioral-tests/bdd/ExampleSteps.java.ejs +61 -0
  318. package/dist/templates/behavioral-tests/bdd/example.feature.ejs +14 -0
  319. package/dist/templates/behavioral-tests/bdd/example.steps.ts.ejs +37 -0
  320. package/dist/templates/behavioral-tests/bdd/example_bdd_test.rs.ejs +64 -0
  321. package/dist/templates/behavioral-tests/bdd/example_test.go.ejs +80 -0
  322. package/dist/templates/behavioral-tests/bdd/test_example_bdd.py.ejs +47 -0
  323. package/dist/templates/behavioral-tests/eslint-playwright.json.ejs +10 -0
  324. package/dist/templates/behavioral-tests/eslint.config.playwright.mjs.ejs +29 -0
  325. package/dist/templates/behavioral-tests/example.behavioral.test.ts.ejs +47 -0
  326. package/dist/templates/behavioral-tests/example_behavioral_test.go.ejs +51 -0
  327. package/dist/templates/behavioral-tests/example_behavioral_test.rs.ejs +47 -0
  328. package/dist/templates/behavioral-tests/test_example_behavioral.py.ejs +40 -0
  329. package/dist/templates/boundaries/.eslintrc-boundaries.cjs.ejs +81 -0
  330. package/dist/templates/boundaries/.eslintrc-frontend-spa.cjs.ejs +79 -0
  331. package/dist/templates/boundaries/.importlinter.ejs +48 -0
  332. package/dist/templates/boundaries/check-boundaries-go.mjs.ejs +94 -0
  333. package/dist/templates/boundaries/check-boundaries-python.mjs.ejs +118 -0
  334. package/dist/templates/boundaries/check-boundaries-rust.mjs.ejs +111 -0
  335. package/dist/templates/boundaries/check-boundaries.mjs.ejs +34 -0
  336. package/dist/templates/boundaries/clippy.toml.ejs +50 -0
  337. package/dist/templates/boundaries/deny.toml.ejs +56 -0
  338. package/dist/templates/boundaries/eslint.config.boundaries.mjs.ejs +112 -0
  339. package/dist/templates/boundaries/eslint.config.frontend-spa.mjs.ejs +93 -0
  340. package/dist/templates/boundaries/golangci-boundaries.yml.ejs +70 -0
  341. package/dist/templates/boundaries/ruff-boundaries.toml.ejs +32 -0
  342. package/dist/templates/claude/CLAUDE.md.ejs +78 -0
  343. package/dist/templates/claude/agents/agent-write-classes.json +30 -0
  344. package/dist/templates/claude/agents/bridge-reviewer.md.ejs +68 -0
  345. package/dist/templates/claude/agents/codebase-scanner.md.ejs +69 -0
  346. package/dist/templates/claude/agents/context-checker.md.ejs +73 -0
  347. package/dist/templates/claude/agents/red-team.md.ejs +134 -0
  348. package/dist/templates/claude/commands/close-gold-gap.md.ejs +43 -0
  349. package/dist/templates/claude/commands/drain.md.ejs +81 -0
  350. package/dist/templates/claude/commands/gold-audit.md.ejs +43 -0
  351. package/dist/templates/claude/commands/impact.md.ejs +39 -0
  352. package/dist/templates/claude/commands/levelup.md.ejs +61 -0
  353. package/dist/templates/claude/commands/ship.md.ejs +902 -0
  354. package/dist/templates/claude/commands/tabletop.md.ejs +46 -0
  355. package/dist/templates/claude/commands/task.md.ejs +87 -0
  356. package/dist/templates/claude/commands/wt-close.md.ejs +58 -0
  357. package/dist/templates/claude/commands/wt-list.md.ejs +48 -0
  358. package/dist/templates/claude/commands/wt-open.md.ejs +61 -0
  359. package/dist/templates/claude/commands/wt-prune.md.ejs +38 -0
  360. package/dist/templates/claude/hooks/check-circular-deps.mjs.ejs +56 -0
  361. package/dist/templates/claude/hooks/check-no-orphan-todo.mjs.ejs +45 -0
  362. package/dist/templates/claude/hooks/check-no-pii.mjs.ejs +121 -0
  363. package/dist/templates/claude/hooks/check-no-placeholders.mjs.ejs +71 -0
  364. package/dist/templates/claude/hooks/check-no-skipped-tests.mjs +80 -0
  365. package/dist/templates/claude/hooks/check-no-unused-exports.mjs +62 -0
  366. package/dist/templates/claude/hooks/closer-mode-guard.mjs.ejs +102 -0
  367. package/dist/templates/claude/hooks/debug-state-on-failure.mjs.ejs +98 -0
  368. package/dist/templates/claude/hooks/enforce-gate-before-pr.mjs +142 -0
  369. package/dist/templates/claude/hooks/enforce-read-only.mjs +28 -0
  370. package/dist/templates/claude/hooks/exitplanmode-banner.mjs.ejs +43 -0
  371. package/dist/templates/claude/hooks/guard-done-evidence.mjs.ejs +85 -0
  372. package/dist/templates/claude/hooks/guard-task-completion.mjs.ejs +56 -0
  373. package/dist/templates/claude/hooks/hooks.mjs.ejs +127 -0
  374. package/dist/templates/claude/hooks/lib.mjs.ejs +523 -0
  375. package/dist/templates/claude/hooks/post-brainstorm-stop.mjs +48 -0
  376. package/dist/templates/claude/hooks/post-commit-check.mjs.ejs +65 -0
  377. package/dist/templates/claude/hooks/post-commit-checklists/go/backend.ejs +4 -0
  378. package/dist/templates/claude/hooks/post-commit-checklists/go/docs.ejs +4 -0
  379. package/dist/templates/claude/hooks/post-commit-checklists/go/frontend.ejs +4 -0
  380. package/dist/templates/claude/hooks/post-commit-checklists/java/backend.ejs +4 -0
  381. package/dist/templates/claude/hooks/post-commit-checklists/java/docs.ejs +4 -0
  382. package/dist/templates/claude/hooks/post-commit-checklists/java/frontend.ejs +4 -0
  383. package/dist/templates/claude/hooks/post-commit-checklists/python/backend.ejs +4 -0
  384. package/dist/templates/claude/hooks/post-commit-checklists/python/docs.ejs +4 -0
  385. package/dist/templates/claude/hooks/post-commit-checklists/python/frontend.ejs +4 -0
  386. package/dist/templates/claude/hooks/post-commit-checklists/rust/backend.ejs +5 -0
  387. package/dist/templates/claude/hooks/post-commit-checklists/rust/docs.ejs +4 -0
  388. package/dist/templates/claude/hooks/post-commit-checklists/rust/frontend.ejs +4 -0
  389. package/dist/templates/claude/hooks/post-commit-checklists/ts/backend.ejs +4 -0
  390. package/dist/templates/claude/hooks/post-commit-checklists/ts/docs.ejs +4 -0
  391. package/dist/templates/claude/hooks/post-commit-checklists/ts/frontend.ejs +5 -0
  392. package/dist/templates/claude/hooks/post-edit-dispatch.mjs.ejs +73 -0
  393. package/dist/templates/claude/hooks/post-subagent-release.mjs +80 -0
  394. package/dist/templates/claude/hooks/pre-compact.mjs.ejs +79 -0
  395. package/dist/templates/claude/hooks/pre-edit-plan-anchor.mjs.ejs +225 -0
  396. package/dist/templates/claude/hooks/pre-edit-ssot-guard.mjs +204 -0
  397. package/dist/templates/claude/hooks/pre-spawn-worktree-guard.mjs +129 -0
  398. package/dist/templates/claude/hooks/skill-forced-eval.mjs.ejs +281 -0
  399. package/dist/templates/claude/hooks/stop-dangerous.mjs +86 -0
  400. package/dist/templates/claude/hooks/stop-evidence-guard.mjs.ejs +245 -0
  401. package/dist/templates/claude/hooks/stop-finding-loss.mjs.ejs +168 -0
  402. package/dist/templates/claude/hooks/wiki-on-commit.mjs.ejs +38 -0
  403. package/dist/templates/claude/knowledge-map.json.ejs +26 -0
  404. package/dist/templates/claude/rules/05-agent-lifecycle.md +7 -0
  405. package/dist/templates/claude/rules/25-todo-folder-policy.md +5 -0
  406. package/dist/templates/claude/rules/40-context-economy.md +32 -0
  407. package/dist/templates/claude/rules/45-mcp-fallback.md +25 -0
  408. package/dist/templates/claude/rules/50-batch-execution.md +75 -0
  409. package/dist/templates/claude/rules/55-brainstorm-terminal-state.md +44 -0
  410. package/dist/templates/claude/rules/60-incidental-capture.md +43 -0
  411. package/dist/templates/claude/rules/75-impact-vault-reading.md +43 -0
  412. package/dist/templates/claude/rules/90-exec-protocol.md.ejs +40 -0
  413. package/dist/templates/claude/rules/95-closer-mode.md +45 -0
  414. package/dist/templates/claude/settings.json.ejs +165 -0
  415. package/dist/templates/claude/skills/architect-review/SKILL.md.ejs +82 -0
  416. package/dist/templates/claude/skills/brainstorming/SKILL.md.ejs +86 -0
  417. package/dist/templates/claude/skills/clean-code/SKILL.md.ejs +39 -0
  418. package/dist/templates/claude/skills/close-gold-gap/SKILL.md.ejs +58 -0
  419. package/dist/templates/claude/skills/codebase-audit/SKILL.md.ejs +88 -0
  420. package/dist/templates/claude/skills/configure/SKILL.md.ejs +72 -0
  421. package/dist/templates/claude/skills/epic-decompose/SKILL.md.ejs +86 -0
  422. package/dist/templates/claude/skills/gold-audit/SKILL.md.ejs +91 -0
  423. package/dist/templates/claude/skills/impact/SKILL.md.ejs +104 -0
  424. package/dist/templates/claude/skills/levelup/SKILL.md.ejs +146 -0
  425. package/dist/templates/claude/skills/tabletop/SKILL.md.ejs +118 -0
  426. package/dist/templates/claude/skills/tdd/SKILL.md.ejs +157 -0
  427. package/dist/templates/claude/skills/understand-code/SKILL.md.ejs +93 -0
  428. package/dist/templates/claude/skills/verification/SKILL.md.ejs +68 -0
  429. package/dist/templates/claude/skills/wave-drain/SKILL.md.ejs +491 -0
  430. package/dist/templates/codex/CODEX.md.ejs +140 -0
  431. package/dist/templates/codex/codex-adapter.mjs +132 -0
  432. package/dist/templates/codex/config.toml.ejs +189 -0
  433. package/dist/templates/compliance/compliance-mapping.md.ejs +124 -0
  434. package/dist/templates/compliance/compliance-menu.md.ejs +89 -0
  435. package/dist/templates/contract-testing/CONTRACTS_POLICY.md.ejs +30 -0
  436. package/dist/templates/contract-testing/api-snapshots/config-keys-baseline.json.ejs +1 -0
  437. package/dist/templates/contract-testing/api-snapshots/config-response-baseline.json.ejs +1 -0
  438. package/dist/templates/contract-testing/api-snapshots/enum-values-baseline.json.ejs +1 -0
  439. package/dist/templates/contract-testing/api-snapshots/error-shape-baseline.json.ejs +6 -0
  440. package/dist/templates/contract-testing/api-snapshots/openapi-baseline.json.ejs +12 -0
  441. package/dist/templates/contract-testing/api-snapshots/openapi-content-types-baseline.json.ejs +3 -0
  442. package/dist/templates/contract-testing/api-snapshots/openapi-paths-baseline.json.ejs +1 -0
  443. package/dist/templates/contract-testing/api-snapshots/openapi-required-fields-baseline.json.ejs +1 -0
  444. package/dist/templates/contract-testing/api-snapshots/openapi-response-status-baseline.json.ejs +3 -0
  445. package/dist/templates/contract-testing/api-snapshots/test-snapshot.json.ejs +4 -0
  446. package/dist/templates/contract-testing/env/.env.pact.ejs +4 -0
  447. package/dist/templates/contract-testing/graphql/GraphqlSchemaTest.java.ejs +29 -0
  448. package/dist/templates/contract-testing/graphql/graphql-inspector.test.ts.ejs +20 -0
  449. package/dist/templates/contract-testing/graphql/graphql_schema_test.go.ejs +27 -0
  450. package/dist/templates/contract-testing/graphql/graphql_schema_test.rs.ejs +27 -0
  451. package/dist/templates/contract-testing/graphql/test_graphql_schema.py.ejs +21 -0
  452. package/dist/templates/contract-testing/grpc/GrpcContractTest.java.ejs +23 -0
  453. package/dist/templates/contract-testing/grpc/buf-breaking.yml.ejs +6 -0
  454. package/dist/templates/contract-testing/grpc/buf.yaml.ejs +11 -0
  455. package/dist/templates/contract-testing/grpc/grpc-contract.test.ts.ejs +16 -0
  456. package/dist/templates/contract-testing/grpc/grpc_contract_test.go.ejs +22 -0
  457. package/dist/templates/contract-testing/grpc/grpc_contract_test.rs.ejs +22 -0
  458. package/dist/templates/contract-testing/grpc/test_grpc_contract.py.ejs +18 -0
  459. package/dist/templates/contract-testing/message-queue/SchemaRegistryCheckIT.java.ejs +44 -0
  460. package/dist/templates/contract-testing/message-queue/schema-registry-check.ts.ejs +42 -0
  461. package/dist/templates/contract-testing/message-queue/schema_registry_test.go.ejs +71 -0
  462. package/dist/templates/contract-testing/message-queue/schema_registry_test.rs.ejs +61 -0
  463. package/dist/templates/contract-testing/message-queue/test_schema_registry.py.ejs +31 -0
  464. package/dist/templates/contract-testing/pact-samples/assignment-response.json.ejs +7 -0
  465. package/dist/templates/contract-testing/pact-samples/availability-response.json.ejs +13 -0
  466. package/dist/templates/contract-testing/pact-samples/availability-rule-response.json.ejs +8 -0
  467. package/dist/templates/contract-testing/pact-samples/capacity-response.json.ejs +11 -0
  468. package/dist/templates/contract-testing/pact-samples/fully-booked-response.json.ejs +6 -0
  469. package/dist/templates/contract-testing/pact-samples/schedule-override-response.json.ejs +7 -0
  470. package/dist/templates/contract-testing/rest-owned/PactVerificationIT.java.ejs +31 -0
  471. package/dist/templates/contract-testing/rest-owned/pact-consumer.test.ts.ejs +36 -0
  472. package/dist/templates/contract-testing/rest-owned/pact-deps.gradle.ejs +15 -0
  473. package/dist/templates/contract-testing/rest-owned/pact_consumer_test.go.ejs +48 -0
  474. package/dist/templates/contract-testing/rest-owned/pact_consumer_test.rs.ejs +23 -0
  475. package/dist/templates/contract-testing/rest-owned/test_pact_consumer.py.ejs +35 -0
  476. package/dist/templates/contract-testing/rest-public/OpenApiDiffIT.java.ejs +44 -0
  477. package/dist/templates/contract-testing/rest-public/export-openapi-java.gradle.ejs +13 -0
  478. package/dist/templates/contract-testing/rest-public/export-openapi.mjs.ejs +21 -0
  479. package/dist/templates/contract-testing/rest-public/export_openapi.go.ejs +30 -0
  480. package/dist/templates/contract-testing/rest-public/export_openapi.py.ejs +27 -0
  481. package/dist/templates/contract-testing/rest-public/export_openapi.rs.ejs +32 -0
  482. package/dist/templates/contract-testing/rest-public/openapi-diff.ts.ejs +42 -0
  483. package/dist/templates/contract-testing/rest-public/openapi_diff_test.go.ejs +35 -0
  484. package/dist/templates/contract-testing/rest-public/openapi_diff_test.rs.ejs +36 -0
  485. package/dist/templates/contract-testing/rest-public/test_openapi_diff.py.ejs +32 -0
  486. package/dist/templates/coverage/.coveragerc.ejs +26 -0
  487. package/dist/templates/coverage/.tarpaulin.toml.ejs +30 -0
  488. package/dist/templates/coverage/Cargo.toml.profile.release.ejs +24 -0
  489. package/dist/templates/coverage/jacoco-maven-setup.md.ejs +72 -0
  490. package/dist/templates/coverage/jacoco.gradle.ejs +49 -0
  491. package/dist/templates/coverage/kover.gradle.ejs +35 -0
  492. package/dist/templates/coverage/vitest.browser.config.ts.ejs +47 -0
  493. package/dist/templates/coverage/vitest.config.ts.ejs +47 -0
  494. package/dist/templates/docs/CODING_STANDARDS.md.ejs +81 -0
  495. package/dist/templates/docs/FEATURE_MATRIX.md.ejs +84 -0
  496. package/dist/templates/docs/GAP.md.ejs +58 -0
  497. package/dist/templates/docs/ISO27001_ANNEX_A.md.ejs +144 -0
  498. package/dist/templates/docs/MASTER_TEST_PLAN.md.ejs +52 -0
  499. package/dist/templates/docs/POST_MERGE_REVIEW_TEMPLATE.md.ejs +63 -0
  500. package/dist/templates/docs/SECURE_CODING_CHECKLIST.md.ejs +61 -0
  501. package/dist/templates/docs/adr/ADR-000_template.md.ejs +28 -0
  502. package/dist/templates/docs/bugs/bug-analysis.md.ejs +47 -0
  503. package/dist/templates/docs/bugs/bug-report.md.ejs +51 -0
  504. package/dist/templates/docs/bugs/bug-verification.md.ejs +42 -0
  505. package/dist/templates/docs/runbooks/deployment.md.ejs +119 -0
  506. package/dist/templates/docs/runbooks/prod-checklist.md.ejs +90 -0
  507. package/dist/templates/docs/runbooks/rollback.md.ejs +124 -0
  508. package/dist/templates/docs/runbooks/troubleshooting.md.ejs +130 -0
  509. package/dist/templates/docs/skeletons/arc42-canvas.md.ejs +46 -0
  510. package/dist/templates/docs/skeletons/arc42-full.md.ejs +59 -0
  511. package/dist/templates/docs/skeletons/decision-registry.md.ejs +38 -0
  512. package/dist/templates/docs/skeletons/er-model.md.ejs +19 -0
  513. package/dist/templates/docs/skeletons/glossary.md.ejs +8 -0
  514. package/dist/templates/docs/skeletons/governance.md.ejs +13 -0
  515. package/dist/templates/docs/skeletons/slo.md.ejs +21 -0
  516. package/dist/templates/docs/skeletons/technical-debt.md.ejs +8 -0
  517. package/dist/templates/docs/skeletons/test-strategy.md.ejs +14 -0
  518. package/dist/templates/docs/skeletons/threat-model-4q.md.ejs +20 -0
  519. package/dist/templates/docs/skeletons/threat-model-stride.md.ejs +22 -0
  520. package/dist/templates/docs/specs/atomic-task-list.md.ejs +57 -0
  521. package/dist/templates/docs/steering/product.md.ejs +44 -0
  522. package/dist/templates/docs/steering/structure.md.ejs +49 -0
  523. package/dist/templates/docs/steering/tech.md.ejs +53 -0
  524. package/dist/templates/documentation/cli-catalog.md.ejs +21 -0
  525. package/dist/templates/e2e/E2E_CONSTITUTION.md.ejs +77 -0
  526. package/dist/templates/e2e/playwright-python/conftest.py.ejs +22 -0
  527. package/dist/templates/e2e/playwright-python/run_axe.py.ejs +99 -0
  528. package/dist/templates/e2e/playwright-python/test_a11y.py.ejs +38 -0
  529. package/dist/templates/e2e/playwright-python/test_smoke.py.ejs +28 -0
  530. package/dist/templates/e2e/playwright-ts/a11y.spec.ts.ejs +20 -0
  531. package/dist/templates/e2e/playwright-ts/render-smoke.spec.ts.ejs +90 -0
  532. package/dist/templates/e2e/playwright-ts/run-axe.ts.ejs +74 -0
  533. package/dist/templates/e2e/smoke-journeys/journeys.spec.ts.ejs +53 -0
  534. package/dist/templates/evidence-files.json.ejs +13 -0
  535. package/dist/templates/frontend/FE_DESIGN_PRINCIPLES.md.ejs +115 -0
  536. package/dist/templates/frontend/FRONTEND_CONSTITUTION.md.ejs +170 -0
  537. package/dist/templates/frontend/design-tokens.json.ejs +107 -0
  538. package/dist/templates/frontend/vrt-setup.md.ejs +83 -0
  539. package/dist/templates/githooks/commit-msg.ejs +60 -0
  540. package/dist/templates/githooks/pre-commit.ejs +95 -0
  541. package/dist/templates/githooks/pre-push.ejs +251 -0
  542. package/dist/templates/githooks/setup-hooks.sh.ejs +8 -0
  543. package/dist/templates/github/PULL_REQUEST_TEMPLATE.md +37 -0
  544. package/dist/templates/github/PULL_REQUEST_TEMPLATE.md.ejs +52 -0
  545. package/dist/templates/github/actions/build-cache/action.yml.ejs +84 -0
  546. package/dist/templates/github/actions/nas-ssh/action.yml.ejs +53 -0
  547. package/dist/templates/github/actions/setup-java-maven/action.yml.ejs +53 -0
  548. package/dist/templates/github/actions/setup-node-pnpm/action.yml.ejs +39 -0
  549. package/dist/templates/github/actions/sign-and-attest/action.yml.ejs +53 -0
  550. package/dist/templates/github/dependabot.yml.ejs +66 -0
  551. package/dist/templates/github/extended-ci-paths.txt.ejs +38 -0
  552. package/dist/templates/github/issue-templates/bug-report.yml +72 -0
  553. package/dist/templates/github/issue-templates/compliance-item.yml.ejs +75 -0
  554. package/dist/templates/github/issue-templates/config.yml +2 -0
  555. package/dist/templates/github/issue-templates/epic.yml +66 -0
  556. package/dist/templates/github/issue-templates/feature-request.yml +24 -0
  557. package/dist/templates/github/issue-templates/task-brief.yml.ejs +125 -0
  558. package/dist/templates/github/labels.yml.ejs +161 -0
  559. package/dist/templates/github/scripts/sticky-failure-issue.sh.ejs +61 -0
  560. package/dist/templates/github/workflows/01-pr-fast.yml.ejs +1089 -0
  561. package/dist/templates/github/workflows/02-pr-extended.yml.ejs +512 -0
  562. package/dist/templates/github/workflows/03-human-approval.yml.ejs +76 -0
  563. package/dist/templates/github/workflows/04-deploy-test.yml.ejs +425 -0
  564. package/dist/templates/github/workflows/05-release.yml.ejs +813 -0
  565. package/dist/templates/github/workflows/06-nightly-lite.yml.ejs +125 -0
  566. package/dist/templates/github/workflows/06-nightly.yml.ejs +25 -0
  567. package/dist/templates/github/workflows/07-weekly-lite.yml.ejs +221 -0
  568. package/dist/templates/github/workflows/07-weekly.yml.ejs +23 -0
  569. package/dist/templates/github/workflows/08-monthly.yml.ejs +23 -0
  570. package/dist/templates/github/workflows/09-heartbeat.yml.ejs +268 -0
  571. package/dist/templates/github/workflows/10-deploy-prod.yml.ejs +144 -0
  572. package/dist/templates/github/workflows/11-k6-on-demand.yml.ejs +43 -0
  573. package/dist/templates/github/workflows/12-mutation-scheduled.yml.ejs +100 -0
  574. package/dist/templates/github/workflows/13-archunit-extended.yml.ejs +33 -0
  575. package/dist/templates/github/workflows/14-license-scan.yml.ejs +45 -0
  576. package/dist/templates/github/workflows/15-codeql.yml.ejs +117 -0
  577. package/dist/templates/github/workflows/16-frontend-quality.yml.ejs +154 -0
  578. package/dist/templates/github/workflows/17-ossf-scorecard.yml.ejs +46 -0
  579. package/dist/templates/github/workflows/18-frontend-lane.yml.ejs +50 -0
  580. package/dist/templates/github/workflows/_ai-draft-check.yml.ejs +44 -0
  581. package/dist/templates/github/workflows/_contract-postman.yml.ejs +93 -0
  582. package/dist/templates/github/workflows/_cosign-copy/aws-ecs.ejs +90 -0
  583. package/dist/templates/github/workflows/_cosign-copy/azure-container-app.ejs +97 -0
  584. package/dist/templates/github/workflows/_cosign-copy/gcp-cloud-run.ejs +102 -0
  585. package/dist/templates/github/workflows/_cosign-copy/ghcr.ejs +76 -0
  586. package/dist/templates/github/workflows/_cosign-copy/nas-compose.ejs +69 -0
  587. package/dist/templates/github/workflows/_deploy/aws-ecs.ejs +78 -0
  588. package/dist/templates/github/workflows/_deploy/azure-container-app.ejs +59 -0
  589. package/dist/templates/github/workflows/_deploy/gcp-cloud-run.ejs +71 -0
  590. package/dist/templates/github/workflows/_deploy/ghcr.ejs +19 -0
  591. package/dist/templates/github/workflows/_deploy/nas-compose.ejs +45 -0
  592. package/dist/templates/github/workflows/_k6-runner.yml.ejs +107 -0
  593. package/dist/templates/github/workflows/_label-on-approve.yml.ejs +44 -0
  594. package/dist/templates/github/workflows/_label-sync.yml.ejs +58 -0
  595. package/dist/templates/github/workflows/_monthly.yml.ejs +343 -0
  596. package/dist/templates/github/workflows/_nightly.yml.ejs +826 -0
  597. package/dist/templates/github/workflows/_notify.yml.ejs +96 -0
  598. package/dist/templates/github/workflows/_partials/macos-keychain-bypass.ejs +6 -0
  599. package/dist/templates/github/workflows/_partials/macos-keychain-restore.ejs +6 -0
  600. package/dist/templates/github/workflows/_partials/scheduled-heavy-jobs.ejs +117 -0
  601. package/dist/templates/github/workflows/_partials/sigstore-preflight.ejs +8 -0
  602. package/dist/templates/github/workflows/_partials/trivy-fs-java.ejs +27 -0
  603. package/dist/templates/github/workflows/_post-merge-notify.yml.ejs +228 -0
  604. package/dist/templates/github/workflows/_pr-staleness.yml.ejs +85 -0
  605. package/dist/templates/github/workflows/_shared-security.yml.ejs +132 -0
  606. package/dist/templates/github/workflows/_sigstore-retry-sign.yml.ejs +102 -0
  607. package/dist/templates/github/workflows/_weekly.yml.ejs +313 -0
  608. package/dist/templates/github/workflows/drift-shadow.yml.ejs +51 -0
  609. package/dist/templates/github/workflows/five-lane/ci.yml.ejs +54 -0
  610. package/dist/templates/github/workflows/five-lane/nightly.yml.ejs +70 -0
  611. package/dist/templates/github/workflows/five-lane/release.yml.ejs +53 -0
  612. package/dist/templates/github/workflows/five-lane/weekly.yml.ejs +72 -0
  613. package/dist/templates/github/workflows/issue-state.yml.ejs +93 -0
  614. package/dist/templates/global-invariants/GLOBAL_INVARIANTS.md.ejs +34 -0
  615. package/dist/templates/governance/RACI.md.ejs +21 -0
  616. package/dist/templates/governance/ci-mental-model.md.ejs +268 -0
  617. package/dist/templates/governance/contract-integrity-policy.md.ejs +116 -0
  618. package/dist/templates/governance/enterprise-compliance-baseline.md.ejs +111 -0
  619. package/dist/templates/governance/evidence-retention.md.ejs +43 -0
  620. package/dist/templates/governance/gdpr-erasure-runbook.md.ejs +165 -0
  621. package/dist/templates/governance/qa-audit-phases.md.ejs +129 -0
  622. package/dist/templates/governance/risk-assessment-template.md.ejs +105 -0
  623. package/dist/templates/governance/risk-register.md.ejs +76 -0
  624. package/dist/templates/governance/solo-dev-exception.md.ejs +96 -0
  625. package/dist/templates/governance/validation-evidence.md.ejs +131 -0
  626. package/dist/templates/infra/azure/containerapp.tpl.yaml.ejs +74 -0
  627. package/dist/templates/infra/nas-compose/deploy.sh.ejs +114 -0
  628. package/dist/templates/integration-testing/AbstractIntegrationTest.java.ejs +36 -0
  629. package/dist/templates/integration-testing/NoH2ArchTest.java.ejs +24 -0
  630. package/dist/templates/integration-testing/conftest.py.ejs +20 -0
  631. package/dist/templates/integration-testing/db_fixture.rs.ejs +48 -0
  632. package/dist/templates/integration-testing/eslint-no-fake-db.json.ejs +19 -0
  633. package/dist/templates/integration-testing/eslint.config.no-fake-db.mjs.ejs +46 -0
  634. package/dist/templates/integration-testing/main_test.go.ejs +53 -0
  635. package/dist/templates/integration-testing/main_test_sqlite.go.ejs +37 -0
  636. package/dist/templates/integration-testing/test-setup.ts.ejs +26 -0
  637. package/dist/templates/integration-testing/testcontainers-deps.gradle.ejs +9 -0
  638. package/dist/templates/java/DeprecationInterceptor.java.ejs +50 -0
  639. package/dist/templates/java/archunit-hexagonal.ejs +69 -0
  640. package/dist/templates/java/modulith/ApplicationModulesTest.java.ejs +21 -0
  641. package/dist/templates/java/modulith/modulith-deps.gradle.ejs +13 -0
  642. package/dist/templates/java/modulith/modulith-maven-setup.md.ejs +21 -0
  643. package/dist/templates/java/pharma/AuditEvent.java.ejs +138 -0
  644. package/dist/templates/java/pharma/AuditMapper.java.ejs +60 -0
  645. package/dist/templates/java/pharma/archunit-pharma.ejs +128 -0
  646. package/dist/templates/kit/GLOBAL_KIT.md.ejs +11 -0
  647. package/dist/templates/kit/dim.md.ejs +33 -0
  648. package/dist/templates/llms-txt.config.json.ejs +22 -0
  649. package/dist/templates/local-wrapper/.env.example.ejs +15 -0
  650. package/dist/templates/local-wrapper/Makefile.ejs +44 -0
  651. package/dist/templates/local-wrapper/run.sh.ejs +5 -0
  652. package/dist/templates/middleware/410-gone-handler.ts.ejs +41 -0
  653. package/dist/templates/middleware/correlation-id.ts.ejs +32 -0
  654. package/dist/templates/middleware/deprecation.ts.ejs +45 -0
  655. package/dist/templates/middleware/error-handler.ts.ejs +73 -0
  656. package/dist/templates/middleware/payload-size-limit.ts.ejs +51 -0
  657. package/dist/templates/mutation/README.md.ejs +67 -0
  658. package/dist/templates/mutation/cargo-mutants.toml.ejs +9 -0
  659. package/dist/templates/mutation/mutmut-config.toml.ejs +7 -0
  660. package/dist/templates/mutation/parse-mutants.mjs.ejs +44 -0
  661. package/dist/templates/mutation/parse-mutmut.py.ejs +44 -0
  662. package/dist/templates/mutation/pitest-l2-setup.md.ejs +66 -0
  663. package/dist/templates/mutation/pitest-maven-setup.md.ejs +64 -0
  664. package/dist/templates/mutation/pitest.gradle.ejs +23 -0
  665. package/dist/templates/mutation/stryker.conf.json.ejs +17 -0
  666. package/dist/templates/observability/setup.md.ejs +495 -0
  667. package/dist/templates/operations/handbook.md.ejs +217 -0
  668. package/dist/templates/perf/bundle-budget.json.ejs +29 -0
  669. package/dist/templates/perf/k6/reports/csv-report.py.ejs +91 -0
  670. package/dist/templates/perf/k6/reports/html-report.py.ejs +114 -0
  671. package/dist/templates/perf/k6/reports/json-report.py.ejs +74 -0
  672. package/dist/templates/perf/k6/scenarios/breakpoint.js.ejs +26 -0
  673. package/dist/templates/perf/k6/scenarios/burst.js.ejs +43 -0
  674. package/dist/templates/perf/k6/scenarios/endurance.js.ejs +36 -0
  675. package/dist/templates/perf/k6/scenarios/load.js.ejs +26 -0
  676. package/dist/templates/perf/k6/scenarios/ramp-down.js.ejs +29 -0
  677. package/dist/templates/perf/k6/scenarios/ramp-up.js.ejs +32 -0
  678. package/dist/templates/perf/k6/scenarios/smoke.js.ejs +23 -0
  679. package/dist/templates/perf/k6/scenarios/soak.js.ejs +26 -0
  680. package/dist/templates/perf/k6/scenarios/spike.js.ejs +29 -0
  681. package/dist/templates/perf/k6/scenarios/steady-state.js.ejs +31 -0
  682. package/dist/templates/perf/k6/scenarios/stress.js.ejs +29 -0
  683. package/dist/templates/perf/k6/scenarios/volume.js.ejs +36 -0
  684. package/dist/templates/perf/k6/seed/test-data.sql.ejs +59 -0
  685. package/dist/templates/perf/lighthouserc.json.ejs +41 -0
  686. package/dist/templates/quality/iso9001/CAPA_LOG.md.ejs +36 -0
  687. package/dist/templates/quality/iso9001/DOCUMENT_CONTROL.md.ejs +36 -0
  688. package/dist/templates/quality/iso9001/REQUIREMENTS_TRACEABILITY.md.ejs +61 -0
  689. package/dist/templates/regulated/overlay.json.ejs +26 -0
  690. package/dist/templates/regulated/regulated-overlay.md.ejs +40 -0
  691. package/dist/templates/resilience/RESILIENCE.md.ejs +310 -0
  692. package/dist/templates/root/.gitignore.ejs +85 -0
  693. package/dist/templates/root/CODEOWNERS.ejs +18 -0
  694. package/dist/templates/root/CONTRIBUTING.md.ejs +62 -0
  695. package/dist/templates/root/SECURITY.md.ejs +19 -0
  696. package/dist/templates/root/TEST_TAXONOMY.md.ejs +100 -0
  697. package/dist/templates/root/commitlint.config.js.ejs +4 -0
  698. package/dist/templates/root/docs/METHOD/CANONICAL_PATHS.md.ejs +24 -0
  699. package/dist/templates/root/docs/METHOD/ENGINEERING_DEFAULTS.md.ejs +136 -0
  700. package/dist/templates/root/docs/METHOD/KNOWLEDGE_MAP.md.ejs +90 -0
  701. package/dist/templates/root/docs/METHOD/PATTERNS_CATALOG.md.ejs +51 -0
  702. package/dist/templates/root/docs/METHOD/REUSE_REGISTRY.md.ejs +26 -0
  703. package/dist/templates/root/docs/METHOD/REUSE_REGISTRY_SPEC.md.ejs +80 -0
  704. package/dist/templates/root/docs/METHOD/SSOT_CORE_SET.md.ejs +62 -0
  705. package/dist/templates/root/docs/METHOD/TRACK_ROUTER.md.ejs +39 -0
  706. package/dist/templates/root/docs/SYSTEM/CANON.md.ejs +359 -0
  707. package/dist/templates/root/docs/SYSTEM/FAIL_CLOSED.md.ejs +171 -0
  708. package/dist/templates/root/editorconfig.ejs +39 -0
  709. package/dist/templates/root/tsconfig.json.ejs +21 -0
  710. package/dist/templates/rust/clippy.toml.ejs +14 -0
  711. package/dist/templates/schemas/milestone.schema.json.ejs +164 -0
  712. package/dist/templates/schemas/rtm-verdict.schema.json.ejs +52 -0
  713. package/dist/templates/schemas/source-record.schema.json.ejs +91 -0
  714. package/dist/templates/schemas/tabletop-evidence.schema.json.ejs +53 -0
  715. package/dist/templates/schemas/use-case.schema.json.ejs +59 -0
  716. package/dist/templates/scripts/apply-branch-protection.mjs.ejs +70 -0
  717. package/dist/templates/scripts/audit-toolchain.mjs.ejs +112 -0
  718. package/dist/templates/scripts/bg-run.sh.ejs +59 -0
  719. package/dist/templates/scripts/capacity-probe.mjs.ejs +147 -0
  720. package/dist/templates/scripts/capture-debt-baseline.mjs.ejs +99 -0
  721. package/dist/templates/scripts/check-acceptance.mjs.ejs +298 -0
  722. package/dist/templates/scripts/check-action-pins.mjs.ejs +215 -0
  723. package/dist/templates/scripts/check-agent-return.mjs.ejs +177 -0
  724. package/dist/templates/scripts/check-all.mjs.ejs +1746 -0
  725. package/dist/templates/scripts/check-anti-fake-green.mjs.ejs +80 -0
  726. package/dist/templates/scripts/check-anti-proforma.mjs.ejs +547 -0
  727. package/dist/templates/scripts/check-api-e2e.mjs.ejs +106 -0
  728. package/dist/templates/scripts/check-arc42-slots.mjs.ejs +25 -0
  729. package/dist/templates/scripts/check-assertion-delta.mjs.ejs +287 -0
  730. package/dist/templates/scripts/check-audit-dry-pass.mjs.ejs +176 -0
  731. package/dist/templates/scripts/check-bundle-size.mjs.ejs +151 -0
  732. package/dist/templates/scripts/check-canonical-paths.mjs.ejs +39 -0
  733. package/dist/templates/scripts/check-ci-tiers.mjs.ejs +111 -0
  734. package/dist/templates/scripts/check-claude-md-lint.mjs.ejs +142 -0
  735. package/dist/templates/scripts/check-collab-mode-wired.mjs.ejs +60 -0
  736. package/dist/templates/scripts/check-commit-footer-rationale.mjs.ejs +170 -0
  737. package/dist/templates/scripts/check-constraint-scan.mjs.ejs +681 -0
  738. package/dist/templates/scripts/check-consumer-audit.mjs.ejs +414 -0
  739. package/dist/templates/scripts/check-continue-on-error.mjs.ejs +227 -0
  740. package/dist/templates/scripts/check-cross-model-review.mjs.ejs +458 -0
  741. package/dist/templates/scripts/check-decision-registry.mjs.ejs +101 -0
  742. package/dist/templates/scripts/check-doc-freshness.mjs.ejs +17 -0
  743. package/dist/templates/scripts/check-doc-links.mjs.ejs +98 -0
  744. package/dist/templates/scripts/check-doc-set.mjs.ejs +18 -0
  745. package/dist/templates/scripts/check-docs.mjs.ejs +71 -0
  746. package/dist/templates/scripts/check-domain-api-surface.mjs.ejs +80 -0
  747. package/dist/templates/scripts/check-drift.mjs.ejs +167 -0
  748. package/dist/templates/scripts/check-duplication.mjs.ejs +45 -0
  749. package/dist/templates/scripts/check-e2e-escalation.mjs.ejs +199 -0
  750. package/dist/templates/scripts/check-e2e-quarantine.mjs.ejs +68 -0
  751. package/dist/templates/scripts/check-emission-parity.mjs.ejs +91 -0
  752. package/dist/templates/scripts/check-exit-code-contract.mjs.ejs +138 -0
  753. package/dist/templates/scripts/check-fail-closed-audit.mjs.ejs +389 -0
  754. package/dist/templates/scripts/check-fe-boundaries.mjs.ejs +184 -0
  755. package/dist/templates/scripts/check-feature-matrix.mjs.ejs +835 -0
  756. package/dist/templates/scripts/check-frontend-lane.mjs.ejs +73 -0
  757. package/dist/templates/scripts/check-grace-window.mjs.ejs +102 -0
  758. package/dist/templates/scripts/check-handoff-doc.mjs.ejs +192 -0
  759. package/dist/templates/scripts/check-hook-routing.mjs.ejs +224 -0
  760. package/dist/templates/scripts/check-image-pins.mjs.ejs +90 -0
  761. package/dist/templates/scripts/check-inline-suppressions.mjs.ejs +153 -0
  762. package/dist/templates/scripts/check-iso9001.mjs.ejs +146 -0
  763. package/dist/templates/scripts/check-knowledge-map.mjs.ejs +49 -0
  764. package/dist/templates/scripts/check-local-ci-parity.mjs.ejs +382 -0
  765. package/dist/templates/scripts/check-m16-handoff.mjs.ejs +112 -0
  766. package/dist/templates/scripts/check-merge-method.mjs.ejs +92 -0
  767. package/dist/templates/scripts/check-milestones.mjs.ejs +678 -0
  768. package/dist/templates/scripts/check-min-test-execution.mjs.ejs +225 -0
  769. package/dist/templates/scripts/check-muted-test.mjs.ejs +270 -0
  770. package/dist/templates/scripts/check-no-stub-redirects.mjs.ejs +154 -0
  771. package/dist/templates/scripts/check-no-tracked-artifacts.mjs.ejs +134 -0
  772. package/dist/templates/scripts/check-oracle-discrimination.mjs.ejs +244 -0
  773. package/dist/templates/scripts/check-pii-scan.mjs.ejs +67 -0
  774. package/dist/templates/scripts/check-pipe-tee-hazard.mjs.ejs +85 -0
  775. package/dist/templates/scripts/check-pr-size-gate.mjs.ejs +85 -0
  776. package/dist/templates/scripts/check-refutation-verdicts.mjs.ejs +256 -0
  777. package/dist/templates/scripts/check-regulated-overlay.mjs.ejs +158 -0
  778. package/dist/templates/scripts/check-render-smoke.mjs.ejs +100 -0
  779. package/dist/templates/scripts/check-reuse-registry.mjs.ejs +44 -0
  780. package/dist/templates/scripts/check-review-completion.mjs.ejs +708 -0
  781. package/dist/templates/scripts/check-safety-adopt-ratchet.mjs.ejs +142 -0
  782. package/dist/templates/scripts/check-secret-presence.mjs.ejs +191 -0
  783. package/dist/templates/scripts/check-secret-scan.mjs.ejs +82 -0
  784. package/dist/templates/scripts/check-skip-critical-e2e.mjs.ejs +142 -0
  785. package/dist/templates/scripts/check-smoke-journeys.mjs.ejs +272 -0
  786. package/dist/templates/scripts/check-solo-reactivation.mjs.ejs +102 -0
  787. package/dist/templates/scripts/check-sources.mjs.ejs +245 -0
  788. package/dist/templates/scripts/check-ssot-core.mjs.ejs +45 -0
  789. package/dist/templates/scripts/check-stack-conformity.mjs.ejs +160 -0
  790. package/dist/templates/scripts/check-stride-traceability.mjs.ejs +101 -0
  791. package/dist/templates/scripts/check-suppression-expiry.mjs.ejs +79 -0
  792. package/dist/templates/scripts/check-suppression-rationale.mjs.ejs +76 -0
  793. package/dist/templates/scripts/check-suppressions.mjs.ejs +150 -0
  794. package/dist/templates/scripts/check-tabletop-evidence.mjs.ejs +499 -0
  795. package/dist/templates/scripts/check-tdd-evidence.mjs.ejs +381 -0
  796. package/dist/templates/scripts/check-test-naming.mjs.ejs +151 -0
  797. package/dist/templates/scripts/check-test-pyramid.mjs.ejs +170 -0
  798. package/dist/templates/scripts/check-test-scope-tier.mjs.ejs +208 -0
  799. package/dist/templates/scripts/check-tier-coverage.mjs.ejs +71 -0
  800. package/dist/templates/scripts/check-todo-max-age.mjs.ejs +205 -0
  801. package/dist/templates/scripts/check-touched-vs-manifest.mjs.ejs +179 -0
  802. package/dist/templates/scripts/check-unwired-guards.mjs.ejs +367 -0
  803. package/dist/templates/scripts/check-use-cases.mjs.ejs +345 -0
  804. package/dist/templates/scripts/check-validator-helptext.mjs.ejs +80 -0
  805. package/dist/templates/scripts/check-wiki-lint.mjs.ejs +162 -0
  806. package/dist/templates/scripts/check-workflow-docs-sync.mjs.ejs +91 -0
  807. package/dist/templates/scripts/check-workflow-job-naming.mjs.ejs +102 -0
  808. package/dist/templates/scripts/check-workflow-perms.mjs.ejs +54 -0
  809. package/dist/templates/scripts/check-workflow-runners.mjs.ejs +87 -0
  810. package/dist/templates/scripts/check-workflow-sha-pinning.mjs.ejs +89 -0
  811. package/dist/templates/scripts/check-workflow-test-integrity.mjs.ejs +235 -0
  812. package/dist/templates/scripts/checks/check-rust-no-unsafe.mjs.ejs +58 -0
  813. package/dist/templates/scripts/checks/check-rust-no-unwrap.mjs.ejs +61 -0
  814. package/dist/templates/scripts/ci-classify-changes.mjs.ejs +119 -0
  815. package/dist/templates/scripts/conformance.mjs.ejs +37 -0
  816. package/dist/templates/scripts/constraint-map.json.ejs +25 -0
  817. package/dist/templates/scripts/debt-lib.mjs.ejs +1015 -0
  818. package/dist/templates/scripts/debt-report.mjs.ejs +199 -0
  819. package/dist/templates/scripts/domain-api-surface.json.ejs +23 -0
  820. package/dist/templates/scripts/done-evidence.mjs.ejs +359 -0
  821. package/dist/templates/scripts/evidence-collect.mjs.ejs +264 -0
  822. package/dist/templates/scripts/evidence-prune.mjs.ejs +96 -0
  823. package/dist/templates/scripts/evidence-rotate.mjs.ejs +53 -0
  824. package/dist/templates/scripts/evidence-writer.sh.ejs +220 -0
  825. package/dist/templates/scripts/gate-registry.yml.ejs +286 -0
  826. package/dist/templates/scripts/gen-doc-index.mjs.ejs +19 -0
  827. package/dist/templates/scripts/gen-gap.mjs.ejs +153 -0
  828. package/dist/templates/scripts/gen-llms-txt.mjs.ejs +18 -0
  829. package/dist/templates/scripts/gen-wiki.mjs.ejs +196 -0
  830. package/dist/templates/scripts/gh-cleanup-expired-artifacts.mjs.ejs +134 -0
  831. package/dist/templates/scripts/gold-audit.mjs.ejs +19 -0
  832. package/dist/templates/scripts/i18n-literal-scanner.mjs.ejs +175 -0
  833. package/dist/templates/scripts/ingest-zap-report.mjs.ejs +107 -0
  834. package/dist/templates/scripts/inject-pact-samples.sh.ejs +96 -0
  835. package/dist/templates/scripts/issue-readiness.mjs.ejs +84 -0
  836. package/dist/templates/scripts/knowledge-map-update.mjs.ejs +32 -0
  837. package/dist/templates/scripts/lib/acceptance-criteria.mjs.ejs +213 -0
  838. package/dist/templates/scripts/lib/agent-return-validate.mjs.ejs +481 -0
  839. package/dist/templates/scripts/lib/coverage-gate.mjs.ejs +54 -0
  840. package/dist/templates/scripts/lib/e2e-reliability.mjs.ejs +207 -0
  841. package/dist/templates/scripts/lib/ephemeral-server.mjs.ejs +148 -0
  842. package/dist/templates/scripts/lib/evidence-binding.mjs.ejs +140 -0
  843. package/dist/templates/scripts/lib/exact-sha-policy.mjs.ejs +233 -0
  844. package/dist/templates/scripts/lib/gate-args.mjs.ejs +20 -0
  845. package/dist/templates/scripts/lib/gate-evidence.mjs.ejs +622 -0
  846. package/dist/templates/scripts/lib/gate-mutex.mjs.ejs +340 -0
  847. package/dist/templates/scripts/lib/glob-walk.mjs.ejs +148 -0
  848. package/dist/templates/scripts/lib/run-helpers.mjs.ejs +416 -0
  849. package/dist/templates/scripts/lib/seed-common.sh.ejs +105 -0
  850. package/dist/templates/scripts/lib/waiter-count.mjs.ejs +62 -0
  851. package/dist/templates/scripts/muted-tests-baseline.json.ejs +4 -0
  852. package/dist/templates/scripts/optional-emissions.json.ejs +117 -0
  853. package/dist/templates/scripts/oracle-discrimination-baseline.json.ejs +6 -0
  854. package/dist/templates/scripts/pid-watch.sh.ejs +78 -0
  855. package/dist/templates/scripts/pii-scan.mjs.ejs +125 -0
  856. package/dist/templates/scripts/pr-merge-watch.mjs.ejs +445 -0
  857. package/dist/templates/scripts/record-agent-return.mjs.ejs +279 -0
  858. package/dist/templates/scripts/record-journey-evidence.mjs.ejs +69 -0
  859. package/dist/templates/scripts/rework-log.mjs.ejs +172 -0
  860. package/dist/templates/scripts/run-postman-tests.sh.ejs +112 -0
  861. package/dist/templates/scripts/schemas/agent-return-external.schema.json +74 -0
  862. package/dist/templates/scripts/schemas/agent-return.schema.json.ejs +181 -0
  863. package/dist/templates/scripts/schemas/cross-model-dispatch.schema.json +95 -0
  864. package/dist/templates/scripts/seed-test-data.sh.ejs +99 -0
  865. package/dist/templates/scripts/self-validation.mjs.ejs +126 -0
  866. package/dist/templates/scripts/setup-repo.sh.ejs +78 -0
  867. package/dist/templates/scripts/spotbugs-baseline.json.ejs +4 -0
  868. package/dist/templates/scripts/test-gate-layering.mjs.ejs +92 -0
  869. package/dist/templates/scripts/validate-api-snapshots.mjs.ejs +86 -0
  870. package/dist/templates/scripts/validate-k6-scenarios.mjs.ejs +96 -0
  871. package/dist/templates/scripts/validate-openapi-field-types.mjs.ejs +124 -0
  872. package/dist/templates/scripts/validate-postman-collection.mjs.ejs +159 -0
  873. package/dist/templates/scripts/verify-fe-coverage.mjs.ejs +138 -0
  874. package/dist/templates/scripts/verify-i18n-parity.mjs.ejs +114 -0
  875. package/dist/templates/scripts/verify-module-coverage.mjs.ejs +207 -0
  876. package/dist/templates/scripts/verify-mutation-baseline.mjs.ejs +209 -0
  877. package/dist/templates/scripts/verify-spotbugs.mjs.ejs +216 -0
  878. package/dist/templates/scripts/verify-tokens.mjs.ejs +177 -0
  879. package/dist/templates/security/RISK_ASSESSMENT.md.ejs +35 -0
  880. package/dist/templates/security/STRIDE.md.ejs +24 -0
  881. package/dist/templates/security/gitleaks.toml.ejs +21 -0
  882. package/dist/templates/security/zap/baseline-auth.context.ejs +58 -0
  883. package/dist/templates/security/zap/rules.tsv.ejs +100 -0
  884. package/dist/templates/ship/TICK_PROMPT.md.ejs +55 -0
  885. package/dist/templates/ship/supervisor.sh.ejs +46 -0
  886. package/dist/templates/smoke-journeys/manifest.json.ejs +1 -0
  887. package/dist/templates/sonar-project.properties.ejs +25 -0
  888. package/dist/templates/standards/doc-profile.ejs +27 -0
  889. package/dist/templates/standards/gold-doc-set.yml.ejs +528 -0
  890. package/dist/templates/standards/gold-registry.java.yml.ejs +266 -0
  891. package/dist/templates/standards/gold-registry.typescript.yml.ejs +104 -0
  892. package/dist/templates/standards/gold-registry.yml.ejs +119 -0
  893. package/dist/templates/standards/thresholds.yml.ejs +86 -0
  894. package/dist/templates/static-analysis/.dependency-cruiser.cjs.ejs +78 -0
  895. package/dist/templates/static-analysis/.golangci.yml.ejs +70 -0
  896. package/dist/templates/static-analysis/.shellcheckrc.ejs +13 -0
  897. package/dist/templates/static-analysis/.yamllint.yml.ejs +41 -0
  898. package/dist/templates/static-analysis/checkstyle.xml.ejs +83 -0
  899. package/dist/templates/static-analysis/detekt.yml.ejs +26 -0
  900. package/dist/templates/static-analysis/eslint.config.mjs.ejs +54 -0
  901. package/dist/templates/static-analysis/eslint.config.static.mjs.ejs +38 -0
  902. package/dist/templates/static-analysis/eslintrc-static.json.ejs +30 -0
  903. package/dist/templates/static-analysis/jscpd.json.ejs +25 -0
  904. package/dist/templates/static-analysis/knip.json.ejs +26 -0
  905. package/dist/templates/static-analysis/pmd-ruleset.xml.ejs +92 -0
  906. package/dist/templates/static-analysis/prettierignore.ejs +59 -0
  907. package/dist/templates/static-analysis/prettierrc.json.ejs +10 -0
  908. package/dist/templates/static-analysis/requirements-dev.txt.ejs +11 -0
  909. package/dist/templates/static-analysis/ruff.toml.ejs +40 -0
  910. package/dist/templates/static-analysis/rustfmt.toml.ejs +3 -0
  911. package/dist/templates/static-analysis/spotbugs-exclude.xml.ejs +120 -0
  912. package/dist/templates/static-analysis/spotbugs.gradle.ejs +18 -0
  913. package/dist/templates/static-analysis/spotless.gradle.ejs +38 -0
  914. package/dist/templates/static-analysis/stylelintrc.json.ejs +16 -0
  915. package/dist/templates/suppressions/archunit-baseline.json.ejs +1 -0
  916. package/dist/templates/suppressions/consumer-audit-allowlist.json.ejs +1 -0
  917. package/dist/templates/suppressions/gitleaksignore.ejs +20 -0
  918. package/dist/templates/suppressions/pii-allowlist.json.ejs +1 -0
  919. package/dist/templates/suppressions/suppressions-schema.json.ejs +39 -0
  920. package/dist/templates/suppressions/trivyignore.ejs +17 -0
  921. package/dist/templates/testing/test-taxonomy.md.ejs +120 -0
  922. package/dist/test/example.behavioral.test.js +2 -0
  923. package/dist/types/memory.d.ts +19 -0
  924. package/dist/types/memory.js +1 -0
  925. package/dist/types/plan.d.ts +102 -0
  926. package/dist/types/plan.js +2 -0
  927. package/dist/types/plugin.d.ts +45 -0
  928. package/dist/types/plugin.js +1 -0
  929. package/dist/utils/canon-loader.js +2 -0
  930. package/dist/utils/channel.js +1 -0
  931. package/dist/utils/config.d.ts +79 -0
  932. package/dist/utils/config.js +5 -0
  933. package/dist/utils/confirm-downgrade.js +5 -0
  934. package/dist/utils/env.js +2 -0
  935. package/dist/utils/error-catalog.js +2 -0
  936. package/dist/utils/errors.js +2 -0
  937. package/dist/utils/evidence-log.js +3 -0
  938. package/dist/utils/file-lock.js +2 -0
  939. package/dist/utils/first-run.js +8 -0
  940. package/dist/utils/fs.js +3 -0
  941. package/dist/utils/github-issue-helper.js +4 -0
  942. package/dist/utils/gradle.js +21 -0
  943. package/dist/utils/json-output.js +2 -0
  944. package/dist/utils/logger-config.js +4 -0
  945. package/dist/utils/logger.js +3 -0
  946. package/dist/utils/maturity-check.js +2 -0
  947. package/dist/utils/pkg.js +3 -0
  948. package/dist/utils/platform.js +2 -0
  949. package/dist/utils/plugin-loader.js +2 -0
  950. package/dist/utils/plugin-worker.js +2 -0
  951. package/dist/utils/prettier-format.js +2 -0
  952. package/dist/utils/profiler.js +2 -0
  953. package/dist/utils/render.js +2 -0
  954. package/dist/utils/replay.js +3 -0
  955. package/dist/utils/run-cli.js +6 -0
  956. package/dist/utils/run-id.js +3 -0
  957. package/dist/utils/safe-read.js +2 -0
  958. package/dist/utils/task-id.js +2 -0
  959. package/dist/utils/today.js +2 -0
  960. package/dist/utils/tty.js +2 -0
  961. package/dist/utils/walk-dir.js +2 -0
  962. package/dist/verify/rules/drive-by-scope.js +1 -0
  963. package/dist/verify/rules/orphan-todos.js +1 -0
  964. package/dist/verify/rules/registry.js +1 -0
  965. package/dist/verify/rules/skip-patterns.js +1 -0
  966. package/dist/verify/rules/types.d.ts +15 -0
  967. package/dist/verify/rules/types.js +1 -0
  968. package/dist/verify/rules/ui-language.js +1 -0
  969. package/dist/verify/run.js +3 -0
  970. package/dist/wizard/archetype-defaults.js +1 -0
  971. package/dist/wizard/presets.js +1 -0
  972. package/dist/wizard/prompts.js +44 -0
  973. package/dist/wizard/types.d.ts +637 -0
  974. package/dist/wizard/types.js +1 -0
  975. package/dist/worktree/harvest.js +2 -0
  976. package/dist/worktree/links.js +2 -0
  977. package/dist/worktree/paths.js +2 -0
  978. package/dist/worktree/validate.js +3 -0
  979. package/package.json +124 -0
  980. package/scripts/check-arc42-slots.mjs +738 -0
  981. package/scripts/check-doc-freshness.mjs +236 -0
  982. package/scripts/check-doc-set.mjs +243 -0
  983. package/scripts/check-doc-style.mjs +228 -0
  984. package/scripts/gold-audit.mjs +319 -0
  985. package/scripts/lib/doc-gate-allowlist.mjs +123 -0
  986. package/scripts/lib/doc-set-resolve.mjs +274 -0
  987. package/scripts/lib/glob-walk.mjs +148 -0
  988. package/scripts/lib/gold-audit-lib.mjs +1260 -0
  989. package/scripts/lib/run-helpers.mjs +413 -0
@@ -0,0 +1,1015 @@
1
+ // debt-lib.mjs — generated by arbiter
2
+ // Shared metric collection helpers used by capture-debt-baseline.mjs and debt-report.mjs.
3
+ // Re-generate with: arbiter update
4
+ import { spawnSync } from 'node:child_process';
5
+ import {
6
+ readFileSync, existsSync, readdirSync, rmSync, mkdirSync, chmodSync, renameSync, statSync,
7
+ } from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
10
+ import { walkRepo } from './lib/glob-walk.mjs';
11
+
12
+ // The `eslint <paths> --format json` output for this repo exceeds the Node
13
+ // spawnSync default maxBuffer (1 MiB); without a raised ceiling the child aborts
14
+ // with ENOBUFS, stdout is silently truncated, the JSON fails to parse, and the
15
+ // metric collapses to 0 (the dormant complexityViolations ratchet root cause,
16
+ // #1542). 64 MiB comfortably covers the full-repo eslint/knip JSON reports.
17
+ const RUN_MAX_BUFFER = 64 * 1024 * 1024;
18
+
19
+ function run(cmd, args, opts) {
20
+ return spawnSync(cmd, args, {
21
+ encoding: 'utf-8',
22
+ shell: false,
23
+ maxBuffer: RUN_MAX_BUFFER,
24
+ ...opts,
25
+ });
26
+ }
27
+
28
+ // jscpd v5 ships its scanner as a prebuilt native `cpd` binary selected per
29
+ // platform by `jscpd/run-jscpd.js` (#1304). On glibc Linux it installs the
30
+ // `jscpd-linux-x64-gnu` package, but that binary is built against GLIBC ≥ 2.34;
31
+ // on an older CI image (glibc < 2.32) the loader rejects it — exit 1, no report
32
+ // — so the duplication scan failed closed even though jscpd "is installed". The
33
+ // musl x64 platform package is a *static-pie* ELF with no libc dependency, so
34
+ // it runs on any Linux regardless of glibc age. npm refuses to install a
35
+ // `libc:["musl"]` optional dep on a glibc host, so we materialise it on demand
36
+ // from the version-pinned registry into a local cache and run it directly.
37
+ // jscpd renamed its platform packages once already (`cpd-linux-x64-musl` ->
38
+ // `jscpd-linux-x64-musl` in 5.0.16, #2370), so the package name is derived from
39
+ // jscpd's own optionalDependencies rather than hardcoded.
40
+
41
+ /**
42
+ * The optionalDependencies key jscpd declares for the musl x64 Linux platform
43
+ * package. Derived instead of hardcoded because jscpd has already renamed this
44
+ * package once (#2370).
45
+ * @param {{optionalDependencies?: Record<string, string>}} jscpdPkgJson - parsed jscpd package.json
46
+ * @returns {string | null}
47
+ */
48
+ export function deriveStaticMuslPkgName(jscpdPkgJson) {
49
+ const optionalDeps = jscpdPkgJson?.optionalDependencies ?? {};
50
+ return Object.keys(optionalDeps).find((name) => name.endsWith('-linux-x64-musl')) ?? null;
51
+ }
52
+
53
+ /**
54
+ * Materialise jscpd's static (musl, libc-independent) `cpd` binary into a local
55
+ * cache and return its path, or null if unavailable (no network / not Linux-x64
56
+ * / npm absent). Idempotent: a cached binary is reused. The version is pinned to
57
+ * the installed jscpd so the static binary always matches the gnu one (#1304).
58
+ * @param {string} jscpdPkgDir - dir of the resolved jscpd package (has version)
59
+ * @returns {string | null}
60
+ */
61
+ function resolveStaticCpd(jscpdPkgDir) {
62
+ if (process.platform !== 'linux' || process.arch !== 'x64') return null;
63
+ let pkgJson;
64
+ try {
65
+ pkgJson = JSON.parse(readFileSync(join(jscpdPkgDir, 'package.json'), 'utf-8'));
66
+ } catch { return null; }
67
+ const version = pkgJson.version;
68
+ const staticPkgName = deriveStaticMuslPkgName(pkgJson);
69
+ if (!version || !staticPkgName) return null;
70
+ const cacheRoot = resolve(jscpdPkgDir, '..', '.cache', 'jscpd-static', version);
71
+ const cpdPath = join(cacheRoot, 'cpd');
72
+ if (existsSync(cpdPath)) return cpdPath;
73
+ // Fetch the version-pinned static package tarball and extract its binary.
74
+ let tmp;
75
+ try {
76
+ mkdirSync(cacheRoot, { recursive: true });
77
+ tmp = join(cacheRoot, '.pack');
78
+ mkdirSync(tmp, { recursive: true });
79
+ const pack = run('npm', ['pack', `${staticPkgName}@${version}`, '--silent'], { cwd: tmp });
80
+ if ((pack.status ?? 1) !== 0) return null;
81
+ const tgz = readdirSync(tmp).find((f) => f.endsWith('.tgz'));
82
+ if (!tgz) return null;
83
+ const untar = run('tar', ['xzf', tgz, '-C', tmp], { cwd: tmp });
84
+ if ((untar.status ?? 1) !== 0) return null;
85
+ // jscpd 5.0.16 ships the binary at package/bin/jscpd (was
86
+ // package/cpd-bin/cpd pre-rename, #2370). Try both so a consumer project
87
+ // pinned to an older jscpd (whose static package still uses the old
88
+ // layout) still resolves.
89
+ const extracted = [
90
+ join(tmp, 'package', 'bin', 'jscpd'),
91
+ join(tmp, 'package', 'cpd-bin', 'cpd'),
92
+ ].find((p) => existsSync(p));
93
+ if (!extracted) return null;
94
+ renameSync(extracted, cpdPath);
95
+ chmodSync(cpdPath, 0o755);
96
+ rmSync(tmp, { recursive: true, force: true });
97
+ return cpdPath;
98
+ } catch {
99
+ if (tmp) rmSync(tmp, { recursive: true, force: true });
100
+ return null;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * True when a jscpd spawn result is the glibc-too-old failure: the native gnu
106
+ * `cpd` binary loaded but the dynamic linker could not satisfy its GLIBC version
107
+ * requirement (exit 1, empty report, `GLIBC_x.yy not found` on stderr) (#1304).
108
+ * @param {{status: number|null, stderr?: string}} r
109
+ * @returns {boolean}
110
+ */
111
+ export function isGlibcIncompatible(r) {
112
+ return (r?.status ?? 0) !== 0 && /GLIBC_[\d.]+'? not found/.test(r?.stderr ?? '');
113
+ }
114
+
115
+ /**
116
+ * Resolve the default jscpd spawn (#1304). jscpd v5 runs a prebuilt native `cpd`
117
+ * binary chosen per platform by `jscpd/run-jscpd.js`. This resolves the installed
118
+ * jscpd launcher and runs it directly with the current node. On the docker-ci-build
119
+ * runner the glibc `cpd-linux-x64-gnu` binary is built against GLIBC ≥ 2.34 but the
120
+ * image's glibc is < 2.32, so the loader rejects it (exit 1, no report) and the scan
121
+ * failed closed even though jscpd is installed. On that glibc-too-old failure the
122
+ * spawn retries once with the static (musl, libc-independent) `cpd` — materialised
123
+ * on demand by resolveStaticCpd — so the scan actually runs and writes a report.
124
+ * If jscpd cannot be resolved at all (greenfield/baseline target without the
125
+ * devDependency) it falls back to the `npx --no-install jscpd` skip-spawn, whose
126
+ * missing-packages result drives the tool-not-installed (`{skipped:true}`) contract.
127
+ *
128
+ * The returned spawn accepts the SAME argv the caller built for the npx path
129
+ * (`['--no-install','jscpd',...paths,'--silent']`); the direct-binary path strips
130
+ * the npx-only `--no-install`/`jscpd` prefix tokens, the fallback passes them
131
+ * through. `resolveBin`, `resolveStatic`, and the per-call `spawnFn` are injectable
132
+ * so tests never shell to a real npx or native binary.
133
+ * @param {string} cwd
134
+ * @param {{resolveBin?: (cwd: string) => string, resolveStatic?: () => string | null}} [opts]
135
+ * @returns {(dir: string, args: string[], callOpts?: {spawnFn?: (cmd: string, args: string[], o: object) => {status: number|null, stdout: string, stderr: string}}) => ({status: number|null, stdout: string, stderr: string} | null)}
136
+ */
137
+ export function resolveJscpdSpawn(cwd, opts = {}) {
138
+ const resolveBin =
139
+ opts.resolveBin ??
140
+ ((dir) => {
141
+ // Resolve from the scanned project first (its node_modules), then fall
142
+ // back to this module's own require context — the self repo installs jscpd
143
+ // as a devDependency reachable from here even when the scan dir has no
144
+ // local node_modules (e.g. a temp fixture or a nested workspace).
145
+ let pkgPath;
146
+ try {
147
+ pkgPath = createRequire(join(resolve(dir), 'package.json')).resolve('jscpd/package.json');
148
+ } catch {
149
+ pkgPath = createRequire(import.meta.url).resolve('jscpd/package.json');
150
+ }
151
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
152
+ const binEntry = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.jscpd;
153
+ if (!binEntry) throw new Error('jscpd package.json has no bin.jscpd entry');
154
+ return join(dirname(pkgPath), binEntry);
155
+ });
156
+
157
+ let binPath;
158
+ try {
159
+ binPath = resolveBin(cwd);
160
+ } catch {
161
+ // jscpd not installed/resolvable — fall back to npx --no-install jscpd, whose
162
+ // ENOENT/"missing packages" result drives jscpdScan's skip contract.
163
+ return (dir, args, callOpts = {}) => {
164
+ const spawnFn = callOpts.spawnFn;
165
+ if (spawnFn) return spawnFn('npx', args, { cwd: dir });
166
+ const r = spawnOrSkip('duplicationPercentage', 'jscpd', 'npx', args, { cwd: dir });
167
+ return r === null ? null : r;
168
+ };
169
+ }
170
+
171
+ const jscpdPkgDir = dirname(binPath);
172
+ const resolveStatic = opts.resolveStatic ?? (() => resolveStaticCpd(jscpdPkgDir));
173
+ // Direct binary: drop the npx-only prefix tokens so jscpd sees only its own
174
+ // positional args (paths + flags). The launcher (run-jscpd.js) selects the
175
+ // platform-native `cpd`; if that binary is glibc-incompatible on this runner
176
+ // (#1304) retry once with the static (musl, libc-independent) `cpd` so the
177
+ // scan ACTUALLY runs and writes a report — fail-closed stays intact when even
178
+ // the static binary is unavailable (the error path is unchanged).
179
+ return (dir, args, callOpts = {}) => {
180
+ const jscpdArgs = args.filter((a) => a !== '--no-install' && a !== 'jscpd');
181
+ const spawnFn = callOpts.spawnFn ?? ((cmd, a, o) => run(cmd, a, o));
182
+ const r = spawnFn(process.execPath, [binPath, ...jscpdArgs], { cwd: dir });
183
+ if (!isGlibcIncompatible(r)) return r;
184
+ const staticCpd = resolveStatic();
185
+ if (!staticCpd) return r;
186
+ return spawnFn(staticCpd, jscpdArgs, { cwd: dir });
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Run a command. If the binary is not installed (ENOENT), log a warning to
192
+ * stderr and return null. Callers skip null-result metrics entirely.
193
+ * @param {string} name - metric key for the warning message
194
+ * @param {string} tool - human name of the missing tool
195
+ * @param {string} cmd - executable
196
+ * @param {string[]} args
197
+ * @param {object} [opts]
198
+ * @returns {import('child_process').SpawnSyncReturns<string> | null}
199
+ */
200
+ /**
201
+ * Whether npx declined to run because its requested package is unavailable.
202
+ * This is the npx equivalent of an ENOENT spawn failure: no measurement was
203
+ * attempted, so callers must omit the metric rather than fabricate a value or
204
+ * report a failed collection.
205
+ * @param {import('child_process').SpawnSyncReturns<string>} result
206
+ * @returns {boolean}
207
+ */
208
+ export function isNpxMissingPackages(result) {
209
+ return (result.status ?? 0) !== 0 && /npx canceled due to missing packages/.test(result.stderr ?? '');
210
+ }
211
+
212
+ export function spawnOrSkip(name, tool, cmd, args, opts) {
213
+ const r = run(cmd, args, opts ?? {});
214
+ if (r.error?.code === 'ENOENT' || isNpxMissingPackages(r)) {
215
+ process.stderr.write(`[baseline] skip ${name} — ${tool} not installed\n`);
216
+ return null;
217
+ }
218
+ return r;
219
+ }
220
+
221
+ /**
222
+ * Count TO-DO markers across common source file extensions in cwd.
223
+ * Uses walkRepo (glob-walk.mjs) instead of raw `grep -r .` so nested worktree
224
+ * checkouts (e.g. `.claude/worktrees/**`) are never double-counted (#1734/#1752).
225
+ * @param {string} cwd
226
+ * @returns {number}
227
+ */
228
+ export function countTodos(cwd) {
229
+ const MARKER_EXTENSIONS = new Set(['.js', '.ts', '.java', '.go', '.py', '.rs']);
230
+ const EXTRA_SKIP_SEGMENTS = new Set(['target', '.arbiter', '.temp']);
231
+ // arbiter-suppress(INV-21, until=2099-01-01, reason="self-referential marker regex inside the TODO-counting collector itself, not an unresolved TODO", owner=@luca)
232
+ const MARKER_RE = /\bTODO\b/;
233
+ let count = 0;
234
+ for (const rel of walkRepo(cwd)) {
235
+ const dot = rel.lastIndexOf('.');
236
+ if (dot === -1 || !MARKER_EXTENSIONS.has(rel.slice(dot))) continue;
237
+ if (rel.split('/').some((seg) => EXTRA_SKIP_SEGMENTS.has(seg))) continue;
238
+ try {
239
+ const lines = readFileSync(resolve(cwd, rel), 'utf-8').split('\n');
240
+ count += lines.filter((l) => MARKER_RE.test(l)).length;
241
+ } catch { /* skip unreadable files */ }
242
+ }
243
+ return count;
244
+ }
245
+
246
+ /**
247
+ * Return the short git commit hash for HEAD, or 'unknown'.
248
+ * @param {string} cwd
249
+ * @returns {string}
250
+ */
251
+ export function getCommit(cwd) {
252
+ const r = run('git', ['rev-parse', '--short', 'HEAD'], { cwd });
253
+ return r.status === 0 ? r.stdout.trim() : 'unknown';
254
+ }
255
+
256
+ // ── Finding-hygiene metrics (#1405) ──────────────────────────────────────────
257
+ // The incidental-findings spool (`.arbiter/findings/*.jsonl`, #1401) is the SSOT
258
+ // for un-promoted findings. These metrics make the debt ratchet reward DRAINING
259
+ // the spool (lower-is-better) and never let merely FILING findings inflate a
260
+ // green signal (anti-gaming, INV-114). Spool ABSENT → both metrics are OMITTED
261
+ // (treated as not-applicable / "missing tool"), NEVER recorded as a regression.
262
+
263
+ /** Relative path of the per-shard findings spool directory (#1401). */
264
+ const FINDINGS_DIR = '.arbiter/findings';
265
+
266
+ /**
267
+ * Read the findings spool and return open (un-promoted) finding metrics.
268
+ * Returns `{}` when the spool directory is absent (metric omitted = NA, never a
269
+ * regression). When present:
270
+ * - openFindingsCount — distinct fingerprints across all shards (dedup-safe)
271
+ * - unpromotedFindingsAge — age in whole days of the OLDEST open finding (0 when drained)
272
+ * Malformed JSONL lines are skipped; a missing/empty spool yields count 0, age 0.
273
+ * @param {string} cwd
274
+ * @returns {Record<string, {value: number, unit: string, direction: string}>}
275
+ */
276
+ export function collectFindingsMetrics(cwd) {
277
+ const findingsDir = resolve(cwd, FINDINGS_DIR);
278
+ if (!existsSync(findingsDir)) return {};
279
+ let shards;
280
+ try {
281
+ shards = readdirSync(findingsDir).filter((f) => f.endsWith('.jsonl'));
282
+ } catch {
283
+ return {};
284
+ }
285
+ const fingerprints = new Set();
286
+ let oldestTs = null;
287
+ for (const shard of shards) {
288
+ let text;
289
+ try {
290
+ text = readFileSync(join(findingsDir, shard), 'utf-8');
291
+ } catch {
292
+ continue;
293
+ }
294
+ for (const line of text.split('\n')) {
295
+ const trimmed = line.trim();
296
+ if (trimmed.length === 0) continue;
297
+ let entry;
298
+ try {
299
+ entry = JSON.parse(trimmed);
300
+ } catch {
301
+ continue; // malformed line — resilient skip
302
+ }
303
+ if (entry === null || typeof entry !== 'object') continue;
304
+ const fp = typeof entry.fingerprint === 'string' ? entry.fingerprint : trimmed;
305
+ fingerprints.add(fp);
306
+ const ts = typeof entry.ts === 'string' ? Date.parse(entry.ts) : NaN;
307
+ if (!Number.isNaN(ts) && (oldestTs === null || ts < oldestTs)) oldestTs = ts;
308
+ }
309
+ }
310
+ const ageDays = oldestTs === null ? 0 : Math.floor((Date.now() - oldestTs) / (24 * 60 * 60 * 1000));
311
+ return {
312
+ openFindingsCount: { value: fingerprints.size, unit: 'count', direction: 'lower-is-better' },
313
+ unpromotedFindingsAge: { value: ageDays < 0 ? 0 : ageDays, unit: 'days', direction: 'lower-is-better' },
314
+ };
315
+ }
316
+
317
+ /**
318
+ * Baseline key-drop guard (#1286): a recapture must never silently delete a
319
+ * metric that the prior baseline tracked (e.g. a jscpd hiccup during capture
320
+ * would otherwise remove duplicationPercentage and disable the ratchet).
321
+ * New keys are always allowed.
322
+ * @param {Record<string, unknown>} priorMetrics
323
+ * @param {Record<string, unknown>} nextMetrics
324
+ */
325
+ export function assertKeyParity(priorMetrics, nextMetrics) {
326
+ const dropped = Object.keys(priorMetrics ?? {}).filter((k) => !(k in (nextMetrics ?? {})));
327
+ if (dropped.length > 0) {
328
+ throw new Error(
329
+ `baseline recapture would drop metric(s): ${dropped.join(', ')} — ` +
330
+ 'refusing to write (fail-closed). Fix the collector or remove the key deliberately.',
331
+ );
332
+ }
333
+ }
334
+
335
+ // ─── Per-stack metric collectors ─────────────────────────────────────────────
336
+ // Each language block exports a single `collectMetrics(cwd)` function.
337
+ // EJS conditionals select the correct block at generation time.
338
+
339
+ <% if (language === 'typescript') { %>
340
+ /**
341
+ * Fail-closed jscpd v5 scan (#1286). jscpd v5 silently ignores the config
342
+ * `pattern`/`path` keys and exits 0 on a 0-file scan, writing a 0% report —
343
+ * so the fileset MUST be passed as positional CLI args. `.jscpd.json#path`
344
+ * is the script-private SSOT for that fileset (jscpd itself disregards the
345
+ * key; this helper and check-duplication.mjs read it).
346
+ * A scan that ran but covered 0 sources is an ERROR, never a recorded 0%.
347
+ * @param {string} cwd
348
+ * @param {{spawn?: (cwd: string, args: string[]) => {status: number|null, stdout: string, stderr: string}}} [opts]
349
+ * spawn is injectable for tests; production default shells `npx --no-install jscpd`.
350
+ * @returns {{error?: string, skipped?: boolean, legacyNoReport?: boolean, status?: number, sources?: number, percentage?: number}}
351
+ */
352
+ export function jscpdScan(cwd, opts = {}) {
353
+ const configPath = resolve(cwd, '.jscpd.json');
354
+ let cfg;
355
+ try {
356
+ cfg = JSON.parse(readFileSync(configPath, 'utf-8'));
357
+ } catch (err) {
358
+ return { error: `.jscpd.json unreadable at ${configPath}: ${err.message}` };
359
+ }
360
+ const paths = Array.isArray(cfg.path) ? cfg.path.filter((p) => typeof p === 'string' && p) : [];
361
+ if (paths.length === 0) {
362
+ return {
363
+ error: cfg.pattern
364
+ ? 'legacy v4 .jscpd.json: `pattern` is silently ignored by jscpd v5 — ' +
365
+ 'replace it with a "path" array (the scan fileset, passed as positional args). See #1286.'
366
+ : '.jscpd.json has no "path" array — required as the jscpd v5 fileset SSOT (#1286).',
367
+ };
368
+ }
369
+ // Without the json reporter, v5 exits 0 writing no report — indistinguishable
370
+ // from the v4 zero-clones case, so exit-0-plus-no-report could no longer be
371
+ // trusted as a v4 signal. Reject the config up front (fail-closed, #1286).
372
+ if (!Array.isArray(cfg.reporters) || !cfg.reporters.includes('json')) {
373
+ return { error: '.jscpd.json "reporters" must include "json" — the scan is parsed from report/jscpd-report.json (#1286).' };
374
+ }
375
+ // Honor a custom `output` dir; default matches jscpd's `report/`.
376
+ const reportPath = resolve(cwd, typeof cfg.output === 'string' ? cfg.output : 'report', 'jscpd-report.json');
377
+ // Stale-report poisoning guard: a leftover report from a previous run must
378
+ // never be parsed as if it were produced by this invocation.
379
+ rmSync(reportPath, { force: true });
380
+ const spawn = opts.spawn ?? resolveJscpdSpawn(cwd);
381
+ const r = spawn(cwd, ['--no-install', 'jscpd', ...paths, '--silent']);
382
+ if (r === null) return { skipped: true };
383
+ // `npx --no-install` exits 1 (not ENOENT) when the package is absent — that is
384
+ // the tool-not-installed case (greenfield target without jscpd), same contract
385
+ // as spawnOrSkip: skip the collector, never a failed-scan error.
386
+ if (isNpxMissingPackages(r)) {
387
+ process.stderr.write('[baseline] skip duplicationPercentage — jscpd not installed\n');
388
+ return { skipped: true };
389
+ }
390
+ if (!existsSync(reportPath)) {
391
+ if ((r.status ?? 0) === 0) {
392
+ // jscpd v4 writes the json report only when clones exist; v5 always
393
+ // writes it. Exit 0 + no report can only be a v4 binary (brownfield
394
+ // project not yet bumped) with zero clones.
395
+ return { legacyNoReport: true, status: 0 };
396
+ }
397
+ // Surface jscpd's own stderr so the cause is visible in the gate output
398
+ // (e.g. a glibc-too-old runner image, #1304) instead of an opaque exit code.
399
+ const why = (r.stderr ?? '').trim().split('\n').slice(0, 3).join(' | ');
400
+ return {
401
+ error:
402
+ `jscpd failed (exit ${r.status}) and wrote no report — fail-closed (#1286)` +
403
+ (why ? `: ${why}` : ''),
404
+ };
405
+ }
406
+ let dup;
407
+ try {
408
+ dup = JSON.parse(readFileSync(reportPath, 'utf-8'));
409
+ } catch (err) {
410
+ return { error: `jscpd report unreadable: ${err.message}` };
411
+ }
412
+ const total = dup.statistics?.total ?? {};
413
+ const sources = total.sources ?? 0;
414
+ if (sources === 0) {
415
+ return {
416
+ error:
417
+ 'jscpd scanned 0 source files — fileset/config drift; refusing to record 0% ' +
418
+ '(fail-closed, CANON-22 / #1286). Check .jscpd.json "path" entries exist.',
419
+ status: r.status ?? 0,
420
+ };
421
+ }
422
+ if (typeof total.percentage !== 'number') {
423
+ return {
424
+ error:
425
+ 'jscpd report has no numeric statistics.total.percentage — schema drift; refusing to record 0% (#1286).',
426
+ };
427
+ }
428
+ return { status: r.status ?? 0, sources, percentage: total.percentage };
429
+ }
430
+
431
+ /**
432
+ * Enriched vitest-coverage failure diagnostic (#2226). The bare ENOENT named no
433
+ * cause: vitest exits 1 for many reasons (a failing test, a missing provider, a
434
+ * broken reporter) and writes no json-summary for any of them. This names the
435
+ * cause from the run's OWN output:
436
+ * - stdout (vitest prints its version on the RUN line and the Failed Tests
437
+ * summary at the end) — the stderr-only tail misled wave-1, it can be a
438
+ * PASSING test's incidental stderr;
439
+ * - whether .coverage-tmp/ was created at all (stale-guard vs never-ran);
440
+ * - exit status/signal.
441
+ * @param {unknown} err - error thrown while reading/parsing the summary
442
+ * @param {{status: number|null, signal?: string, stdout?: string, stderr?: string}} raw
443
+ * the spawned vitest result
444
+ * @param {string} cwd - project root (probes .coverage-tmp/)
445
+ * @returns {string}
446
+ */
447
+ // eslint-disable-next-line no-control-regex -- strips ANSI SGR codes (\x1b[...m)
448
+ const ANSI_SGR = /\x1b\[[0-9;]*m/g;
449
+
450
+ function coverageFailureReason(err, raw, cwd) {
451
+ const clean = (s = '') => s.replace(ANSI_SGR, '');
452
+ const lines = (s) => clean(s).split('\n').filter(Boolean);
453
+ const stdoutTail = lines(raw.stdout).slice(-3).join(' | ');
454
+ const stderrTail = lines(raw.stderr).slice(-3).join(' | ');
455
+ const outcome = [
456
+ raw.status !== null ? `exit ${raw.status}` : '',
457
+ raw.signal ? `signal ${raw.signal}` : '',
458
+ ].filter(Boolean).join(', ');
459
+ const version =
460
+ lines(raw.stdout).find((l) => /^RUN\s+v\d+\.\d+\.\d+/.test(l))?.trim() ?? 'vitest version unknown';
461
+ const tmpDir = resolve(cwd, '.coverage-tmp');
462
+ const tmpState = existsSync(tmpDir)
463
+ ? existsSync(join(tmpDir, 'coverage-summary.json'))
464
+ ? '.coverage-tmp created, summary present'
465
+ : '.coverage-tmp created, no summary'
466
+ : '.coverage-tmp NOT created';
467
+ return [
468
+ `vitest coverage summary unreadable: ${err instanceof Error ? err.message : String(err)}`,
469
+ outcome,
470
+ version,
471
+ tmpState,
472
+ stdoutTail ? `stdout: ${stdoutTail}` : '',
473
+ stderrTail ? `stderr: ${stderrTail}` : '',
474
+ ].filter(Boolean).join(' — ');
475
+ }
476
+
477
+ function readReusedCoverageSummary(cwd, opts) {
478
+ const rawPath = opts.coverageSummaryPath;
479
+ if (typeof rawPath !== 'string' || rawPath.length === 0) {
480
+ throw new Error('coverage summary path is missing');
481
+ }
482
+ const summaryPath = resolve(cwd, rawPath);
483
+ if (isAbsolute(rawPath) || relative(cwd, summaryPath).startsWith('..')) {
484
+ throw new Error('coverage summary path must stay inside the project root');
485
+ }
486
+ const stat = statSync(summaryPath);
487
+ if (!stat.isFile()) throw new Error(`coverage summary is not a file: ${summaryPath}`);
488
+ if (opts.coverageStartedAt !== undefined) {
489
+ if (!Number.isFinite(opts.coverageStartedAt)) {
490
+ throw new Error('coverage summary freshness timestamp is invalid');
491
+ }
492
+ if (stat.mtimeMs < opts.coverageStartedAt) {
493
+ throw new Error(`coverage summary is stale: ${summaryPath}`);
494
+ }
495
+ }
496
+ return JSON.parse(readFileSync(summaryPath, 'utf-8'));
497
+ }
498
+
499
+ function coverageMetricsFromSummary(summary) {
500
+ if (typeof summary.total?.lines?.pct !== 'number') {
501
+ throw new Error('coverage summary has no numeric total.lines.pct');
502
+ }
503
+ return {
504
+ coverageLine: { value: summary.total.lines.pct, unit: 'percent', direction: 'higher-is-better' },
505
+ <% if (metricsProfile.includeBranchCoverage) { %>
506
+ coverageBranch: { value: summary.total.branches?.pct ?? 0, unit: 'percent', direction: 'higher-is-better' },
507
+ <% } %>
508
+ };
509
+ }
510
+
511
+ /**
512
+ * Collect TypeScript/JavaScript debt metrics.
513
+ * @param {string} cwd
514
+ * @param {Array<{metric: string, reason: string}>} [collectionErrors]
515
+ * Sink for tool-ran-but-collection-failed events (distinct from
516
+ * tool-not-installed, which soft-skips). debt-report --gate hard-fails on
517
+ * any entry (#1286, fail-closed).
518
+ * @param {{spawnCoverage?: (cwd: string, args: string[]) => {status: number|null, stdout: string, stderr: string} | null, coverageSummaryPath?: string, coverageStartedAt?: number}} [opts]
519
+ * spawnCoverage is injectable for tests, mirroring jscpdScan's opts.spawn
520
+ * seam: it replaces the `npx vitest run --coverage` invocation so a unit test
521
+ * can deterministically simulate "vitest ran, no summary written" (the
522
+ * CI-only ENOENT of #2226). Return null to soft-skip (tool-not-installed).
523
+ * @returns {Record<string, {value: number, unit: string, direction: string}>}
524
+ */
525
+ export function collectMetrics(cwd, collectionErrors = [], opts = {}) {
526
+ const metrics = {};
527
+
528
+ // ── Coverage (vitest json-summary reporter) ───────────────────────────────
529
+ if (opts.coverageSummaryPath !== undefined) {
530
+ try {
531
+ Object.assign(metrics, coverageMetricsFromSummary(readReusedCoverageSummary(cwd, opts)));
532
+ } catch (err) {
533
+ const reason = `gate coverage summary unreadable: ${err instanceof Error ? err.message : String(err)}`;
534
+ collectionErrors.push({ metric: 'coverageLine', reason });
535
+ process.stderr.write(`[baseline] ERROR: ${reason}\n`);
536
+ }
537
+ } else {
538
+ // Stale-report poisoning guard, mirroring jscpdScan's reportPath guard above: a leftover .coverage-tmp from a previous run (this box's self-hosted CI workdir persists between jobs) must never be read as this run's output.
539
+ rmSync(resolve(cwd, '.coverage-tmp'), { recursive: true, force: true });
540
+ const spawnCoverage =
541
+ opts.spawnCoverage ??
542
+ ((dir, args) => spawnOrSkip('coverageLine', 'vitest', 'npx', args, { cwd: dir }));
543
+ const coverageRaw = spawnCoverage(cwd,
544
+ ['vitest', 'run', '--coverage', '--coverage.reporter=json-summary', '--coverage.reportsDirectory=.coverage-tmp']);
545
+ if (coverageRaw !== null) {
546
+ try {
547
+ const summary = JSON.parse(readFileSync(resolve(cwd, '.coverage-tmp/coverage-summary.json'), 'utf-8'));
548
+ Object.assign(metrics, coverageMetricsFromSummary(summary));
549
+ } catch (err) {
550
+ const reason = coverageFailureReason(err, coverageRaw, cwd);
551
+ collectionErrors.push({ metric: 'coverageLine', reason });
552
+ process.stderr.write(`[baseline] ERROR: ${reason}\n`);
553
+ }
554
+ }
555
+ }
556
+
557
+ // ── ESLint errors (severity 2) ────────────────────────────────────────────
558
+ const eslintRaw = spawnOrSkip('eslintErrors', 'eslint', 'npx', ['eslint', 'src', '--format', 'json'], { cwd });
559
+ if (eslintRaw !== null) {
560
+ try {
561
+ const report = JSON.parse(eslintRaw.stdout || '[]');
562
+ metrics.eslintErrors = {
563
+ value: report.flatMap(f => f.messages).filter(m => m.severity === 2).length,
564
+ unit: 'count', direction: 'lower-is-better',
565
+ };
566
+ } catch (err) {
567
+ const reason = `eslint report unreadable: ${err instanceof Error ? err.message : String(err)}`;
568
+ collectionErrors.push({ metric: 'eslintErrors', reason });
569
+ process.stderr.write(`[baseline] ERROR: ${reason}\n`);
570
+ }
571
+ }
572
+
573
+ // ── TSC strict errors ─────────────────────────────────────────────────────
574
+ const tscRaw = spawnOrSkip('tscStrictErrors', 'tsc', 'npx', ['tsc', '--noEmit'], { cwd });
575
+ if (tscRaw !== null) {
576
+ const output = (tscRaw.stdout ?? '') + (tscRaw.stderr ?? '');
577
+ metrics.tscStrictErrors = {
578
+ value: output.split('\n').filter(l => l.includes('error TS')).length,
579
+ unit: 'count', direction: 'lower-is-better',
580
+ };
581
+ }
582
+
583
+ // ── Complexity violations (eslint rule) ───────────────────────────────────
584
+ // Scan `scripts` alongside `src`: the gate-enforcement layer is the
585
+ // highest-leverage code in the repo and was previously exempt from the
586
+ // complexity ratchet (#1523/#1542). The baseline grandfathers the current
587
+ // offenders; the ratchet blocks any net increase and admits gradual burn-down.
588
+ const eslintComplexRaw = spawnOrSkip('complexityViolations', 'eslint', 'npx',
589
+ ['eslint', 'src', 'scripts', '--format', 'json', '--rule', '{"complexity":["warn",10]}'], { cwd });
590
+ if (eslintComplexRaw !== null) {
591
+ try {
592
+ const report = JSON.parse(eslintComplexRaw.stdout || '[]');
593
+ metrics.complexityViolations = {
594
+ value: report.flatMap(f => f.messages).filter(m => m.ruleId === 'complexity').length,
595
+ unit: 'count', direction: 'lower-is-better',
596
+ };
597
+ } catch (err) {
598
+ const reason = `eslint complexity report unreadable: ${err instanceof Error ? err.message : String(err)}`;
599
+ collectionErrors.push({ metric: 'complexityViolations', reason });
600
+ process.stderr.write(`[baseline] ERROR: ${reason}\n`);
601
+ }
602
+ }
603
+
604
+ // ── Dead code (knip) ──────────────────────────────────────────────────────
605
+ const knipRaw = spawnOrSkip('deadCode', 'knip', 'npx', ['knip', '--reporter', 'json'], { cwd });
606
+ if (knipRaw !== null) {
607
+ try {
608
+ const report = JSON.parse(knipRaw.stdout || '{}');
609
+ metrics.deadCode = {
610
+ value: (report.files ?? []).length + (report.exports ?? []).length,
611
+ unit: 'count', direction: 'lower-is-better',
612
+ };
613
+ } catch (err) {
614
+ const reason = `knip report unreadable: ${err instanceof Error ? err.message : String(err)}`;
615
+ collectionErrors.push({ metric: 'deadCode', reason });
616
+ process.stderr.write(`[baseline] ERROR: ${reason}\n`);
617
+ }
618
+ }
619
+
620
+ // ── Duplication (jscpd) — CANON-22 DRY ratchet (Lehman entropy) ────────────
621
+ // jscpdScan handles the v5 fileset contract (positional paths from
622
+ // .jscpd.json#path, stale-report cleanup, 0-source rejection). A scan that
623
+ // ran but failed is surfaced via collectionErrors so debt-report --gate can
624
+ // hard-fail instead of soft-skipping as "missing tool" (#1286, fail-closed).
625
+ {
626
+ const scan = jscpdScan(cwd);
627
+ if (scan.skipped) {
628
+ // npx/tooling absent on this machine — genuine soft-skip (ENOENT path).
629
+ } else if (scan.error) {
630
+ collectionErrors.push({ metric: 'duplicationPercentage', reason: scan.error });
631
+ process.stderr.write(`[baseline] ERROR: ${scan.error}\n`);
632
+ } else if (scan.legacyNoReport) {
633
+ // v4 binary, zero clones (v5 always writes the report) — genuinely 0%.
634
+ metrics.duplicationPercentage = { value: 0, unit: 'percent', direction: 'lower-is-better' };
635
+ } else {
636
+ metrics.duplicationPercentage = {
637
+ value: scan.percentage,
638
+ unit: 'percent', direction: 'lower-is-better',
639
+ };
640
+ }
641
+ }
642
+
643
+ <% if (metricsProfile.includeBundleSize) { %>
644
+ // ── Bundle size (frontend-spa only) ───────────────────────────────────────
645
+ if (existsSync(resolve(cwd, 'dist'))) {
646
+ const duRaw = run('du', ['-sb', 'dist/'], { cwd });
647
+ if (!duRaw.error && duRaw.status === 0) {
648
+ const match = duRaw.stdout.match(/^(\d+)/);
649
+ if (match) {
650
+ metrics.bundleSizeKb = {
651
+ value: Math.round(parseInt(match[1]) / 1024),
652
+ unit: 'kb', direction: 'lower-is-better',
653
+ };
654
+ }
655
+ }
656
+ }
657
+ <% } %>
658
+ <% if (metricsProfile.includePublicApiSurface) { %>
659
+ // ── Public API surface (library archetype — count of top-level exports) ───
660
+ {
661
+ if (existsSync(resolve(cwd, 'src'))) {
662
+ const grepOut = run('grep', ['-rh', '--include=*.ts', '--include=*.mts', '^export', 'src/'], {
663
+ cwd,
664
+ });
665
+ if (grepOut.error || (grepOut.status !== 0 && grepOut.status !== 1)) {
666
+ const reason = `public API grep failed${grepOut.error?.message ? `: ${grepOut.error.message}` : ` (exit ${grepOut.status})`}`;
667
+ collectionErrors.push({ metric: 'publicApiSurface', reason });
668
+ process.stderr.write(`[baseline] ERROR: ${reason}\n`);
669
+ } else {
670
+ metrics.publicApiSurface = {
671
+ value: grepOut.stdout ? grepOut.stdout.split('\n').filter(Boolean).length : 0,
672
+ unit: 'count', direction: 'lower-is-better',
673
+ };
674
+ }
675
+ }
676
+ }
677
+ <% } %>
678
+
679
+ // ── Finding-hygiene (un-promoted findings spool, #1405) ───────────────────
680
+ // Language-agnostic: reads `.arbiter/findings/*.jsonl`. Spool absent → omitted
681
+ // (NA), so a project that never filed a finding is not penalised. Rewards
682
+ // DRAINING (lower-is-better); filing more findings is a regression (anti-gaming).
683
+ Object.assign(metrics, collectFindingsMetrics(cwd));
684
+
685
+ return metrics;
686
+ }
687
+ <% } else if (language === 'rust') { %>
688
+ /**
689
+ * Collect Rust debt metrics.
690
+ * @param {string} cwd
691
+ * @returns {Record<string, {value: number, unit: string, direction: string}>}
692
+ */
693
+ export function collectMetrics(cwd) {
694
+ const metrics = {};
695
+
696
+ // ── Coverage (cargo tarpaulin) ────────────────────────────────────────────
697
+ run('cargo', ['tarpaulin', '--out', 'Json', '--output-dir', '.coverage-tmp'], { cwd });
698
+ try {
699
+ const data = JSON.parse(readFileSync(resolve(cwd, '.coverage-tmp/tarpaulin-report.json'), 'utf-8'));
700
+ metrics.coverageLine = { value: data.coverage, unit: 'percent', direction: 'higher-is-better' };
701
+ } catch { metrics.coverageLine = { value: 0, unit: 'percent', direction: 'higher-is-better' }; }
702
+
703
+ // ── Clippy errors + complexity + dead code ────────────────────────────────
704
+ const clippyRaw = spawnOrSkip('clippyDenyCount', 'cargo', 'cargo',
705
+ ['clippy', '--message-format=json', '--', '-W', 'clippy::cognitive_complexity', '-W', 'dead_code'], { cwd });
706
+ if (clippyRaw !== null) {
707
+ try {
708
+ const lines = (clippyRaw.stdout ?? '').split('\n').filter(Boolean);
709
+ const msgs = lines.map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
710
+ metrics.clippyDenyCount = {
711
+ value: msgs.filter(m => m.reason === 'compiler-message' && m.message?.level === 'error').length,
712
+ unit: 'count', direction: 'lower-is-better',
713
+ };
714
+ metrics.complexityViolations = {
715
+ value: msgs.filter(m => m.reason === 'compiler-message' && m.message?.code?.code?.includes('cognitive_complexity')).length,
716
+ unit: 'count', direction: 'lower-is-better',
717
+ };
718
+ metrics.deadCode = {
719
+ value: msgs.filter(m => m.reason === 'compiler-message' && m.message?.code?.code === 'dead_code').length,
720
+ unit: 'count', direction: 'lower-is-better',
721
+ };
722
+ } catch {
723
+ metrics.clippyDenyCount = { value: 0, unit: 'count', direction: 'lower-is-better' };
724
+ metrics.complexityViolations = { value: 0, unit: 'count', direction: 'lower-is-better' };
725
+ metrics.deadCode = { value: 0, unit: 'count', direction: 'lower-is-better' };
726
+ }
727
+ }
728
+
729
+ // ── cargo audit advisories ────────────────────────────────────────────────
730
+ const auditRaw = spawnOrSkip('cargoAuditAdvisories', 'cargo-audit', 'cargo', ['audit', '--json'], { cwd });
731
+ if (auditRaw !== null) {
732
+ try {
733
+ const report = JSON.parse(auditRaw.stdout || '{}');
734
+ metrics.cargoAuditAdvisories = {
735
+ value: report.vulnerabilities?.found ?? 0,
736
+ unit: 'count', direction: 'lower-is-better',
737
+ };
738
+ } catch { metrics.cargoAuditAdvisories = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
739
+ }
740
+
741
+ // ── Finding-hygiene (un-promoted findings spool, #1405) ───────────────────
742
+ Object.assign(metrics, collectFindingsMetrics(cwd));
743
+
744
+ return metrics;
745
+ }
746
+ <% } else if (language === 'java') { %>
747
+ /**
748
+ * Collect Java debt metrics.
749
+ * @param {string} cwd
750
+ * @returns {Record<string, {value: number, unit: string, direction: string}>}
751
+ */
752
+ export function collectMetrics(cwd) {
753
+ const metrics = {};
754
+ const gradleOrMvn = '<%= buildTool === "gradle" ? "./gradlew" : "mvn" %>';
755
+ const pmdTask = '<%= buildTool === "gradle" ? "pmdMain" : "pmd:pmd" %>';
756
+ const checkstyleTask = '<%= buildTool === "gradle" ? "checkstyleMain" : "checkstyle:checkstyle" %>';
757
+
758
+ // ── JaCoCo line coverage ──────────────────────────────────────────────────
759
+ try {
760
+ const report = readFileSync(resolve(cwd, 'build/reports/jacoco/test/jacocoTestReport.xml'), 'utf-8');
761
+ const lineMatch = report.match(/type="LINE"[^/]*missed="(\d+)"[^/]*covered="(\d+)"/);
762
+ if (lineMatch) {
763
+ const missed = parseInt(lineMatch[1]), covered = parseInt(lineMatch[2]);
764
+ const pct = covered + missed > 0 ? (covered / (covered + missed)) * 100 : 0;
765
+ metrics.coverageLine = { value: Math.round(pct * 10) / 10, unit: 'percent', direction: 'higher-is-better' };
766
+ } else { metrics.coverageLine = { value: 0, unit: 'percent', direction: 'higher-is-better' }; }
767
+ <% if (metricsProfile.includeBranchCoverage) { %>
768
+ const branchMatch = report.match(/type="BRANCH"[^/]*missed="(\d+)"[^/]*covered="(\d+)"/);
769
+ if (branchMatch) {
770
+ const missed = parseInt(branchMatch[1]), covered = parseInt(branchMatch[2]);
771
+ const pct = covered + missed > 0 ? (covered / (covered + missed)) * 100 : 0;
772
+ metrics.coverageBranch = { value: Math.round(pct * 10) / 10, unit: 'percent', direction: 'higher-is-better' };
773
+ }
774
+ <% } %>
775
+ } catch { metrics.coverageLine = { value: 0, unit: 'percent', direction: 'higher-is-better' }; }
776
+
777
+ // ── PMD violations ────────────────────────────────────────────────────────
778
+ const pmdBuildRaw = spawnOrSkip('pmdViolations', gradleOrMvn, gradleOrMvn, [pmdTask, '-q'], { cwd });
779
+ if (pmdBuildRaw !== null) {
780
+ try {
781
+ const pmdXmlPaths = [
782
+ resolve(cwd, 'build/reports/pmd/main.xml'),
783
+ resolve(cwd, 'target/pmd.xml'),
784
+ ];
785
+ let found = false;
786
+ for (const p of pmdXmlPaths) {
787
+ if (existsSync(p)) {
788
+ const xml = readFileSync(p, 'utf-8');
789
+ const matches = xml.match(/<violation\b/g);
790
+ metrics.pmdViolations = { value: matches ? matches.length : 0, unit: 'count', direction: 'lower-is-better' };
791
+ found = true; break;
792
+ }
793
+ }
794
+ if (!found) metrics.pmdViolations = { value: 0, unit: 'count', direction: 'lower-is-better' };
795
+ } catch { metrics.pmdViolations = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
796
+ }
797
+
798
+ // ── Checkstyle violations ─────────────────────────────────────────────────
799
+ const csBuildRaw = spawnOrSkip('checkstyleViolations', gradleOrMvn, gradleOrMvn, [checkstyleTask, '-q'], { cwd });
800
+ if (csBuildRaw !== null) {
801
+ try {
802
+ const csXmlPaths = [
803
+ resolve(cwd, 'build/reports/checkstyle/main.xml'),
804
+ resolve(cwd, 'target/checkstyle-result.xml'),
805
+ ];
806
+ let found = false;
807
+ for (const p of csXmlPaths) {
808
+ if (existsSync(p)) {
809
+ const xml = readFileSync(p, 'utf-8');
810
+ const matches = xml.match(/<error\b/g);
811
+ metrics.checkstyleViolations = { value: matches ? matches.length : 0, unit: 'count', direction: 'lower-is-better' };
812
+ found = true; break;
813
+ }
814
+ }
815
+ if (!found) metrics.checkstyleViolations = { value: 0, unit: 'count', direction: 'lower-is-better' };
816
+ } catch { metrics.checkstyleViolations = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
817
+ }
818
+
819
+ <% if (metricsProfile.spotbugsEnabled) { %>
820
+ // ── SpotBugs violations ───────────────────────────────────────────────────
821
+ const sbBuildRaw = spawnOrSkip('spotbugsViolations', gradleOrMvn, gradleOrMvn, ['spotbugsMain', '-q'], { cwd });
822
+ if (sbBuildRaw !== null) {
823
+ try {
824
+ const sbXmlPaths = [
825
+ resolve(cwd, 'build/reports/spotbugs/main.xml'),
826
+ resolve(cwd, 'build/reports/spotbugs/spotbugsMain.xml'),
827
+ ];
828
+ let found = false;
829
+ for (const p of sbXmlPaths) {
830
+ if (existsSync(p)) {
831
+ const xml = readFileSync(p, 'utf-8');
832
+ const matches = xml.match(/<BugInstance\b/g);
833
+ metrics.spotbugsViolations = { value: matches ? matches.length : 0, unit: 'count', direction: 'lower-is-better' };
834
+ found = true; break;
835
+ }
836
+ }
837
+ if (!found) metrics.spotbugsViolations = { value: 0, unit: 'count', direction: 'lower-is-better' };
838
+ } catch { metrics.spotbugsViolations = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
839
+ }
840
+ <% } %>
841
+
842
+ <% if (metricsProfile.archunitEnabled) { %>
843
+ // ── ArchUnit failing rule count ───────────────────────────────────────────
844
+ const archBuildRaw = spawnOrSkip('archunitFailingRules', gradleOrMvn, gradleOrMvn,
845
+ ['test', '-q', '--tests', '*ArchitectureTest*'], { cwd });
846
+ if (archBuildRaw !== null) {
847
+ try {
848
+ const testResultsDir = resolve(cwd, 'build/test-results/test');
849
+ if (existsSync(testResultsDir)) {
850
+ const files = readdirSync(testResultsDir).filter(f => f.includes('ArchitectureTest') && f.endsWith('.xml'));
851
+ const failingRules = [];
852
+ for (const f of files) {
853
+ const xml = readFileSync(resolve(testResultsDir, f), 'utf-8');
854
+ const methodMatches = [...xml.matchAll(/<testcase[^>]*name="([^"]+)"[^>]*>[^<]*<failure/g)];
855
+ for (const m of methodMatches) failingRules.push(m[1]);
856
+ }
857
+ metrics.archunitFailingRules = {
858
+ value: failingRules.length,
859
+ unit: 'count', direction: 'lower-is-better',
860
+ ...(failingRules.length > 0 ? { items: failingRules } : {}),
861
+ };
862
+ } else {
863
+ metrics.archunitFailingRules = { value: 0, unit: 'count', direction: 'lower-is-better' };
864
+ }
865
+ } catch { metrics.archunitFailingRules = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
866
+ }
867
+ <% } %>
868
+
869
+ // ── Finding-hygiene (un-promoted findings spool, #1405) ───────────────────
870
+ Object.assign(metrics, collectFindingsMetrics(cwd));
871
+
872
+ return metrics;
873
+ }
874
+ <% } else if (language === 'go') { %>
875
+ /**
876
+ * Collect Go debt metrics.
877
+ * @param {string} cwd
878
+ * @returns {Record<string, {value: number, unit: string, direction: string}>}
879
+ */
880
+ export function collectMetrics(cwd) {
881
+ const metrics = {};
882
+
883
+ // ── Coverage (go test -cover) ─────────────────────────────────────────────
884
+ // Enumerate project packages with `go list ./...` and exclude import paths
885
+ // containing /node_modules/ — npm packages that ship Go source without a
886
+ // nested go.mod (e.g. `flatted`) are otherwise measured as project code,
887
+ // making the debt ratchet dependency-layout-sensitive (#1950). Package paths
888
+ // are passed as separate spawn arguments (shell:false), never concatenated.
889
+ // Fail closed if enumeration fails or yields no usable packages.
890
+ const goList = run('go', ['list', './...'], { cwd });
891
+ if ((goList.status ?? 1) !== 0) {
892
+ throw new Error(
893
+ `go list ./... failed (exit ${goList.status}): ${goList.stderr ?? ''} — refusing to write (fail-closed)`,
894
+ );
895
+ }
896
+ const goPkgs = (goList.stdout ?? '')
897
+ .split('\n')
898
+ .map((l) => l.trim())
899
+ .filter((l) => l.length > 0 && !l.includes('/node_modules/'));
900
+ if (goPkgs.length === 0) {
901
+ throw new Error(
902
+ 'go list ./... returned no project packages after excluding /node_modules/ — refusing to write (fail-closed)',
903
+ );
904
+ }
905
+ run('go', ['test', '-coverprofile=.coverage-tmp.out', ...goPkgs], { cwd });
906
+ const goCoverRaw = run('go', ['tool', 'cover', '-func=.coverage-tmp.out'], { cwd });
907
+ try {
908
+ const match = goCoverRaw.stdout.match(/total:\s+\(statements\)\s+([\d.]+)%/);
909
+ metrics.coverageLine = { value: match ? parseFloat(match[1]) : 0, unit: 'percent', direction: 'higher-is-better' };
910
+ } catch { metrics.coverageLine = { value: 0, unit: 'percent', direction: 'higher-is-better' }; }
911
+
912
+ // ── golangci-lint violations ──────────────────────────────────────────────
913
+ const golintRaw = spawnOrSkip('golangciViolations', 'golangci-lint', 'golangci-lint',
914
+ ['run', '--out-format=json'], { cwd });
915
+ if (golintRaw !== null) {
916
+ try {
917
+ const report = JSON.parse(golintRaw.stdout || '{"Issues":[]}');
918
+ metrics.golangciViolations = {
919
+ value: (report.Issues ?? []).length,
920
+ unit: 'count', direction: 'lower-is-better',
921
+ };
922
+ } catch { metrics.golangciViolations = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
923
+ }
924
+
925
+ // ── govulncheck advisories ────────────────────────────────────────────────
926
+ const govulnRaw = spawnOrSkip('govulncheckAdvisories', 'govulncheck', 'govulncheck',
927
+ ['-json', './...'], { cwd });
928
+ if (govulnRaw !== null) {
929
+ try {
930
+ const lines = (govulnRaw.stdout ?? '').split('\n').filter(Boolean);
931
+ const osvIds = new Set();
932
+ for (const l of lines) {
933
+ try {
934
+ const obj = JSON.parse(l);
935
+ if (obj.osv?.id) osvIds.add(obj.osv.id);
936
+ } catch { /* skip malformed line */ }
937
+ }
938
+ metrics.govulncheckAdvisories = { value: osvIds.size, unit: 'count', direction: 'lower-is-better' };
939
+ } catch { metrics.govulncheckAdvisories = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
940
+ }
941
+
942
+ // ── Finding-hygiene (un-promoted findings spool, #1405) ───────────────────
943
+ Object.assign(metrics, collectFindingsMetrics(cwd));
944
+
945
+ return metrics;
946
+ }
947
+ <% } else if (language === 'python') { %>
948
+ /**
949
+ * Collect Python debt metrics.
950
+ * @param {string} cwd
951
+ * @returns {Record<string, {value: number, unit: string, direction: string}>}
952
+ */
953
+ export function collectMetrics(cwd) {
954
+ const metrics = {};
955
+
956
+ // ── Coverage (pytest --cov) ───────────────────────────────────────────────
957
+ run('pytest', ['--cov', '--cov-report=json', '--cov-report=term'], { cwd });
958
+ try {
959
+ const data = JSON.parse(readFileSync(resolve(cwd, 'coverage.json'), 'utf-8'));
960
+ metrics.coverageLine = { value: data.totals?.percent_covered ?? 0, unit: 'percent', direction: 'higher-is-better' };
961
+ } catch { metrics.coverageLine = { value: 0, unit: 'percent', direction: 'higher-is-better' }; }
962
+
963
+ // ── ruff errors (all rules) ───────────────────────────────────────────────
964
+ const ruffRaw = spawnOrSkip('ruffErrors', 'ruff', 'ruff',
965
+ ['check', '.', '--output-format', 'json'], { cwd });
966
+ if (ruffRaw !== null) {
967
+ try {
968
+ const violations = JSON.parse(ruffRaw.stdout || '[]');
969
+ metrics.ruffErrors = { value: violations.length, unit: 'count', direction: 'lower-is-better' };
970
+ } catch { metrics.ruffErrors = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
971
+ }
972
+
973
+ // ── mypy strict errors (skip if no mypy config) ───────────────────────────
974
+ const mypyConfigExists = existsSync(resolve(cwd, 'mypy.ini'))
975
+ || existsSync(resolve(cwd, '.mypy.ini'))
976
+ || existsSync(resolve(cwd, 'setup.cfg'));
977
+ if (mypyConfigExists) {
978
+ const mypyRaw = spawnOrSkip('mypyStrictErrors', 'mypy', 'mypy',
979
+ ['--strict', '.', '--no-color-output'], { cwd });
980
+ if (mypyRaw !== null) {
981
+ const errorLines = (mypyRaw.stdout ?? '').split('\n').filter(l => l.includes(': error:'));
982
+ metrics.mypyStrictErrors = { value: errorLines.length, unit: 'count', direction: 'lower-is-better' };
983
+ }
984
+ } else {
985
+ metrics.mypyStrictErrors = { value: 0, unit: 'count', direction: 'lower-is-better' };
986
+ }
987
+
988
+ // ── pip-audit advisories ──────────────────────────────────────────────────
989
+ const pipAuditRaw = spawnOrSkip('pipAuditAdvisories', 'pip-audit', 'pip-audit',
990
+ ['--format', 'json'], { cwd });
991
+ if (pipAuditRaw !== null) {
992
+ try {
993
+ const report = JSON.parse(pipAuditRaw.stdout || '{"dependencies":[]}');
994
+ const vulnCount = (report.dependencies ?? []).reduce(
995
+ (acc, dep) => acc + (dep.vulns?.length ?? 0), 0,
996
+ );
997
+ metrics.pipAuditAdvisories = { value: vulnCount, unit: 'count', direction: 'lower-is-better' };
998
+ } catch { metrics.pipAuditAdvisories = { value: 0, unit: 'count', direction: 'lower-is-better' }; }
999
+ }
1000
+
1001
+ // ── Finding-hygiene (un-promoted findings spool, #1405) ───────────────────
1002
+ Object.assign(metrics, collectFindingsMetrics(cwd));
1003
+
1004
+ return metrics;
1005
+ }
1006
+ <% } else { %>
1007
+ /**
1008
+ * Unknown language — no stack-specific metrics collected.
1009
+ * @param {string} _cwd
1010
+ * @returns {Record<string, never>}
1011
+ */
1012
+ export function collectMetrics(_cwd) {
1013
+ return {};
1014
+ }
1015
+ <% } %>