pi-dev-team 0.2.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 (780) hide show
  1. package/LICENSE +21 -0
  2. package/PORTING.md +134 -0
  3. package/README.md +207 -0
  4. package/UPSTREAM.json +64 -0
  5. package/agents/Explore.md +15 -0
  6. package/agents/a11y-review.md +118 -0
  7. package/agents/adr-author.md +70 -0
  8. package/agents/ai-provenance-review.md +120 -0
  9. package/agents/angular-reactivity-review.md +95 -0
  10. package/agents/arch-review.md +135 -0
  11. package/agents/architect.md +78 -0
  12. package/agents/autoship-batch-proposer.md +69 -0
  13. package/agents/claude-setup-review.md +136 -0
  14. package/agents/codebase-recon.md +184 -0
  15. package/agents/component-architecture-review.md +119 -0
  16. package/agents/concurrency-review.md +109 -0
  17. package/agents/correctness-review.md +290 -0
  18. package/agents/data-flow-tracer.md +120 -0
  19. package/agents/doc-review.md +165 -0
  20. package/agents/domain-review.md +136 -0
  21. package/agents/general-purpose.md +10 -0
  22. package/agents/gherkin-quality-critic.md +113 -0
  23. package/agents/js-fp-review.md +114 -0
  24. package/agents/mutation-kill.md +684 -0
  25. package/agents/naming-review.md +142 -0
  26. package/agents/orchestrator.md +339 -0
  27. package/agents/performance-review.md +105 -0
  28. package/agents/plan-review-acceptance.md +115 -0
  29. package/agents/plan-review-design.md +90 -0
  30. package/agents/plan-review-parallelization.md +84 -0
  31. package/agents/plan-review-strategic.md +96 -0
  32. package/agents/plan-review-ux.md +110 -0
  33. package/agents/platform-engineer.md +64 -0
  34. package/agents/product-manager.md +68 -0
  35. package/agents/progress-guardian.md +79 -0
  36. package/agents/qa-engineer.md +289 -0
  37. package/agents/quality-reviewer.md +132 -0
  38. package/agents/react-reactivity-review.md +102 -0
  39. package/agents/refactor-opportunity-review.md +128 -0
  40. package/agents/security-engineer.md +60 -0
  41. package/agents/security-review.md +218 -0
  42. package/agents/session-analysis.md +95 -0
  43. package/agents/software-engineer.md +105 -0
  44. package/agents/spec-compliance-review.md +100 -0
  45. package/agents/spec-reviewer.md +114 -0
  46. package/agents/structure-review.md +146 -0
  47. package/agents/tech-writer.md +84 -0
  48. package/agents/test-review.md +246 -0
  49. package/agents/test-smell-review.md +188 -0
  50. package/agents/token-efficiency-review.md +139 -0
  51. package/agents/ui-ux-designer.md +54 -0
  52. package/agents/vue-reactivity-review.md +95 -0
  53. package/bin/__pycache__/claudecpython-314.pyc +0 -0
  54. package/bin/claude +258 -0
  55. package/docs/upstream/.pages +1 -0
  56. package/docs/upstream/CHANGELOG.md +2586 -0
  57. package/docs/upstream/README.md +155 -0
  58. package/docs/upstream/agent-architecture.md +214 -0
  59. package/docs/upstream/agent_info.md +187 -0
  60. package/docs/upstream/artifact-migration.md +124 -0
  61. package/docs/upstream/code-intelligence-nudge.md +149 -0
  62. package/docs/upstream/code-review-process.md +294 -0
  63. package/docs/upstream/concurrent-use.md +73 -0
  64. package/docs/upstream/context-management.md +111 -0
  65. package/docs/upstream/developer-notes.md +280 -0
  66. package/docs/upstream/diagrams/architecture-overview.svg +101 -0
  67. package/docs/upstream/diagrams/review-dispatch.svg +139 -0
  68. package/docs/upstream/diagrams/team-agents.svg +128 -0
  69. package/docs/upstream/diagrams/test-improve-flow.svg +166 -0
  70. package/docs/upstream/diagrams/workflow-linear.svg +66 -0
  71. package/docs/upstream/diagrams/workflow-three-phase.svg +200 -0
  72. package/docs/upstream/eval-maintenance.md +95 -0
  73. package/docs/upstream/eval-running-guide.md +147 -0
  74. package/docs/upstream/eval-system.md +291 -0
  75. package/docs/upstream/session-review-oss-complements.md +75 -0
  76. package/docs/upstream/session-review.md +212 -0
  77. package/docs/upstream/skills.md +188 -0
  78. package/docs/upstream/team-structure.md +21 -0
  79. package/docs/upstream/telemetry-ci-access.md +129 -0
  80. package/docs/upstream/telemetry-repo-security.md +120 -0
  81. package/docs/upstream/test-evaluation.md +277 -0
  82. package/docs/upstream/test-improve.md +154 -0
  83. package/docs/upstream/triage-workflow.md +282 -0
  84. package/docs/upstream/workflows.md +289 -0
  85. package/extensions/dev-team/index.ts +539 -0
  86. package/extensions/dev-team/lib/agents.ts +272 -0
  87. package/extensions/dev-team/lib/ai-credits.ts +92 -0
  88. package/extensions/dev-team/lib/autocompact.ts +81 -0
  89. package/extensions/dev-team/lib/child-run.ts +102 -0
  90. package/extensions/dev-team/lib/config.ts +236 -0
  91. package/extensions/dev-team/lib/gh-command.ts +103 -0
  92. package/extensions/dev-team/lib/github-style.ts +307 -0
  93. package/extensions/dev-team/lib/hooks.ts +350 -0
  94. package/extensions/dev-team/lib/metrics.ts +115 -0
  95. package/extensions/dev-team/lib/safe-read.ts +49 -0
  96. package/extensions/dev-team/lib/session-files.ts +57 -0
  97. package/extensions/dev-team/lib/session-spend.ts +123 -0
  98. package/extensions/dev-team/lib/shell-scan.ts +205 -0
  99. package/extensions/dev-team/lib/skills.ts +213 -0
  100. package/extensions/dev-team/lib/subagent-render.ts +245 -0
  101. package/extensions/dev-team/lib/subagent-types.ts +164 -0
  102. package/extensions/dev-team/lib/subagent.ts +596 -0
  103. package/extensions/dev-team/lib/terminal-text.ts +54 -0
  104. package/extensions/dev-team/lib/tools-misc.ts +152 -0
  105. package/extensions/dev-team/lib/transcript.ts +110 -0
  106. package/extensions/dev-team/lib/trust.ts +52 -0
  107. package/extensions/dev-team/lib/usage-breakdown.ts +176 -0
  108. package/extensions/dev-team/lib/usage-chart.ts +153 -0
  109. package/extensions/dev-team/lib/usage-command.ts +107 -0
  110. package/extensions/dev-team/lib/usage-history.ts +203 -0
  111. package/extensions/dev-team/lib/usage-render.ts +225 -0
  112. package/extensions/dev-team/lib/usage-split-bar.ts +127 -0
  113. package/extensions/dev-team/lib/usage-state.ts +116 -0
  114. package/extensions/dev-team/lib/usage-text.ts +159 -0
  115. package/extensions/dev-team/lib/usage-view.ts +109 -0
  116. package/hooks/__pycache__/refactor_test_freeze_guard.cpython-314.pyc +0 -0
  117. package/hooks/agent_dispatch_ledger.py +190 -0
  118. package/hooks/autocompact_setup_nudge.py +99 -0
  119. package/hooks/bash_retry_guard.py +228 -0
  120. package/hooks/boundary_events_write_guard.py +352 -0
  121. package/hooks/code_intelligence_nudge.py +293 -0
  122. package/hooks/code_intelligence_turn_mark.py +317 -0
  123. package/hooks/codegraph_bootstrap.py +139 -0
  124. package/hooks/contract_version_guard.py +362 -0
  125. package/hooks/cost_meter.py +106 -0
  126. package/hooks/destructive-commands.json +62 -0
  127. package/hooks/destructive_guard.py +477 -0
  128. package/hooks/eval_compliance_check.py +440 -0
  129. package/hooks/guards.json +17 -0
  130. package/hooks/hooks.json +323 -0
  131. package/hooks/internal_double_gate.py +296 -0
  132. package/hooks/js_fp_review.py +212 -0
  133. package/hooks/knowledge_index.py +119 -0
  134. package/hooks/lib/__pycache__/artifact_paths.cpython-314.pyc +0 -0
  135. package/hooks/lib/__pycache__/atomic_state.cpython-314.pyc +0 -0
  136. package/hooks/lib/__pycache__/autocompact_config.cpython-314.pyc +0 -0
  137. package/hooks/lib/__pycache__/boundary_events.cpython-314.pyc +0 -0
  138. package/hooks/lib/__pycache__/doc_classification.cpython-314.pyc +0 -0
  139. package/hooks/lib/__pycache__/gh_pr_create_detect.cpython-314.pyc +0 -0
  140. package/hooks/lib/__pycache__/git_safe_diff.cpython-314.pyc +0 -0
  141. package/hooks/lib/__pycache__/instrument_log.cpython-314.pyc +0 -0
  142. package/hooks/lib/__pycache__/metrics_query.cpython-314.pyc +0 -0
  143. package/hooks/lib/__pycache__/plugin_version.cpython-314.pyc +0 -0
  144. package/hooks/lib/__pycache__/pre_commit_doc_classifier.cpython-314.pyc +0 -0
  145. package/hooks/lib/__pycache__/review_agent_registry.cpython-314.pyc +0 -0
  146. package/hooks/lib/__pycache__/review_gate_corroboration.cpython-314.pyc +0 -0
  147. package/hooks/lib/__pycache__/review_gate_hash.cpython-314.pyc +0 -0
  148. package/hooks/lib/__pycache__/review_verdicts.cpython-314.pyc +0 -0
  149. package/hooks/lib/__pycache__/stdin_json.cpython-314.pyc +0 -0
  150. package/hooks/lib/__pycache__/stryker_invocation.cpython-314.pyc +0 -0
  151. package/hooks/lib/__pycache__/telemetry_consent.cpython-314.pyc +0 -0
  152. package/hooks/lib/__pycache__/test_file_classify.cpython-314.pyc +0 -0
  153. package/hooks/lib/__pycache__/token_efficiency_limits.cpython-314.pyc +0 -0
  154. package/hooks/lib/__pycache__/verify_guard_state.cpython-314.pyc +0 -0
  155. package/hooks/lib/__pycache__/xunit_v3_operator_gate.cpython-314.pyc +0 -0
  156. package/hooks/lib/agent_skill_hints.py +74 -0
  157. package/hooks/lib/artifact_paths.py +263 -0
  158. package/hooks/lib/atomic_state.py +557 -0
  159. package/hooks/lib/autocompact_config.py +103 -0
  160. package/hooks/lib/autoship_log.py +106 -0
  161. package/hooks/lib/banned_scripts_policy.py +51 -0
  162. package/hooks/lib/boundary_events.py +436 -0
  163. package/hooks/lib/build_knowledge_index.py +504 -0
  164. package/hooks/lib/build_skills_index.py +361 -0
  165. package/hooks/lib/build_state.py +116 -0
  166. package/hooks/lib/classify_ship_outcome.py +126 -0
  167. package/hooks/lib/config_changelog_schema.py +115 -0
  168. package/hooks/lib/cost_meter.py +955 -0
  169. package/hooks/lib/doc_classification.py +116 -0
  170. package/hooks/lib/gh_pr_create_detect.py +136 -0
  171. package/hooks/lib/git_safe_diff.py +123 -0
  172. package/hooks/lib/instrument_log.py +66 -0
  173. package/hooks/lib/iteration_journal_gate.py +197 -0
  174. package/hooks/lib/knowledge_index_paths.py +88 -0
  175. package/hooks/lib/mcp_json_repowise.py +177 -0
  176. package/hooks/lib/metrics_query.py +202 -0
  177. package/hooks/lib/minimal_yaml.py +434 -0
  178. package/hooks/lib/plugin_version.py +142 -0
  179. package/hooks/lib/pre_commit_detect.py +537 -0
  180. package/hooks/lib/pre_commit_doc_classifier.py +126 -0
  181. package/hooks/lib/pricing.py +118 -0
  182. package/hooks/lib/report_pdf.py +371 -0
  183. package/hooks/lib/review_agent_registry.py +142 -0
  184. package/hooks/lib/review_dispatch_ledger.py +101 -0
  185. package/hooks/lib/review_gate_corroboration.py +521 -0
  186. package/hooks/lib/review_gate_hash.py +252 -0
  187. package/hooks/lib/review_gate_normalized_hash.py +1115 -0
  188. package/hooks/lib/review_verdicts.py +301 -0
  189. package/hooks/lib/run_report.py +160 -0
  190. package/hooks/lib/skill_categories.yaml +125 -0
  191. package/hooks/lib/stdin_json.py +57 -0
  192. package/hooks/lib/stryker_invocation.py +102 -0
  193. package/hooks/lib/telemetry_consent.py +41 -0
  194. package/hooks/lib/telemetry_report.py +108 -0
  195. package/hooks/lib/test_file_classify.py +160 -0
  196. package/hooks/lib/token_efficiency_limits.py +51 -0
  197. package/hooks/lib/turn_identity.py +77 -0
  198. package/hooks/lib/verify_guard_state.py +110 -0
  199. package/hooks/lib/workflow_state.py +206 -0
  200. package/hooks/lib/xunit_v3_operator_gate.py +596 -0
  201. package/hooks/mcp_json_repowise_nudge.py +74 -0
  202. package/hooks/mutation_adapters/__init__.py +7 -0
  203. package/hooks/mutation_adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  204. package/hooks/mutation_adapters/__pycache__/lib.cpython-314.pyc +0 -0
  205. package/hooks/mutation_adapters/__pycache__/mutmut.cpython-314.pyc +0 -0
  206. package/hooks/mutation_adapters/__pycache__/pitest.cpython-314.pyc +0 -0
  207. package/hooks/mutation_adapters/__pycache__/stryker.cpython-314.pyc +0 -0
  208. package/hooks/mutation_adapters/__pycache__/stryker_net.cpython-314.pyc +0 -0
  209. package/hooks/mutation_adapters/lib.py +478 -0
  210. package/hooks/mutation_adapters/mutmut.py +188 -0
  211. package/hooks/mutation_adapters/pitest.py +266 -0
  212. package/hooks/mutation_adapters/stryker.py +157 -0
  213. package/hooks/mutation_adapters/stryker_net.py +264 -0
  214. package/hooks/mutation_gate.py +193 -0
  215. package/hooks/mutation_testing_smoke_gate.py +371 -0
  216. package/hooks/pending_review_notify.py +121 -0
  217. package/hooks/phase_marker.py +138 -0
  218. package/hooks/post_compact_state_reinject.py +180 -0
  219. package/hooks/post_format.py +115 -0
  220. package/hooks/pre_commit_knowledge_index.py +128 -0
  221. package/hooks/pre_commit_review.py +66 -0
  222. package/hooks/pre_pr_review.py +694 -0
  223. package/hooks/pre_tool_guard.py +405 -0
  224. package/hooks/py.sh +73 -0
  225. package/hooks/refactor-bash-write-patterns.json +29 -0
  226. package/hooks/refactor_test_bash_guard.py +253 -0
  227. package/hooks/refactor_test_freeze_guard.py +139 -0
  228. package/hooks/refactor_test_revert_guard.py +186 -0
  229. package/hooks/repo_review_nudge.py +287 -0
  230. package/hooks/review_verdict_recorder.py +464 -0
  231. package/hooks/scan_bash_command_for_banned_scripts.py +428 -0
  232. package/hooks/scan_worktree_for_banned_scripts.py +238 -0
  233. package/hooks/session_learning_trigger.py +248 -0
  234. package/hooks/skills_index.py +126 -0
  235. package/hooks/stryker_xunit_shim_guard.py +571 -0
  236. package/hooks/subagent_completion_guard.py +309 -0
  237. package/hooks/subagent_skill_context.py +139 -0
  238. package/hooks/task_completion_metrics.py +216 -0
  239. package/hooks/tdd_guard.py +229 -0
  240. package/hooks/telemetry.py +341 -0
  241. package/hooks/token_efficiency_review.py +194 -0
  242. package/hooks/verify_guard.py +183 -0
  243. package/hooks/verify_guard_edit_marker.py +73 -0
  244. package/hooks/version_check.py +173 -0
  245. package/knowledge/accepted-risks-schema.md +98 -0
  246. package/knowledge/adr-decision-criteria.md +64 -0
  247. package/knowledge/adversarial-review-protocol.md +139 -0
  248. package/knowledge/agent-registry.md +228 -0
  249. package/knowledge/agent-review-methodology.md +80 -0
  250. package/knowledge/ai-friendly-repo-guidelines.md +67 -0
  251. package/knowledge/architecture-assessment.md +96 -0
  252. package/knowledge/artifact-lifecycle.md +57 -0
  253. package/knowledge/cd-maturity-model.md +82 -0
  254. package/knowledge/cd-test-architecture.md +190 -0
  255. package/knowledge/ci-cd-file-scope.md +24 -0
  256. package/knowledge/codegraph-vs-graphify.md +192 -0
  257. package/knowledge/component-test-patterns.md +139 -0
  258. package/knowledge/database-change-management.md +80 -0
  259. package/knowledge/database-test-patterns.md +79 -0
  260. package/knowledge/decision-defaults.md +88 -0
  261. package/knowledge/dependency-breaking-techniques.md +116 -0
  262. package/knowledge/deployment-pipeline.md +86 -0
  263. package/knowledge/design-smells.md +122 -0
  264. package/knowledge/directory-enumeration.md +38 -0
  265. package/knowledge/domain-modeling.md +123 -0
  266. package/knowledge/evidence-bundle.md +90 -0
  267. package/knowledge/exploratory-testing-field-guide.md +122 -0
  268. package/knowledge/failure-routing.md +28 -0
  269. package/knowledge/fixture-construction.md +56 -0
  270. package/knowledge/frontend-component-architecture.md +139 -0
  271. package/knowledge/gherkin-quality-review-dispatch.md +135 -0
  272. package/knowledge/index.json +6766 -0
  273. package/knowledge/internal-collaborator-doubling.md +101 -0
  274. package/knowledge/legacy-test-strategy.md +71 -0
  275. package/knowledge/long-run-waiting.md +66 -0
  276. package/knowledge/microservice-testing.md +71 -0
  277. package/knowledge/model-pricing.json +23 -0
  278. package/knowledge/mutation-score-formulas.md +60 -0
  279. package/knowledge/object-calisthenics.md +147 -0
  280. package/knowledge/oracle-provenance.md +94 -0
  281. package/knowledge/orchestrator-script-implementation.md +185 -0
  282. package/knowledge/owasp-detection.md +148 -0
  283. package/knowledge/plan-review-rubric.md +56 -0
  284. package/knowledge/proxy-connectivity.md +62 -0
  285. package/knowledge/reactive-effect-patterns.md +73 -0
  286. package/knowledge/recon-inventory-excludes.txt +32 -0
  287. package/knowledge/references/bdd-value-guide.md +61 -0
  288. package/knowledge/references/csharp-http-client-testing.md +264 -0
  289. package/knowledge/release-strategies.md +74 -0
  290. package/knowledge/report-output-location.md +117 -0
  291. package/knowledge/report-pdf-integration.md +63 -0
  292. package/knowledge/report-print.css +129 -0
  293. package/knowledge/report-template.md +114 -0
  294. package/knowledge/report-to-pdf.md +69 -0
  295. package/knowledge/request-processing-flow.md +63 -0
  296. package/knowledge/result-verification.md +52 -0
  297. package/knowledge/review-agent-output-contract.md +121 -0
  298. package/knowledge/review-lens-classification.md +113 -0
  299. package/knowledge/review-rubric.md +62 -0
  300. package/knowledge/review-template.md +104 -0
  301. package/knowledge/rule-fixtures/A02.insecure-random-js/negative.js +1 -0
  302. package/knowledge/rule-fixtures/A02.insecure-random-js/positive.js +1 -0
  303. package/knowledge/rule-fixtures/A02.weak-hashing-md5/negative.py +1 -0
  304. package/knowledge/rule-fixtures/A02.weak-hashing-md5/positive.py +1 -0
  305. package/knowledge/rule-fixtures/A03.command-injection/negative.js +1 -0
  306. package/knowledge/rule-fixtures/A03.command-injection/positive.js +1 -0
  307. package/knowledge/rule-fixtures/A03.sql-injection/negative.js +1 -0
  308. package/knowledge/rule-fixtures/A03.sql-injection/positive.js +1 -0
  309. package/knowledge/rule-fixtures/A03.xss-innerhtml/negative.js +1 -0
  310. package/knowledge/rule-fixtures/A03.xss-innerhtml/positive.js +1 -0
  311. package/knowledge/rule-fixtures/A05.cors-wildcard/negative.js +1 -0
  312. package/knowledge/rule-fixtures/A05.cors-wildcard/positive.js +1 -0
  313. package/knowledge/rule-fixtures/A05.default-credentials/negative.js +1 -0
  314. package/knowledge/rule-fixtures/A05.default-credentials/positive.js +1 -0
  315. package/knowledge/rule-fixtures/A07.jwt-alg-none/negative.js +1 -0
  316. package/knowledge/rule-fixtures/A07.jwt-alg-none/positive.js +1 -0
  317. package/knowledge/rule-fixtures/A08.binary-formatter/negative.cs +1 -0
  318. package/knowledge/rule-fixtures/A08.binary-formatter/positive.cs +1 -0
  319. package/knowledge/rule-fixtures/A08.js-eval/negative.js +1 -0
  320. package/knowledge/rule-fixtures/A08.js-eval/positive.js +1 -0
  321. package/knowledge/rule-fixtures/A08.object-input-stream/negative.java +1 -0
  322. package/knowledge/rule-fixtures/A08.object-input-stream/positive.java +1 -0
  323. package/knowledge/schemas/disposition-register-v1.json +65 -0
  324. package/knowledge/schemas/recon-envelope-v1.json +198 -0
  325. package/knowledge/schemas/unified-finding-v1.json +72 -0
  326. package/knowledge/security-primitives-contract.md +301 -0
  327. package/knowledge/security-review-rule-map.yaml +107 -0
  328. package/knowledge/skills-registry.md +72 -0
  329. package/knowledge/task-size-classifier.md +103 -0
  330. package/knowledge/telemetry-schema.md +881 -0
  331. package/knowledge/test-automation-maturity.md +56 -0
  332. package/knowledge/test-automation-principles.md +71 -0
  333. package/knowledge/test-cadence-tradeoffs.md +68 -0
  334. package/knowledge/test-doubles.md +105 -0
  335. package/knowledge/test-file-indicators.md +22 -0
  336. package/knowledge/test-layer-gates.md +35 -0
  337. package/knowledge/test-matrix-examples/django-batch.md +24 -0
  338. package/knowledge/test-matrix-examples/dotnet-grpc-fronting-api.md +90 -0
  339. package/knowledge/test-matrix-examples/dotnet-http-consumer.md +131 -0
  340. package/knowledge/test-matrix-examples/react-node-spa.md +24 -0
  341. package/knowledge/test-matrix-examples/spring-boot-service.md +25 -0
  342. package/knowledge/test-matrix-examples/ssr-htmx.md +24 -0
  343. package/knowledge/test-organization.md +70 -0
  344. package/knowledge/test-pyramid.md +84 -0
  345. package/knowledge/test-refactoring.md +67 -0
  346. package/knowledge/test-review-division-of-labor.md +85 -0
  347. package/knowledge/test-smells.md +80 -0
  348. package/knowledge/test-stack-profiles/bdd-frameworks.md +235 -0
  349. package/knowledge/test-stack-profiles/django.md +13 -0
  350. package/knowledge/test-stack-profiles/dotnet.md +18 -0
  351. package/knowledge/test-stack-profiles/go.md +16 -0
  352. package/knowledge/test-stack-profiles/node.md +16 -0
  353. package/knowledge/test-stack-profiles/react.md +12 -0
  354. package/knowledge/test-stack-profiles/spring-boot.md +16 -0
  355. package/knowledge/test-stack-profiles/ssr-htmx.md +14 -0
  356. package/knowledge/test-stack-profiles/vue.md +12 -0
  357. package/knowledge/test-strategy.md +70 -0
  358. package/knowledge/testability-patterns.md +240 -0
  359. package/knowledge/testing-quadrants.md +44 -0
  360. package/knowledge/testing-techniques/approval.md +15 -0
  361. package/knowledge/testing-techniques/chaos.md +17 -0
  362. package/knowledge/testing-techniques/fuzz.md +15 -0
  363. package/knowledge/testing-techniques/property-based.md +15 -0
  364. package/knowledge/testing-techniques/schema-validation.md +15 -0
  365. package/knowledge/testing-techniques/screenshot.md +15 -0
  366. package/knowledge/three-phase-workflow.md +198 -0
  367. package/knowledge/value-patterns.md +55 -0
  368. package/knowledge/verification-mode.md +116 -0
  369. package/knowledge/virtual-service-libraries.md +75 -0
  370. package/knowledge/wave-consolidation-guidance.md +21 -0
  371. package/overrides/agents/Explore.md +15 -0
  372. package/overrides/agents/general-purpose.md +10 -0
  373. package/overrides/notes/autoship.md +6 -0
  374. package/overrides/notes/issues-from-assessment.md +3 -0
  375. package/overrides/notes/issues-from-plan.md +3 -0
  376. package/overrides/notes/mutation-night-watch.md +3 -0
  377. package/overrides/notes/mutation-testing.md +3 -0
  378. package/overrides/notes/pr.md +7 -0
  379. package/overrides/notes/project-init.md +6 -0
  380. package/overrides/notes/setup.md +13 -0
  381. package/overrides/notes/specs.md +3 -0
  382. package/overrides/skills/headless-run/SKILL.md +45 -0
  383. package/overrides/skills/upgrade/SKILL.md +30 -0
  384. package/overrides/skills/version/SKILL.md +25 -0
  385. package/package.json +36 -0
  386. package/scripts/authoring_digest.py +93 -0
  387. package/scripts/autoship_discover.py +121 -0
  388. package/scripts/autoship_group.py +409 -0
  389. package/scripts/autoship_proposals.py +494 -0
  390. package/scripts/autoship_queue.py +291 -0
  391. package/scripts/autoship_reclaim.py +495 -0
  392. package/scripts/build_jobs.py +108 -0
  393. package/scripts/build_rollback_point.py +240 -0
  394. package/scripts/build_slice_scope.py +157 -0
  395. package/scripts/build_wave.py +109 -0
  396. package/scripts/build_wave_reconcile.py +252 -0
  397. package/scripts/build_worktree_baseref.py +113 -0
  398. package/scripts/check_agent_scope.py +117 -0
  399. package/scripts/check_agent_tool_mapping.py +213 -0
  400. package/scripts/check_review_agent_mcp_tools.py +317 -0
  401. package/scripts/check_security_assessment_mcp_tools.py +165 -0
  402. package/scripts/checkpoint_abort.py +502 -0
  403. package/scripts/claude_setup_review.py +438 -0
  404. package/scripts/codebase_recon.py +556 -0
  405. package/scripts/coverage_config.py +623 -0
  406. package/scripts/coverage_delta_steering.py +330 -0
  407. package/scripts/coverage_discovery_dotnet.py +315 -0
  408. package/scripts/coverage_discovery_java.py +742 -0
  409. package/scripts/coverage_discovery_js.py +546 -0
  410. package/scripts/coverage_gap_ranking.py +556 -0
  411. package/scripts/coverage_readiness.py +455 -0
  412. package/scripts/coverage_report_parse.py +521 -0
  413. package/scripts/detect_bdd_convention.py +252 -0
  414. package/scripts/eval_ablation.py +376 -0
  415. package/scripts/gherkin_analysis_coverage_gate.py +306 -0
  416. package/scripts/gherkin_cross_feature_duplicate_titles_gate.py +173 -0
  417. package/scripts/gherkin_effectiveness_rollup.py +238 -0
  418. package/scripts/gherkin_failure_path_gate.py +206 -0
  419. package/scripts/gherkin_feature_merge.py +720 -0
  420. package/scripts/gherkin_stub_gate.py +163 -0
  421. package/scripts/gherkin_stub_merge.py +479 -0
  422. package/scripts/git_origin_host.py +88 -0
  423. package/scripts/install-java-static-analysis.py +110 -0
  424. package/scripts/issue_deps.py +74 -0
  425. package/scripts/lib/_bdd_markers.py +28 -0
  426. package/scripts/lib/_gherkin_text.py +93 -0
  427. package/scripts/lib/_vendored_tree.py +70 -0
  428. package/scripts/lib/autoship_state.py +397 -0
  429. package/scripts/lib/claude_md_guard.py +226 -0
  430. package/scripts/lib/deterministic_recon.py +446 -0
  431. package/scripts/lib/mcp_tool_grants.py +211 -0
  432. package/scripts/lib/plan_parse.py +386 -0
  433. package/scripts/lib/review_result.py +84 -0
  434. package/scripts/lib/review_roster.py +86 -0
  435. package/scripts/lib/session_log/__init__.py +34 -0
  436. package/scripts/lib/session_log/__pycache__/__init__.cpython-314.pyc +0 -0
  437. package/scripts/lib/session_log/__pycache__/records.cpython-314.pyc +0 -0
  438. package/scripts/lib/session_log/classify.py +231 -0
  439. package/scripts/lib/session_log/corrections.py +194 -0
  440. package/scripts/lib/session_log/discovery.py +108 -0
  441. package/scripts/lib/session_log/records.py +218 -0
  442. package/scripts/lib/session_log/redact.py +76 -0
  443. package/scripts/lib/session_log/signals.py +373 -0
  444. package/scripts/lib/session_report_downstream.py +614 -0
  445. package/scripts/lib/session_report_maintainer.py +1273 -0
  446. package/scripts/lib/session_report_shared.py +262 -0
  447. package/scripts/lib/settings_hook_guard.py +157 -0
  448. package/scripts/lib/slug.py +33 -0
  449. package/scripts/lib/stub_extractors/__init__.py +82 -0
  450. package/scripts/lib/stub_extractors/_common.py +328 -0
  451. package/scripts/lib/stub_extractors/csharp.py +19 -0
  452. package/scripts/lib/stub_extractors/go.py +173 -0
  453. package/scripts/lib/stub_extractors/java.py +18 -0
  454. package/scripts/lib/stub_extractors/jsts.py +126 -0
  455. package/scripts/mutation_stack_sections.py +149 -0
  456. package/scripts/mutation_yield_steering.py +345 -0
  457. package/scripts/orchestrator.py +895 -0
  458. package/scripts/plan_gherkin_export.py +227 -0
  459. package/scripts/plan_waves.py +208 -0
  460. package/scripts/pr_close_keyword_lint.py +108 -0
  461. package/scripts/progress_guardian.py +888 -0
  462. package/scripts/recon_inventory.py +273 -0
  463. package/scripts/review_findings_log.py +93 -0
  464. package/scripts/run_invariants.py +124 -0
  465. package/scripts/select_lenses.py +640 -0
  466. package/scripts/session_report.py +486 -0
  467. package/scripts/set_autocompact_env.py +221 -0
  468. package/scripts/ship_resume_guard.py +135 -0
  469. package/scripts/ship_review_gate.py +63 -0
  470. package/scripts/specs_convention_marker.py +103 -0
  471. package/scripts/test_improve_resume.py +277 -0
  472. package/scripts/test_review_mechanics.py +958 -0
  473. package/scripts/token_efficiency_review.py +322 -0
  474. package/scripts/verdict_scope.py +285 -0
  475. package/scripts/verify_gherkin_quality_critic_isolation.py +296 -0
  476. package/scripts/verify_tier.py +157 -0
  477. package/skills/adr-tools/SKILL.md +118 -0
  478. package/skills/agent-readiness/SKILL.md +105 -0
  479. package/skills/agent-readiness/ai_friendly_analyzers.py +326 -0
  480. package/skills/agent-readiness/scanner.py +441 -0
  481. package/skills/agent-readiness/scorecard.yaml +88 -0
  482. package/skills/api-design/SKILL.md +115 -0
  483. package/skills/apply-fixes/SKILL.md +171 -0
  484. package/skills/apply-test-doubles/SKILL.md +321 -0
  485. package/skills/artifact-lifecycle/SKILL.md +127 -0
  486. package/skills/autoship/SKILL.md +1124 -0
  487. package/skills/benchmark/SKILL.md +105 -0
  488. package/skills/branch-workflow/SKILL.md +89 -0
  489. package/skills/browse/SKILL.md +184 -0
  490. package/skills/browser-testing/SKILL.md +62 -0
  491. package/skills/browser-testing/references/playwright-patterns.md +216 -0
  492. package/skills/build/SKILL.md +422 -0
  493. package/skills/build/references/static-self-heal.md +245 -0
  494. package/skills/careful/SKILL.md +72 -0
  495. package/skills/cd-test-architecture/SKILL.md +371 -0
  496. package/skills/ci-debugging/SKILL.md +105 -0
  497. package/skills/co-evolution-audit/SKILL.md +269 -0
  498. package/skills/code-review/SKILL.md +1015 -0
  499. package/skills/code-review/examples/aggregated-sample.json +56 -0
  500. package/skills/code-review/examples/sample-report.md +41 -0
  501. package/skills/code-review/output-format.md +478 -0
  502. package/skills/code-review/scripts/activation.py +86 -0
  503. package/skills/code-review/scripts/change_impact.py +357 -0
  504. package/skills/code-review/scripts/change_shape.py +372 -0
  505. package/skills/code-review/scripts/change_size.py +212 -0
  506. package/skills/code-review/scripts/changed_file_list.py +141 -0
  507. package/skills/code-review/scripts/closing_pass.py +187 -0
  508. package/skills/code-review/scripts/consolidate.py +277 -0
  509. package/skills/code-review/scripts/contract_failure_report.py +185 -0
  510. package/skills/code-review/scripts/dispatch_reconcile.py +66 -0
  511. package/skills/code-review/scripts/dispatch_waves.py +164 -0
  512. package/skills/code-review/scripts/finding_signature.py +446 -0
  513. package/skills/code-review/scripts/ledger.py +283 -0
  514. package/skills/code-review/scripts/partition.py +169 -0
  515. package/skills/code-review/scripts/render_tiered_findings.py +274 -0
  516. package/skills/code-review/scripts/repo_invariants.py +1066 -0
  517. package/skills/code-review/scripts/review_context_pack.py +306 -0
  518. package/skills/code-review/scripts/review_round_log.py +345 -0
  519. package/skills/code-review/scripts/review_value_coverage.py +297 -0
  520. package/skills/code-review/scripts/validate_review_output.py +467 -0
  521. package/skills/code-review/sliced-mode.md +205 -0
  522. package/skills/competitive-analysis/SKILL.md +191 -0
  523. package/skills/context-loading-protocol/SKILL.md +157 -0
  524. package/skills/continue/SKILL.md +90 -0
  525. package/skills/cost-report/SKILL.md +178 -0
  526. package/skills/coverage-baseline/SKILL.md +335 -0
  527. package/skills/coverage-baseline/references/multi-project-discovery.md +202 -0
  528. package/skills/coverage-delta/SKILL.md +181 -0
  529. package/skills/coverage-delta/references/mutation-gate.md +70 -0
  530. package/skills/design-doc/SKILL.md +95 -0
  531. package/skills/design-interrogation/SKILL.md +89 -0
  532. package/skills/design-it-twice/SKILL.md +91 -0
  533. package/skills/docker-image-audit/SKILL.md +108 -0
  534. package/skills/docker-image-audit/references/install-guide.md +64 -0
  535. package/skills/docker-image-audit/references/report-template.md +73 -0
  536. package/skills/docker-image-create/SKILL.md +185 -0
  537. package/skills/domain-analysis/SKILL.md +183 -0
  538. package/skills/domain-driven-design/SKILL.md +194 -0
  539. package/skills/exploratory-testing/SKILL.md +108 -0
  540. package/skills/explore/SKILL.md +51 -0
  541. package/skills/farley-score/SKILL.md +165 -0
  542. package/skills/feature-file-validation/SKILL.md +78 -0
  543. package/skills/feature-file-validation/references/validation-rules.md +115 -0
  544. package/skills/feedback-learning/SKILL.md +414 -0
  545. package/skills/fix/SKILL.md +450 -0
  546. package/skills/freeze/SKILL.md +68 -0
  547. package/skills/frontend-architecture/SKILL.md +113 -0
  548. package/skills/gherkin-derive/SKILL.md +630 -0
  549. package/skills/gherkin-public/SKILL.md +266 -0
  550. package/skills/governance-compliance/SKILL.md +150 -0
  551. package/skills/guard/SKILL.md +75 -0
  552. package/skills/handoff/SKILL.md +139 -0
  553. package/skills/handoff/references/summary-templates.md +242 -0
  554. package/skills/harness-audit/SKILL.md +751 -0
  555. package/skills/harness-audit/scripts/lesson_validate.py +386 -0
  556. package/skills/harness-audit/scripts/redundancy_criterion.py +188 -0
  557. package/skills/headless-run/SKILL.md +45 -0
  558. package/skills/headless-run/scripts/isolated_dispatch.py +381 -0
  559. package/skills/help/SKILL.md +72 -0
  560. package/skills/hexagonal-architecture/SKILL.md +85 -0
  561. package/skills/human-oversight-protocol/SKILL.md +224 -0
  562. package/skills/issues-from-assessment/SKILL.md +223 -0
  563. package/skills/issues-from-plan/SKILL.md +133 -0
  564. package/skills/legacy-code/SKILL.md +132 -0
  565. package/skills/mermaid-diagramming/SKILL.md +120 -0
  566. package/skills/mutation-night-watch/SKILL.md +154 -0
  567. package/skills/mutation-night-watch/references/scheduling.md +135 -0
  568. package/skills/mutation-testing/SKILL.md +396 -0
  569. package/skills/mutation-testing/references/languages/csharp-stryker-net.md +676 -0
  570. package/skills/mutation-testing/references/languages/go-go-mutesting.md +95 -0
  571. package/skills/mutation-testing/references/languages/java-pitest.md +77 -0
  572. package/skills/mutation-testing/references/languages/javascript-stryker.md +188 -0
  573. package/skills/mutation-testing/references/languages/python-mutmut.md +97 -0
  574. package/skills/mutation-testing/references/time-estimation.md +34 -0
  575. package/skills/mutation-testing/references/tool-detection.md +15 -0
  576. package/skills/mutation-testing/references/workflow-callers.md +23 -0
  577. package/skills/mutation-testing/scripts/__pycache__/xunit_v3_feature_detector.cpython-314.pyc +0 -0
  578. package/skills/mutation-testing/scripts/csharp_stryker_net_slice_runner.py +635 -0
  579. package/skills/mutation-testing/scripts/csharp_stryker_net_status_loop.py +525 -0
  580. package/skills/mutation-testing/scripts/csharp_stryker_net_wrapper.py +681 -0
  581. package/skills/mutation-testing/scripts/mutation_baseline_reuse.py +292 -0
  582. package/skills/mutation-testing/scripts/mutation_exclude_policy.py +268 -0
  583. package/skills/mutation-testing/scripts/mutation_feasibility_gate.py +463 -0
  584. package/skills/mutation-testing/scripts/mutation_kill_headless.py +331 -0
  585. package/skills/mutation-testing/scripts/mutation_kill_insert.py +199 -0
  586. package/skills/mutation-testing/scripts/mutation_kill_insert_python.py +150 -0
  587. package/skills/mutation-testing/scripts/mutation_kill_loop.py +869 -0
  588. package/skills/mutation-testing/scripts/mutation_kill_loop_python.py +949 -0
  589. package/skills/mutation-testing/scripts/mutation_kill_retry.py +592 -0
  590. package/skills/mutation-testing/scripts/mutation_kill_shared.py +620 -0
  591. package/skills/mutation-testing/scripts/mutation_nightwatch.py +462 -0
  592. package/skills/mutation-testing/scripts/mutation_nightwatch_stacks.py +425 -0
  593. package/skills/mutation-testing/scripts/mutation_report.py +743 -0
  594. package/skills/mutation-testing/scripts/mutation_report_cli.py +175 -0
  595. package/skills/mutation-testing/scripts/mutation_safety_gate.py +69 -0
  596. package/skills/mutation-testing/scripts/stryker_shard_pipeline.py +847 -0
  597. package/skills/mutation-testing/scripts/stryker_shard_setup.py +440 -0
  598. package/skills/mutation-testing/scripts/stryker_timeout_retry.py +142 -0
  599. package/skills/mutation-testing/scripts/xunit_v3_feature_detector.py +341 -0
  600. package/skills/performance-benchmark/SKILL.md +174 -0
  601. package/skills/performance-benchmark/examples/report-format.md +43 -0
  602. package/skills/performance-benchmark/references/benchmark-script.md +169 -0
  603. package/skills/performance-metrics/SKILL.md +265 -0
  604. package/skills/plan/SKILL.md +199 -0
  605. package/skills/plan/references/gherkin-persistence.md +43 -0
  606. package/skills/plan/references/plan-template.md +182 -0
  607. package/skills/pr/SKILL.md +289 -0
  608. package/skills/pr/scripts/gate_retry_state.py +368 -0
  609. package/skills/project-init/README.md +141 -0
  610. package/skills/project-init/SKILL.md +1197 -0
  611. package/skills/project-init/evals/evals.json +200 -0
  612. package/skills/project-init/references/capability-tools.md +55 -0
  613. package/skills/project-init/references/configs.md +221 -0
  614. package/skills/property-based-testing/SKILL.md +121 -0
  615. package/skills/property-based-testing/fixtures/invariant_fixture.py +15 -0
  616. package/skills/property-based-testing/fixtures/js-roundtrip/README.md +42 -0
  617. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/LICENSE +21 -0
  618. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/README.md +263 -0
  619. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.d.ts +5165 -0
  620. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/fast-check.js +12147 -0
  621. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/package.json +3 -0
  622. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/cjs/types57/fast-check.d.ts +5165 -0
  623. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.d.ts +5165 -0
  624. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/fast-check.js +12011 -0
  625. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/rolldown-runtime-D7D4PA-g.js +13 -0
  626. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/lib/types57/fast-check.d.ts +5165 -0
  627. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/fast-check/package.json +94 -0
  628. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/LICENSE +21 -0
  629. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/README.md +168 -0
  630. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/RandomGenerator-DcXj09Ch.d.ts +14 -0
  631. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.d.ts +15 -0
  632. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformBigInt.js +38 -0
  633. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.d.ts +15 -0
  634. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat32.js +18 -0
  635. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.d.ts +15 -0
  636. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformFloat64.js +22 -0
  637. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.d.ts +15 -0
  638. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/distribution/uniformInt.js +134 -0
  639. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/RandomGenerator-DcXj09Ch.d.ts +14 -0
  640. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.d.ts +15 -0
  641. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformBigInt.js +37 -0
  642. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.d.ts +15 -0
  643. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat32.js +17 -0
  644. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.d.ts +15 -0
  645. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformFloat64.js +21 -0
  646. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.d.ts +15 -0
  647. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/distribution/uniformInt.js +133 -0
  648. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.d.ts +7 -0
  649. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/congruential32.js +44 -0
  650. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.d.ts +7 -0
  651. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/mersenne.js +90 -0
  652. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.d.ts +7 -0
  653. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xoroshiro128plus.js +80 -0
  654. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.d.ts +7 -0
  655. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/generator/xorshift128plus.js +78 -0
  656. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/package.json +3 -0
  657. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.d.ts +16 -0
  658. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/JumpableRandomGenerator.js +0 -0
  659. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.d.ts +2 -0
  660. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/types/RandomGenerator.js +0 -0
  661. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.d.ts +6 -0
  662. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/generateN.js +8 -0
  663. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.d.ts +12 -0
  664. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/purify.js +9 -0
  665. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.d.ts +6 -0
  666. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/esm/utils/skipN.js +6 -0
  667. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.d.ts +7 -0
  668. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/congruential32.js +46 -0
  669. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.d.ts +7 -0
  670. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/mersenne.js +92 -0
  671. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.d.ts +7 -0
  672. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xoroshiro128plus.js +82 -0
  673. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.d.ts +7 -0
  674. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/generator/xorshift128plus.js +80 -0
  675. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.d.ts +16 -0
  676. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/JumpableRandomGenerator.js +0 -0
  677. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.d.ts +2 -0
  678. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/types/RandomGenerator.js +0 -0
  679. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.d.ts +6 -0
  680. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/generateN.js +9 -0
  681. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.d.ts +12 -0
  682. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/purify.js +10 -0
  683. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.d.ts +6 -0
  684. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/lib/utils/skipN.js +7 -0
  685. package/skills/property-based-testing/fixtures/js-roundtrip/node_modules/pure-rand/package.json +133 -0
  686. package/skills/property-based-testing/fixtures/js-roundtrip/package-lock.json +1179 -0
  687. package/skills/property-based-testing/fixtures/js-roundtrip/package.json +14 -0
  688. package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.js +29 -0
  689. package/skills/property-based-testing/fixtures/js-roundtrip/roundtrip.properties.test.js +16 -0
  690. package/skills/property-based-testing/fixtures/no_property_fixture.py +10 -0
  691. package/skills/property-based-testing/fixtures/roundtrip_fixture.py +16 -0
  692. package/skills/property-based-testing/references/languages/javascript.md +54 -0
  693. package/skills/property-based-testing/scripts/detect_and_dispatch.py +80 -0
  694. package/skills/property-based-testing/scripts/hypothesis_scaffold.py +276 -0
  695. package/skills/proxy-resilience/SKILL.md +84 -0
  696. package/skills/quality-gate-pipeline/SKILL.md +184 -0
  697. package/skills/quality-targets-converge/SKILL.md +254 -0
  698. package/skills/repo-review/SKILL.md +159 -0
  699. package/skills/report-pdf/SKILL.md +66 -0
  700. package/skills/review/SKILL.md +47 -0
  701. package/skills/review-agent/SKILL.md +152 -0
  702. package/skills/review-summary/SKILL.md +73 -0
  703. package/skills/run-report/SKILL.md +70 -0
  704. package/skills/semantic-duplication-scan/SKILL.md +337 -0
  705. package/skills/semantic-scan/SKILL.md +53 -0
  706. package/skills/semgrep-analyze/SKILL.md +139 -0
  707. package/skills/setup/SKILL.md +1122 -0
  708. package/skills/ship/SKILL.md +240 -0
  709. package/skills/source-verification/SKILL.md +210 -0
  710. package/skills/source-verification/scripts/claim_extractor.py +155 -0
  711. package/skills/specs/.size-baseline.json +4 -0
  712. package/skills/specs/SKILL.md +243 -0
  713. package/skills/specs/references/completeness-checklist.md +83 -0
  714. package/skills/specs/references/extraction.md +58 -0
  715. package/skills/specs/references/glossary.md +59 -0
  716. package/skills/specs/references/persistence.md +115 -0
  717. package/skills/specs/references/predictability-check.md +77 -0
  718. package/skills/static-analysis-integration/SKILL.md +235 -0
  719. package/skills/static-analysis-integration/adapters/_envelope.py +26 -0
  720. package/skills/static-analysis-integration/adapters/jscpd-adapter.py +66 -0
  721. package/skills/static-analysis-integration/adapters/lizard-adapter.py +81 -0
  722. package/skills/static-analysis-integration/adapters/mypy-adapter.py +50 -0
  723. package/skills/static-analysis-integration/adapters/mypy-src-layout.py +93 -0
  724. package/skills/static-analysis-integration/adapters/security-review-adapter.py +212 -0
  725. package/skills/static-analysis-integration/maintenance.md +23 -0
  726. package/skills/static-analysis-integration/references/language-setup.md +228 -0
  727. package/skills/static-analysis-integration/references/sarif-parser.md +124 -0
  728. package/skills/static-analysis-integration/references/security-review-adapter.md +118 -0
  729. package/skills/static-analysis-integration/references/tool-configs.md +617 -0
  730. package/skills/static-analysis-integration/rulesets/pmd-quickstart.xml +24 -0
  731. package/skills/stryker-xunit-v2-shim/SKILL.md +274 -0
  732. package/skills/stryker-xunit-v2-shim/references/shim-howto.md +256 -0
  733. package/skills/stryker-xunit-v2-shim/scripts/generate_shim.py +143 -0
  734. package/skills/systematic-debugging/SKILL.md +130 -0
  735. package/skills/telemetry/SKILL.md +75 -0
  736. package/skills/test-audit-disable/SKILL.md +129 -0
  737. package/skills/test-design/SKILL.md +177 -0
  738. package/skills/test-design/scripts/__pycache__/internal_double_detector.cpython-314.pyc +0 -0
  739. package/skills/test-design/scripts/internal_double_detector.py +631 -0
  740. package/skills/test-design-advisor/SKILL.md +166 -0
  741. package/skills/test-driven-development/SKILL.md +169 -0
  742. package/skills/test-health/SKILL.md +262 -0
  743. package/skills/test-improve/SKILL.md +239 -0
  744. package/skills/test-improve/references/phase-0-approach-contract.md +228 -0
  745. package/skills/test-improve/references/phase-1-analyze.md +131 -0
  746. package/skills/test-improve/references/phase-2-baseline.md +121 -0
  747. package/skills/test-improve/references/phase-3-derive-gherkin.md +53 -0
  748. package/skills/test-improve/references/phase-4-plan-fixes.md +34 -0
  749. package/skills/test-improve/references/phase-5-improve.md +215 -0
  750. package/skills/test-improve/references/phase-6-refactor-decision.md +45 -0
  751. package/skills/test-improve/references/phase-7-refactor.md +44 -0
  752. package/skills/test-improve/references/phase-8-validate.md +66 -0
  753. package/skills/test-improve/references/phase-9-close-out-prompt.md +11 -0
  754. package/skills/test-improve/references/phase-9-report.md +62 -0
  755. package/skills/test-improve/references/review-loop.md +92 -0
  756. package/skills/test-improve/templates/executive-summary.md +123 -0
  757. package/skills/threat-modeling/SKILL.md +108 -0
  758. package/skills/triage/SKILL.md +211 -0
  759. package/skills/ubiquitous-language/SKILL.md +192 -0
  760. package/skills/ubiquitous-language/scripts/collect_domain_signals.py +300 -0
  761. package/skills/unfreeze/SKILL.md +37 -0
  762. package/skills/upgrade/SKILL.md +31 -0
  763. package/skills/upgrade/scripts/check_version_drift.py +113 -0
  764. package/skills/upgrade/scripts/enable_autoupdate.py +149 -0
  765. package/skills/version/SKILL.md +25 -0
  766. package/sync/__pycache__/sync_upstream.cpython-314.pyc +0 -0
  767. package/sync/sync_upstream.py +293 -0
  768. package/templates/ACCEPTED-RISKS.md.tmpl +46 -0
  769. package/templates/agents/agent-template.md +151 -0
  770. package/templates/agents/angular-testing.md +66 -0
  771. package/templates/agents/csharp-quality.md +63 -0
  772. package/templates/agents/esm-enforcer.md +52 -0
  773. package/templates/agents/front-end-testing.md +65 -0
  774. package/templates/agents/go-quality.md +65 -0
  775. package/templates/agents/python-quality.md +62 -0
  776. package/templates/agents/react-testing.md +61 -0
  777. package/templates/agents/ts-enforcer.md +60 -0
  778. package/templates/agents/twelve-factor-audit.md +49 -0
  779. package/tools/entropy-check.py +250 -0
  780. package/tools/model-hash-verify.py +213 -0
@@ -0,0 +1,623 @@
1
+ #!/usr/bin/env python3
2
+ """coverage_config.py — shared coverage-config module for multi-project
3
+ coverage discovery (issue #1759).
4
+
5
+ `coverage-baseline` and `coverage-delta` previously relied on a single,
6
+ hand-maintained coverage command per manifest. For a multi-project .NET
7
+ solution or JS/TS workspace, that meant a hand-picked, never-revisited
8
+ inclusion/exclusion list — the incident's root cause. This module owns the
9
+ mechanics both skills share: bootstrapping/loading `coverage-config.json`,
10
+ drift-checking it against fresh discovery, weighted-merging per-project
11
+ coverage reports, and flagging a changed measurement basis after a
12
+ bootstrap.
13
+
14
+ **Identity contract.** An accounting unit's "path" is always the exact,
15
+ unmodified string the corresponding `discover_*` function returns for that
16
+ entry — whatever each stack calls the unit natively (a .NET *project*, a JS/TS
17
+ *package*, a Maven *module*, a Gradle *subproject*). No cross-stack
18
+ normalization, case-folding, or
19
+ separator translation is ever applied when matching a discovered path
20
+ against `included`/`excluded` entries — a POSIX-style path and an arbitrary
21
+ opaque label are both treated as opaque strings and compared for exact
22
+ equality only.
23
+
24
+ Stdlib-only (ADR 0014/0015).
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import dataclasses
30
+ import json
31
+ import os
32
+ import re
33
+ import tempfile
34
+ from datetime import datetime, timezone
35
+ from enum import Enum
36
+ from pathlib import Path
37
+
38
+
39
+ class TestClassification(Enum):
40
+ """How a discovered project/package was classified by a `discover_*`
41
+ function. `AMBIGUOUS` covers markers this repo's discovery deliberately
42
+ declines to resolve with certainty (e.g. a conditioned MSBuild property)
43
+ — it is never treated as a negative ("not a test project") result."""
44
+
45
+ TEST = "test"
46
+ NOT_TEST = "not_test"
47
+ AMBIGUOUS = "ambiguous"
48
+
49
+
50
+ def needs_accounting(classification: TestClassification) -> bool:
51
+ """True when a discovered project must appear in `included` or
52
+ `excluded` (with a reason) — true for `TEST` and `AMBIGUOUS`, false only
53
+ for `NOT_TEST`."""
54
+ return classification in (TestClassification.TEST, TestClassification.AMBIGUOUS)
55
+
56
+
57
+ # Shared discovery-result signals — one canonical contract, imported by every
58
+ # `coverage_discovery_<stack>.py` module rather than each independently
59
+ # converging on a matching shape.
60
+ DISCOVERY_NOT_APPLICABLE = {"signal": "not_applicable"}
61
+
62
+
63
+ def discovery_error(message: str) -> dict:
64
+ """Return the shared discovery-error signal shape, naming `message`."""
65
+ return {"signal": "error", "message": message}
66
+
67
+
68
+ def is_within(resolved: Path, root: Path) -> bool:
69
+ """True when `resolved` (already resolved — this performs no resolution
70
+ of its own) equals `root` or is nested inside it. The shared containment
71
+ predicate every `discover_<stack>` module applies before reading or
72
+ classifying a resolved path — previously factored only in
73
+ `coverage_discovery_java.py` and open-coded independently in
74
+ `coverage_discovery_js.py` and `coverage_discovery_dotnet.py` (issues
75
+ #1839/#1848/#1851).
76
+
77
+ Canonical argument order — read this before adding a new call site:
78
+ `is_within(item, container)`. The FIRST argument is the path whose
79
+ containment is being checked; the SECOND is the directory it must be
80
+ inside. Equivalently, `is_within(item, container)` reads as "is `item`
81
+ within `container`?" — same order as `coverage_discovery_dotnet.py`'s and
82
+ `stryker_shard_setup.py`'s calls. #1903 found an independent
83
+ reimplementation in `run_integration_eval.py` with this order inverted
84
+ (container first); that inversion is exactly what a reader moving
85
+ between files cannot safely assume is consistent, hence this note.
86
+ """
87
+ return resolved == root or root in resolved.parents
88
+
89
+
90
+ # Namespace prefix stripped by `local_name` below. Anchored to a leading
91
+ # `{...}` only, rather than searching for the LAST `}` in the tag string —
92
+ # the anchored form is what stays correct for a local name that itself
93
+ # legitimately contains `}` (never happens in practice, but the anchoring
94
+ # costs nothing and is strictly safer).
95
+ _XML_NS_RE = re.compile(r"^\{[^}]*\}")
96
+
97
+
98
+ def local_name(tag) -> str:
99
+ """The namespace-stripped local name of an ElementTree tag (`{ns}Tag` ->
100
+ `Tag`). Tolerates a non-`str` tag (returns `""`) — ElementTree gives
101
+ comment/processing-instruction nodes a non-string, callable `tag`, which
102
+ a `tag.split("}", 1)`-style implementation raises on. The shared,
103
+ canonical implementation: `coverage_discovery_java.py` originally had
104
+ this exact regex-anchored, non-str-safe version; `coverage_discovery_dotnet.py`
105
+ had a second, less-safe `tag.split("}", 1)[-1]` implementation (issues
106
+ #1839/#1848/#1851)."""
107
+ return _XML_NS_RE.sub("", tag if isinstance(tag, str) else "")
108
+
109
+
110
+ # A build file consumed by discovery never legitimately carries a DOCTYPE or
111
+ # ENTITY declaration — presence of either is a zero-false-positive signal of
112
+ # an entity-expansion attempt, not a legitimate build file.
113
+ UNSAFE_XML_MARKERS = ("<!DOCTYPE", "<!ENTITY")
114
+
115
+
116
+ def find_unsafe_xml_marker(data: bytes):
117
+ """Return the first disallowed marker (`UNSAFE_XML_MARKERS`) found in
118
+ `data`'s raw bytes, or in `data` decoded as UTF-16LE/UTF-16BE — the other
119
+ encodings expat auto-detects and natively parses besides UTF-8.
120
+ ASCII/Latin-1 are byte-identical to UTF-8 for these ASCII-only tokens, so
121
+ no separate check is needed for those; decoding the raw bytes as Latin-1
122
+ (a total, never-raising 1:1 byte<->codepoint mapping) covers the
123
+ UTF-8/ASCII/Latin-1 case in one pass. Returns `None` when no marker is
124
+ found in any of the three views."""
125
+ raw_text = data.decode("latin-1")
126
+ utf16le_text = data.decode("utf-16le", errors="ignore")
127
+ utf16be_text = data.decode("utf-16be", errors="ignore")
128
+ for marker in UNSAFE_XML_MARKERS:
129
+ if marker in raw_text or marker in utf16le_text or marker in utf16be_text:
130
+ return marker
131
+ return None
132
+
133
+
134
+ def read_and_screen_xml(path: Path):
135
+ """Read `path` ONCE as bytes and screen the FULL content — never only a
136
+ fixed-size prefix — for a disallowed `<!DOCTYPE`/`<!ENTITY` declaration
137
+ (XML entity-expansion hardening). `xml.etree.ElementTree` is explicitly
138
+ not secure against maliciously constructed data, and discovery parses
139
+ repository-supplied build files, so every stack's XML read routes through
140
+ this one screen.
141
+
142
+ Returns `(data, None)` when the file looks safe to parse — the caller
143
+ parses these SAME already-read bytes via `ET.fromstring`, never re-opening
144
+ `path`, which closes the check-then-use double-open gap a
145
+ `read`-then-`ET.parse(path)` pair leaves open. Returns
146
+ `(None, discovery_error(...))` otherwise (unreadable file, or a
147
+ disallowed marker found).
148
+
149
+ Shared by every discovery stack that parses repository-supplied XML — a
150
+ second copy of this screen is a review finding, not a style choice: one
151
+ stack once shipped without a screen at all, which is exactly the drift a
152
+ single shared implementation prevents. Callers are deliberately not
153
+ enumerated here; a shared helper should not name its consumers."""
154
+ try:
155
+ data = path.read_bytes()
156
+ except OSError as exc:
157
+ return None, discovery_error(f"Could not read '{path}': {exc}")
158
+ marker = find_unsafe_xml_marker(data)
159
+ if marker is not None:
160
+ return None, discovery_error(
161
+ f"Refusing to parse '{path}': contains a disallowed "
162
+ f"'{marker}' declaration (XML entity-expansion hardening)."
163
+ )
164
+ return data, None
165
+
166
+
167
+ # `coverage-config.json`'s exclusion schema (`excluded: [{"path", "reason"}]`)
168
+ # is intentionally NOT the same schema as the sibling `mutation_exclude_policy.py`
169
+ # module's `mutation-exclude-policy.json` (`{schema_version, always: [...],
170
+ # propose_and_ask: [...]}`, unit key `file` rather than `path`) — issue #1853.
171
+ # The two are different bounded contexts (coverage-accounting ledger vs.
172
+ # mutation-exclude proposals) with no shared consumer today; do not assume
173
+ # they should be interchangeable or unify them.
174
+ def load_or_bootstrap(
175
+ config_path: Path, discovered_projects: list, now_iso: str
176
+ ) -> tuple:
177
+ """Load `config_path`, or bootstrap it from `discovered_projects` if
178
+ absent (including malformed/corrupt — treated as absent, matching
179
+ `coverage-baseline`'s existing-baseline guard precedent).
180
+
181
+ `discovered_projects` uses the SAME shape `drift_check` expects: a list
182
+ of `{"path": ..., "classification": TestClassification}` entries. Only
183
+ entries where `needs_accounting(classification)` is true are written to
184
+ `included` — a `NOT_TEST` entry is correctly excluded, and the raw
185
+ `TestClassification` enum value never reaches `atomic_write_json` (it is
186
+ not JSON-serializable).
187
+
188
+ Returns `(config, notice)`. `notice` is `None` when an existing config
189
+ was read verbatim (no write happens on this path — `baseline-coverage.json`
190
+ is never touched by this function either way). On bootstrap, `notice` is
191
+ the exact operator-facing string naming how many projects were included.
192
+
193
+ `now_iso` must use the identical ISO-8601 UTC format (with a literal `Z`
194
+ suffix) as `baseline-coverage.json`'s `captured_at` values — this is what
195
+ makes `measurement_basis_notice`'s later datetime comparison valid.
196
+
197
+ **Concurrency.** Two invocations racing against the same absent
198
+ `config_path` are mutually excluded by a create-exclusive `.lock`
199
+ sibling file (`os.O_CREAT | os.O_EXCL`) rather than the plain existence
200
+ check above, which is subject to a TOCTOU gap between the check and the
201
+ write. The loser of the lock never writes — it re-reads `config_path`
202
+ (the winner should have just written it) and returns that content
203
+ instead of overwriting it. The lock file is removed in a `finally` by
204
+ whichever call created it.
205
+ """
206
+ existing = _read_existing_config(config_path)
207
+ if existing is not None:
208
+ return existing, None
209
+
210
+ included = [
211
+ entry["path"]
212
+ for entry in discovered_projects
213
+ if needs_accounting(entry["classification"])
214
+ ]
215
+ config = {
216
+ "schema_version": 1,
217
+ "included": included,
218
+ "excluded": [],
219
+ "bootstrapped_at": now_iso,
220
+ }
221
+
222
+ config_path.parent.mkdir(parents=True, exist_ok=True)
223
+ lock_path = Path(f"{config_path}.lock")
224
+ try:
225
+ lock_fd = os.open(str(lock_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY)
226
+ except FileExistsError:
227
+ # Another call is concurrently bootstrapping the same config path —
228
+ # it should have just written config_path; return that content
229
+ # rather than racing it to overwrite. If it hasn't written yet (a
230
+ # genuinely stuck concurrent bootstrapper), fail loudly rather than
231
+ # silently proceeding to overwrite.
232
+ winner = _read_existing_config(config_path)
233
+ if winner is not None:
234
+ return winner, None
235
+ raise RuntimeError(
236
+ f"'{lock_path}' is held by a concurrent load_or_bootstrap call, "
237
+ f"but '{config_path}' has not been written yet; refusing to "
238
+ f"overwrite it. Re-run once the concurrent call completes."
239
+ )
240
+
241
+ try:
242
+ os.close(lock_fd)
243
+ # Double-checked locking: another call may have won the race between
244
+ # our existence check above and acquiring this lock, written
245
+ # config_path, and already released its own lock before we got here.
246
+ # Re-check now, inside the lock, so we never overwrite that write.
247
+ winner = _read_existing_config(config_path)
248
+ if winner is not None:
249
+ return winner, None
250
+ atomic_write_json(config_path, config)
251
+ finally:
252
+ lock_path.unlink(missing_ok=True)
253
+
254
+ notice = (
255
+ f"coverage-config.json did not exist; created it from fresh discovery "
256
+ f"with {len(included)} project(s) included and zero "
257
+ f"exclusions. Review and add exclusions (with a reason) if any "
258
+ f"project should not count toward coverage."
259
+ )
260
+ return config, notice
261
+
262
+
263
+ def _read_existing_config(config_path: Path) -> dict | None:
264
+ """Return the parsed config, or `None` if absent/malformed."""
265
+ if not config_path.is_file():
266
+ return None
267
+ try:
268
+ return json.loads(config_path.read_text(encoding="utf-8"))
269
+ except (OSError, json.JSONDecodeError):
270
+ return None
271
+
272
+
273
+ def _normalize_excluded(excluded_entries: list) -> list:
274
+ """Normalize `excluded` entries to `{"path": ..., "reason": ...}` dicts,
275
+ tolerating a bare path string (normalized to `{"path": s, "reason":
276
+ ""}`) — the shared normalization `drift_check` and
277
+ `format_active_exclusions` both need, extracted so they share one
278
+ implementation rather than two byte-identical copies."""
279
+ return [
280
+ {"path": entry, "reason": ""} if isinstance(entry, str) else entry
281
+ for entry in excluded_entries
282
+ ]
283
+
284
+
285
+ def _validate_config_shape(config: dict) -> None:
286
+ """Validate `config`'s `included`/`excluded` shape, raising `ValueError`
287
+ naming the malformed field or entry. Extracted from `drift_check` so
288
+ shape validation and drift computation are each independently readable
289
+ — the same extraction precedent as `_format_hard_failure_message`/
290
+ `_format_stale_warning` below (issue #1858). Raises before any drift
291
+ computation runs; callers never see a partially-computed result on a
292
+ malformed config."""
293
+ included_list = config.get("included", [])
294
+ excluded_entries = config.get("excluded", [])
295
+
296
+ if "included" in config and not isinstance(config["included"], list):
297
+ raise ValueError(
298
+ "coverage-config.json \"included\" must be a list of path "
299
+ f"strings; got {type(config['included']).__name__} instead."
300
+ )
301
+ if "excluded" in config and not isinstance(config["excluded"], list):
302
+ raise ValueError(
303
+ "coverage-config.json \"excluded\" must be a list of "
304
+ "{\"path\": ..., \"reason\": ...} entries; got "
305
+ f"{type(config['excluded']).__name__} instead."
306
+ )
307
+
308
+ for entry in included_list:
309
+ if not isinstance(entry, str):
310
+ raise ValueError( # noqa: TRY004 — ValueError is the documented, tested contract for malformed config (see docstring; test_coverage_config.py asserts ValueError)
311
+ "coverage-config.json \"included\" entries must all be "
312
+ f"path strings; got {entry!r} instead."
313
+ )
314
+ for entry in excluded_entries:
315
+ if not isinstance(entry, str) and (
316
+ not isinstance(entry, dict) or "path" not in entry
317
+ ):
318
+ raise ValueError(
319
+ "coverage-config.json \"excluded\" entries must all be a "
320
+ "path string or a {\"path\": ..., \"reason\": ...} entry "
321
+ f"carrying a \"path\" key; got {entry!r} instead."
322
+ )
323
+
324
+
325
+ def drift_check(config: dict, discovered_projects: list) -> dict:
326
+ """Compare `config`'s `included`/`excluded` entries against fresh
327
+ `discovered_projects` (a list of `{"path": ..., "classification":
328
+ TestClassification}` entries).
329
+
330
+ Returns a structured result:
331
+
332
+ - `unaccounted`: discovered paths needing accounting
333
+ (`needs_accounting(classification)` true) that are in neither
334
+ `included` nor `excluded`.
335
+ - `unaccounted_ambiguous`: the subset of `unaccounted` classified
336
+ `AMBIGUOUS`.
337
+ - `conflicts`: paths present in both `included` and `excluded`.
338
+ - `stale_exclusions`: `excluded` entries whose path no longer appears in
339
+ `discovered_projects` at all (regardless of classification).
340
+ - `hard_failure`: true iff `unaccounted` or `conflicts` is non-empty.
341
+ - `hard_failure_message`: the exact operator-facing failure text, or
342
+ `None` when `hard_failure` is false.
343
+ - `stale_warning_message`: the exact non-blocking warning text (one
344
+ line per stale entry), or `None` when `stale_exclusions` is empty.
345
+
346
+ Malformed-but-parseable configs degrade gracefully rather than raising,
347
+ matching `_read_existing_config`'s own "malformed → treated as absent"
348
+ tolerance: a bare string in `excluded` (instead of `{"path": ...,
349
+ "reason": ...}`) is normalized to `{"path": s, "reason": ""}`, and an
350
+ excluded entry missing `"reason"` renders as `<no reason recorded>`
351
+ rather than raising `KeyError`. A schema violation this module cannot
352
+ silently coerce without failing in the wrong direction — `"included"` or
353
+ `"excluded"` present but not a list at all — raises `ValueError` naming
354
+ the malformed field, rather than degrading `path in included_list` to a
355
+ substring test.
356
+ """
357
+ _validate_config_shape(config)
358
+
359
+ included_list = config.get("included", [])
360
+ excluded_entries = config.get("excluded", [])
361
+
362
+ normalized_excluded = _normalize_excluded(excluded_entries)
363
+ excluded_by_path = {entry["path"]: entry for entry in normalized_excluded}
364
+ excluded_paths = set(excluded_by_path)
365
+ included_paths = set(included_list)
366
+
367
+ conflicts = [path for path in included_list if path in excluded_paths]
368
+
369
+ unaccounted: list = []
370
+ unaccounted_ambiguous: list = []
371
+ for entry in discovered_projects:
372
+ path = entry["path"]
373
+ classification = entry["classification"]
374
+ if not needs_accounting(classification):
375
+ continue
376
+ if path in included_paths or path in excluded_paths:
377
+ continue
378
+ unaccounted.append(path)
379
+ if classification is TestClassification.AMBIGUOUS:
380
+ unaccounted_ambiguous.append(path)
381
+
382
+ discovered_paths = {entry["path"] for entry in discovered_projects}
383
+ stale_exclusions = [
384
+ excluded_by_path[path]
385
+ for path in excluded_by_path
386
+ if path not in discovered_paths
387
+ ]
388
+
389
+ hard_failure = bool(unaccounted) or bool(conflicts)
390
+ hard_failure_message = (
391
+ _format_hard_failure_message(unaccounted, unaccounted_ambiguous, conflicts)
392
+ if hard_failure
393
+ else None
394
+ )
395
+ stale_warning_message = (
396
+ _format_stale_warning(stale_exclusions) if stale_exclusions else None
397
+ )
398
+
399
+ return {
400
+ "unaccounted": unaccounted,
401
+ "unaccounted_ambiguous": unaccounted_ambiguous,
402
+ "conflicts": conflicts,
403
+ "stale_exclusions": stale_exclusions,
404
+ "hard_failure": hard_failure,
405
+ "hard_failure_message": hard_failure_message,
406
+ "stale_warning_message": stale_warning_message,
407
+ }
408
+
409
+
410
+ def _format_hard_failure_message(
411
+ unaccounted: list, unaccounted_ambiguous: list, conflicts: list
412
+ ) -> str:
413
+ """Build the exact operator-facing failure text for `drift_check`'s
414
+ `unaccounted`/`conflicts` findings. Pure string formatting, extracted
415
+ from `drift_check` so the data analysis above stays readable."""
416
+ sentences = []
417
+ if unaccounted:
418
+ sentences.append(
419
+ f"Coverage capture stopped: {len(unaccounted)} discovered "
420
+ f"project(s) are not accounted for in coverage-config.json: "
421
+ f"{', '.join(unaccounted)}. Add each to \"included\", or to "
422
+ f"\"excluded\" with a \"reason\", then re-run."
423
+ )
424
+ if unaccounted_ambiguous:
425
+ sentences.append(
426
+ f"{len(unaccounted_ambiguous)} of these could not be classified "
427
+ f"with certainty from their test markers: "
428
+ f"{', '.join(unaccounted_ambiguous)}."
429
+ )
430
+ if conflicts:
431
+ conflict_sentence = (
432
+ f"{len(conflicts)} project(s) are listed in both "
433
+ f"\"included\" and \"excluded\": {', '.join(conflicts)}. "
434
+ f"Remove each from one list, then re-run."
435
+ )
436
+ if sentences:
437
+ sentences.append(conflict_sentence)
438
+ else:
439
+ sentences.append(f"Coverage capture stopped: {conflict_sentence}")
440
+ return " ".join(sentences)
441
+
442
+
443
+ def _format_stale_warning(stale_exclusions: list) -> str:
444
+ """Build the exact non-blocking warning text for `drift_check`'s
445
+ `stale_exclusions` — one line per entry, in `excluded`'s order. A
446
+ missing `"reason"` (malformed-but-parseable config) renders as
447
+ `<no reason recorded>` rather than raising `KeyError`."""
448
+ return "\n".join(
449
+ f"Warning: excluded entry '{entry['path']}' (reason: "
450
+ f"'{entry.get('reason', '<no reason recorded>')}') no longer "
451
+ f"matches any discovered project — it may have been removed, "
452
+ f"renamed, or the config is out of date. This does not block the "
453
+ f"run."
454
+ for entry in stale_exclusions
455
+ )
456
+
457
+
458
+ def format_active_exclusions(config: dict) -> str | None:
459
+ """Render `config`'s currently-active `excluded` entries as one line per
460
+ entry: `"Excluded: '<path>' (reason: '<reason>')"`, joined with
461
+ newlines. Returns `None` when `excluded` is empty.
462
+
463
+ This is informational, always-shown context — printed on EVERY run
464
+ regardless of staleness — distinct from `drift_check`'s
465
+ `stale_warning_message`, which flags a DIFFERENT problem: an excluded
466
+ path no longer being discovered at all. An exclusion can be active
467
+ (still discovered, reason unchanged) and still worth surfacing so an
468
+ operator sees what is currently excluded and why, on every run — not
469
+ only when something about it goes stale.
470
+
471
+ Same bare-string/missing-`"reason"` tolerance as `_format_stale_warning`."""
472
+ excluded_entries = config.get("excluded", [])
473
+ if not excluded_entries:
474
+ return None
475
+ normalized = _normalize_excluded(excluded_entries)
476
+ return "\n".join(
477
+ f"Excluded: '{entry['path']}' (reason: "
478
+ f"'{entry.get('reason', '<no reason recorded>')}')"
479
+ for entry in normalized
480
+ )
481
+
482
+
483
+ _WEIGHTED_MERGE_REQUIRED_FIELDS = (
484
+ "covered_statements",
485
+ "total_statements",
486
+ "covered_branches",
487
+ "total_branches",
488
+ )
489
+
490
+
491
+ def weighted_merge(project_reports: list) -> dict:
492
+ """Merge per-project coverage reports, weighted by statement/branch
493
+ count — never a per-project average.
494
+
495
+ Each entry in `project_reports` carries `{covered_statements,
496
+ total_statements, covered_branches, total_branches}` — a plain dict, or
497
+ a `coverage_report_parse.CoverageRecord` (e.g. `aggregate()`'s output)
498
+ consumed directly, with no translation layer, since both already use
499
+ this same field-name vocabulary. Returns `{line_pct, branch_pct}`,
500
+ summed across all reports before dividing. Either percentage is `None`
501
+ (JSON `null`) when its total is 0 across all reports, matching
502
+ `coverage-baseline`'s existing Go-coverage `null` convention. A field
503
+ valued `None` in a per-project report (this repo's own Go-coverage
504
+ convention for a tool with no native branch coverage) coerces to 0
505
+ rather than raising `TypeError`, degrading to the same zero-total →
506
+ `None` result.
507
+
508
+ A count field **entirely absent** from a report dict is a different,
509
+ louder case: it means the report is incomplete (a per-project report
510
+ carrying only percentages with no raw counts), never the legitimate
511
+ zero-total-for-this-metric convention above. This is validated in an
512
+ upfront pass, before any summing, and returns the shared
513
+ `discovery_error(...)` signal naming the missing field(s) rather than
514
+ silently proceeding to a null/0 baseline — "key entirely absent" and
515
+ "key present with value `None`" must never be treated identically.
516
+ """
517
+ project_reports = [
518
+ dataclasses.asdict(report) if dataclasses.is_dataclass(report) else report
519
+ for report in project_reports
520
+ ]
521
+ for report in project_reports:
522
+ missing_fields = [
523
+ field for field in _WEIGHTED_MERGE_REQUIRED_FIELDS if field not in report
524
+ ]
525
+ if missing_fields:
526
+ return discovery_error(
527
+ f"Coverage report is missing required count field(s): {missing_fields}"
528
+ )
529
+
530
+ total_statements = sum(
531
+ (report.get("total_statements") or 0) for report in project_reports
532
+ )
533
+ covered_statements = sum(
534
+ (report.get("covered_statements") or 0) for report in project_reports
535
+ )
536
+ total_branches = sum(
537
+ (report.get("total_branches") or 0) for report in project_reports
538
+ )
539
+ covered_branches = sum(
540
+ (report.get("covered_branches") or 0) for report in project_reports
541
+ )
542
+
543
+ line_pct = (
544
+ covered_statements / total_statements * 100 if total_statements else None
545
+ )
546
+ branch_pct = covered_branches / total_branches * 100 if total_branches else None
547
+
548
+ return {"line_pct": line_pct, "branch_pct": branch_pct}
549
+
550
+
551
+ def _parse_iso8601(value: str) -> datetime | None:
552
+ """Parse an ISO-8601 timestamp, substituting a trailing `Z` with
553
+ `+00:00` first (`datetime.fromisoformat` doesn't accept a bare `Z`).
554
+ Returns `None` on any unparseable input rather than raising — callers
555
+ treat that as "cannot compare, no notice".
556
+
557
+ A parsed value with no timezone (naive — no explicit `Z`/offset in the
558
+ input) is assumed UTC. `bootstrapped_at` is always `Z`-suffixed (per
559
+ `load_or_bootstrap`), but `captured_at` may come from a pre-existing
560
+ `baseline-coverage.json` written before this feature, with no timezone
561
+ guarantee at all — comparing a naive and an aware `datetime` raises
562
+ `TypeError`, so every value returned here is aware, treating an absent
563
+ offset as the safe, conservative UTC assumption this codebase already
564
+ uses elsewhere."""
565
+ try:
566
+ parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
567
+ except (TypeError, ValueError, AttributeError):
568
+ return None
569
+ if parsed.tzinfo is None:
570
+ parsed = parsed.replace(tzinfo=timezone.utc)
571
+ return parsed
572
+
573
+
574
+ def measurement_basis_notice(bootstrapped_at, captured_at: str):
575
+ """Return a non-blocking notice when `captured_at` predates
576
+ `bootstrapped_at` — the comparison baseline was captured before
577
+ multi-project discovery existed, so a delta against it may reflect a
578
+ widened measurement scope rather than a real coverage change.
579
+
580
+ Returns `None` when `bootstrapped_at` is `None` (config was never
581
+ bootstrapped, or predates this feature), when either value fails to
582
+ parse as ISO-8601, or when `captured_at >= bootstrapped_at` (compared as
583
+ parsed `datetime` objects, not raw strings — this correctly orders
584
+ equivalent-but-differently-spelled timestamps, e.g. a `Z` suffix vs an
585
+ explicit `+00:00` offset for the same instant). A same-instant capture
586
+ is not "predates" and also returns `None`.
587
+ """
588
+ if bootstrapped_at is None:
589
+ return None
590
+ bootstrapped_dt = _parse_iso8601(bootstrapped_at)
591
+ captured_dt = _parse_iso8601(captured_at)
592
+ if bootstrapped_dt is None or captured_dt is None:
593
+ return None
594
+ if captured_dt >= bootstrapped_dt:
595
+ return None
596
+ return (
597
+ f"Note: this baseline predates multi-project discovery (bootstrapped "
598
+ f"{bootstrapped_at}). This delta may reflect a widened measurement "
599
+ f"scope, not only a real coverage change. Consider re-running "
600
+ f"/coverage-baseline for a directly comparable baseline."
601
+ )
602
+
603
+
604
+ def atomic_write_json(path: Path, obj: dict) -> None:
605
+ """Write `obj` as JSON to `path` atomically (temp-file-then-rename),
606
+ matching `baseline-coverage.json`'s existing write convention. Shared
607
+ across this module's own callers and Slice 4/5's — reuse this instead of
608
+ re-implementing temp-file-then-rename.
609
+
610
+ On any failure (e.g. `obj` is not JSON-serializable), the temp file is
611
+ removed before the exception propagates — the caller never leaks a
612
+ `.tmp` file it has no handle to."""
613
+ path.parent.mkdir(parents=True, exist_ok=True)
614
+ fd, tmp_name = tempfile.mkstemp(
615
+ prefix=f".{path.name}-", suffix=".tmp", dir=str(path.parent)
616
+ )
617
+ try:
618
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
619
+ json.dump(obj, handle, indent=2)
620
+ os.replace(tmp_name, path)
621
+ except BaseException:
622
+ Path(tmp_name).unlink(missing_ok=True)
623
+ raise