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,743 @@
1
+ #!/usr/bin/env python3
2
+ """mutation_report.py — parse a native mutation report and compute scores.
3
+
4
+ Generic, stdlib-only, cross-platform (macOS, Linux, Windows). Carries no repo-specific literal —
5
+ project names, controller names, and test-library names live in the report
6
+ being parsed, never in this module.
7
+
8
+ Two native report shapes are supported, normalized to the same internal
9
+ ``{"files": {"<path>": {"mutants": [...]}}}`` dict before scoring:
10
+
11
+ - Stryker / Stryker.NET's own ``mutation-report.json`` — read directly via
12
+ the path-based functions (``score_report``, ``survivors_by_mutator``, …).
13
+ - mutmut's ``mutmut junitxml`` output — mutmut has no JSON report of its
14
+ own; ``parse_mutmut_junitxml`` converts it into the same internal shape so
15
+ every downstream function (scoring, survivor extraction, file discovery)
16
+ works identically regardless of which tool produced the data.
17
+
18
+ Two scores are computed side by side:
19
+
20
+ - **honest score** = Killed / (Killed + Survived + NoCoverage)
21
+ A Timeout is *not* a demonstrated kill — the test never asserted anything;
22
+ the mutant merely ran too long. Counting Timeouts as kills inflates the
23
+ score, so the honest score excludes them from the numerator entirely.
24
+
25
+ - **reported score** = (Killed + Timeout) / (Killed + Survived + Timeout + NoCoverage)
26
+ The score Stryker itself reports, kept alongside for parity so the gap
27
+ between "what the tool claims" and "what the tests actually prove" is
28
+ visible rather than hidden.
29
+
30
+ Both scores are expressed as percentages in [0, 100]. An absent or empty
31
+ report yields zeroed scores rather than raising.
32
+
33
+ Stryker.NET mutant statuses: Killed, Survived, Timeout, NoCoverage, Ignored,
34
+ CompileError. Only the first four participate in scoring; Ignored and
35
+ CompileError are excluded from both numerator and denominator.
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ import json
41
+ import xml.etree.ElementTree as ET
42
+ from dataclasses import dataclass
43
+ from pathlib import Path
44
+
45
+ # Stryker.NET mutant-status vocabulary — the single source of truth for these
46
+ # literals across the whole pipeline. Sibling modules import these constants
47
+ # rather than re-typing the raw strings, so the status vocabulary lives in one
48
+ # place (AC4). Only these four participate in the score denominators.
49
+ STATUS_KILLED = "Killed"
50
+ STATUS_SURVIVED = "Survived"
51
+ STATUS_TIMEOUT = "Timeout"
52
+ STATUS_NO_COVERAGE = "NoCoverage"
53
+
54
+ # Mutant dict key carrying Stryker's "this mutant sits in module-init code,
55
+ # not per-test code" flag. Centralized here alongside the status vocabulary
56
+ # above so the literal isn't repeated across the functions that read it.
57
+ MUTANT_STATIC_KEY = "static"
58
+
59
+ # Fixed reason string attached to every accepted-static-survivor entry
60
+ # (see ``accepted_static_survivors``) — a static-flagged Survived mutant is
61
+ # not equivalent and not fixed; it is deliberately deferred because killing
62
+ # it would require a full-suite re-run rather than a single-mutant one.
63
+ ACCEPTED_STATIC_REASON = "static — verification requires a full-suite re-run"
64
+
65
+ # ``status`` value for every accepted-static-survivor entry — a sibling
66
+ # constant to ``ACCEPTED_STATIC_REASON`` so the entry's ``"status"`` field is
67
+ # never a bare string literal, matching every other status value in this
68
+ # module (``STATUS_KILLED`` etc. above).
69
+ ACCEPTED_STATIC_ENTRY_STATUS = "accepted"
70
+
71
+
72
+ @dataclass(frozen=True)
73
+ class ScoreSummary:
74
+ """Both scores plus the two counts that distinguish them.
75
+
76
+ ``honest_score`` and ``reported_score`` are percentages in [0, 100].
77
+ ``timeout`` and ``no_coverage`` are raw mutant counts, surfaced so a
78
+ caller can show *why* the two scores diverge.
79
+ """
80
+
81
+ killed: int
82
+ survived: int
83
+ timeout: int
84
+ no_coverage: int
85
+ honest_score: float
86
+ reported_score: float
87
+
88
+
89
+ def load_report(report_path: Path) -> dict:
90
+ """Return the parsed report, or ``{}`` when the file is absent, empty,
91
+ malformed JSON, or valid JSON that isn't a dict (e.g. a report file
92
+ containing ``[]`` or ``42``) — never raises."""
93
+ path = Path(report_path)
94
+ if not path.exists():
95
+ return {}
96
+ text = path.read_text(encoding="utf-8").strip()
97
+ if not text:
98
+ return {}
99
+ try:
100
+ parsed = json.loads(text)
101
+ except json.JSONDecodeError:
102
+ return {}
103
+ return parsed if isinstance(parsed, dict) else {}
104
+
105
+
106
+ def _iter_mutants(data: dict):
107
+ """Yield every mutant dict across all files in a parsed report."""
108
+ for info in data.get("files", {}).values():
109
+ yield from info.get("mutants", [])
110
+
111
+
112
+ def _find_file_entry(data: dict, file_path: str) -> tuple[str, dict] | None:
113
+ """Return the matched ``(report_key, info)`` pair for one file from an
114
+ already-parsed report dict.
115
+
116
+ Matches ``file_path`` against report keys by exact match first, then by
117
+ basename, so a caller can pass either the full report key or just the
118
+ filename. Returns ``None`` when no file matches. ``report_key`` is the
119
+ key exactly as the report emits it — a caller that only passed a
120
+ basename (or an absolute path) gets back the matched report key, not an
121
+ echo of its own input.
122
+ """
123
+ files = data.get("files", {})
124
+
125
+ info = files.get(file_path)
126
+ if info is not None:
127
+ return file_path, info
128
+
129
+ target = Path(file_path).name
130
+ for key, value in files.items():
131
+ if Path(key).name == target:
132
+ return key, value
133
+ return None
134
+
135
+
136
+ def _find_file_info(data: dict, file_path: str) -> dict | None:
137
+ """Return one file's report entry (the ``{"mutants": [...]}`` dict) from
138
+ an already-parsed report dict.
139
+
140
+ Matches ``file_path`` against report keys by exact match first, then by
141
+ basename, so a caller can pass either the full report key or just the
142
+ filename. Returns ``None`` when no file matches. Thin wrapper around
143
+ ``_find_file_entry`` that discards the matched key — callers that need
144
+ the matched key too (e.g. ``_accepted_static_survivors_from_data``) call
145
+ ``_find_file_entry`` directly instead.
146
+ """
147
+ entry = _find_file_entry(data, file_path)
148
+ return entry[1] if entry is not None else None
149
+
150
+
151
+ def _tally(mutants: list[dict]) -> ScoreSummary:
152
+ """Compute the honest and reported scores for a flat list of mutant
153
+ dicts (one file's ``mutants`` list, or every file's combined)."""
154
+ killed = survived = timeout = no_coverage = 0
155
+ for mutant in mutants:
156
+ status = mutant.get("status")
157
+ if status == STATUS_KILLED:
158
+ killed += 1
159
+ elif status == STATUS_SURVIVED:
160
+ survived += 1
161
+ elif status == STATUS_TIMEOUT:
162
+ timeout += 1
163
+ elif status == STATUS_NO_COVERAGE:
164
+ no_coverage += 1
165
+
166
+ honest_denom = killed + survived + no_coverage
167
+ reported_denom = killed + survived + timeout + no_coverage
168
+
169
+ honest_score = (killed / honest_denom * 100) if honest_denom else 0.0
170
+ reported_score = (
171
+ (killed + timeout) / reported_denom * 100 if reported_denom else 0.0
172
+ )
173
+
174
+ return ScoreSummary(
175
+ killed=killed,
176
+ survived=survived,
177
+ timeout=timeout,
178
+ no_coverage=no_coverage,
179
+ honest_score=honest_score,
180
+ reported_score=reported_score,
181
+ )
182
+
183
+
184
+ def _score_data(data: dict) -> ScoreSummary:
185
+ """Compute the honest and reported scores for an already-parsed report
186
+ dict (the ``{"files": {...}}`` shape shared by every supported tool)."""
187
+ return _tally(list(_iter_mutants(data)))
188
+
189
+
190
+ def score_report(report_path: Path) -> ScoreSummary:
191
+ """Compute the honest and reported scores for a Stryker-shaped mutation
192
+ report on disk.
193
+
194
+ Returns a fully zeroed :class:`ScoreSummary` when the report is absent
195
+ or empty — never raises for a missing file.
196
+ """
197
+ return _score_data(load_report(report_path))
198
+
199
+
200
+ def _score_data_for_file(data: dict, file_path: str) -> ScoreSummary:
201
+ """Compute the honest and reported scores for one file within an
202
+ already-parsed report dict.
203
+
204
+ Matches ``file_path`` the same way ``_find_file_info`` does (exact key,
205
+ then basename). Returns a fully zeroed :class:`ScoreSummary` when the
206
+ file is not matched — never raises.
207
+ """
208
+ info = _find_file_info(data, file_path)
209
+ if info is None:
210
+ return _tally([])
211
+ return _tally(info.get("mutants", []))
212
+
213
+
214
+ def score_report_for_file(report_path: Path, file_path: str) -> ScoreSummary:
215
+ """Compute the honest and reported scores for one file within a
216
+ Stryker-shaped mutation report on disk.
217
+
218
+ Matches ``file_path`` against report keys by exact match first, then by
219
+ basename (same rule as ``survivors_by_mutator``). Returns a fully zeroed
220
+ :class:`ScoreSummary` when the report is absent/empty or the file is not
221
+ matched — never raises.
222
+ """
223
+ return _score_data_for_file(load_report(report_path), file_path)
224
+
225
+
226
+ def _survivors_from_data(
227
+ data: dict, file_path: str, skip_static: bool = False
228
+ ) -> dict[str, list[dict]]:
229
+ """Return the Survived mutants for one source file, grouped by mutator
230
+ name, from an already-parsed report dict.
231
+
232
+ Matches ``file_path`` against report keys by exact match first, then by
233
+ basename, so a caller can pass either the full report key or just the
234
+ filename. Only ``Survived`` mutants are returned — killed, timed-out, and
235
+ uncovered mutants are not survivors. Returns ``{}`` when the file is not
236
+ in the report or has no survivors.
237
+
238
+ When ``skip_static`` is ``True``, a mutant is excluded only when it
239
+ carries ``static: true``; absence of the key (or ``static: false``)
240
+ never excludes it. This function stays a pure data transform — the CLI
241
+ wrapper owns any user-facing "skip is inapplicable" notice.
242
+ """
243
+ info = _find_file_info(data, file_path)
244
+ if info is None:
245
+ return {}
246
+
247
+ mutants = info.get("mutants", [])
248
+ if skip_static:
249
+ mutants = [
250
+ mutant for mutant in mutants if mutant.get(MUTANT_STATIC_KEY) is not True
251
+ ]
252
+
253
+ grouped: dict[str, list[dict]] = {}
254
+ for mutant in mutants:
255
+ if mutant.get("status") != STATUS_SURVIVED:
256
+ continue
257
+ mutator = mutant.get("mutatorName", "")
258
+ grouped.setdefault(mutator, []).append(mutant)
259
+ return grouped
260
+
261
+
262
+ def survivors_by_mutator(
263
+ report_path: Path, file_path: str, skip_static: bool = False
264
+ ) -> dict[str, list[dict]]:
265
+ """Return the Survived mutants for one source file, grouped by mutator
266
+ name, from a Stryker-shaped mutation report on disk.
267
+
268
+ See ``_survivors_from_data`` for the ``skip_static`` behavior.
269
+ """
270
+ return _survivors_from_data(
271
+ load_report(report_path), file_path, skip_static=skip_static
272
+ )
273
+
274
+
275
+ def survivors_by_mutator_from_data(
276
+ data: dict, file_path: str, skip_static: bool = False
277
+ ) -> dict[str, list[dict]]:
278
+ """Return the Survived mutants for one source file, grouped by mutator
279
+ name, from an already-parsed report dict.
280
+
281
+ Thin public wrapper around ``_survivors_from_data`` — lets a caller that
282
+ already holds a parsed report (e.g. the CLI's ``--skip-static`` path,
283
+ which loads the report once to run ``has_static_field``/
284
+ ``is_file_in_report`` diagnostics before computing survivors) reuse that
285
+ dict instead of triggering a second, redundant ``load_report`` call. See
286
+ ``_survivors_from_data`` for the ``skip_static`` behavior.
287
+ """
288
+ return _survivors_from_data(data, file_path, skip_static=skip_static)
289
+
290
+
291
+ def has_static_field(data: dict, file_path: str) -> bool:
292
+ """Return whether any mutant in the matched file's list carries a
293
+ ``"static"`` key, from an already-parsed report dict.
294
+
295
+ Matches ``file_path`` the same way ``_find_file_info`` does (exact key,
296
+ then basename). Returns ``False`` uniformly both when the matched file
297
+ has no mutant carrying a ``"static"`` key and when ``file_path`` isn't
298
+ present in the report at all (``_find_file_info`` returns ``None``) —
299
+ the two cases are not distinguished by this function's return value; use
300
+ ``is_file_in_report`` alongside it to tell them apart.
301
+
302
+ Data-based (takes an already-parsed ``data`` dict) rather than
303
+ ``Path``-based. This module's public API is not uniformly ``Path``-based
304
+ — ``parse_mutmut_junitxml``/``score_mutmut_junitxml``/
305
+ ``survivors_from_mutmut_junitxml`` already take raw input — so this is
306
+ an established shape, not a new one. It exists to serve the CLI's
307
+ pre-dispatch diagnostic, which already holds the parsed report; a
308
+ ``Path``-based signature would force the ``--skip-static`` path to
309
+ parse the report an extra, redundant time instead of reusing the dict
310
+ it already loaded once (see ``survivors_by_mutator_from_data``, which
311
+ the same CLI path reuses this dict with, for the same reason).
312
+ """
313
+ info = _find_file_info(data, file_path)
314
+ if info is None:
315
+ return False
316
+ return any(MUTANT_STATIC_KEY in mutant for mutant in info.get("mutants", []))
317
+
318
+
319
+ def is_file_in_report(data: dict, file_path: str) -> bool:
320
+ """Return whether ``file_path`` matches any report key, from an
321
+ already-parsed report dict.
322
+
323
+ Matches the same way ``_find_file_info`` does (exact key, then
324
+ basename). Lets a caller distinguish "file matched but has no static
325
+ field" from "file never matched any report key" — the two causes
326
+ ``has_static_field`` collapses into a single ``False`` return value.
327
+ """
328
+ return _find_file_info(data, file_path) is not None
329
+
330
+
331
+ def _resolve_survivor_line(mutant: dict) -> int | None:
332
+ """Resolve one mutant's source line from its ``location.start.line``
333
+ field, or ``None`` when that shape is missing or malformed.
334
+
335
+ Never raises: a missing/non-dict ``location``, a missing/non-dict
336
+ ``start``, or a ``line`` that isn't a plain ``int`` (``None``, a
337
+ non-int value, or a ``bool`` — ``bool`` is an ``int`` subclass in
338
+ Python but is never a real line number) all resolve to ``None``. This
339
+ matches the module's never-raise-on-bad-input posture (see the module
340
+ docstring, ``load_report``'s "never raises" note, and
341
+ ``parse_mutmut_junitxml``'s "malformed input returns empty rather than
342
+ raising"). Extracted from ``_survivors_by_line_from_data``'s original
343
+ inline unwrap so ``_accepted_static_survivors_from_data`` can reuse the
344
+ same hardened logic instead of duplicating it.
345
+ """
346
+ loc = mutant.get("location")
347
+ loc = loc if isinstance(loc, dict) else {}
348
+ start = loc.get("start")
349
+ start = start if isinstance(start, dict) else {}
350
+ line = start.get("line")
351
+ if not isinstance(line, int) or isinstance(line, bool):
352
+ return None
353
+ return line
354
+
355
+
356
+ def _survivors_by_line_from_data(
357
+ data: dict, file_path: str, skip_static: bool = False
358
+ ) -> dict:
359
+ """Return the Survived mutants for one source file, clustered by source
360
+ line, from an already-parsed report dict.
361
+
362
+ Matches ``file_path`` against report keys the same way
363
+ ``_survivors_from_data`` does (via ``_find_file_info`` — exact key, then
364
+ basename). Only ``Survived`` mutants participate; a mutant of any other
365
+ status (Killed, Timeout, NoCoverage, ...) sharing a line with survivors
366
+ never appears in the result.
367
+
368
+ ``skip_static`` is accepted for API symmetry with ``_survivors_from_data``
369
+ and so a caller (the CLI wrapper) can pass ``--skip-static
370
+ --survivors-by-line`` without being rejected — but it is a **no-op** on
371
+ the returned clusters (#1948, Option A): a static-flagged survivor still
372
+ counts toward its line's cluster weight and ranking exactly as it would
373
+ without the flag. A static mutant is still real evidence about that
374
+ line's mutation density, even when generation (a decision made
375
+ downstream, not here) skips writing a test for it — the two are
376
+ deliberately decoupled. A caller that needs to know whether a specific
377
+ survivor within a cluster should be skipped for generation checks that
378
+ survivor's own ``static`` field directly (already present on the raw
379
+ mutant dict); this function does not filter or annotate it further.
380
+
381
+ Return shape: ``{"clusters": [{"line": int, "survivors": [dict, ...]}, ...],
382
+ "unclustered": [dict, ...]}``.
383
+
384
+ - ``clusters`` groups survivors by ``mutant["location"]["start"]["line"]``
385
+ and is sorted by ``len(survivors)`` descending; ties are broken by
386
+ ``line`` ascending.
387
+ - ``unclustered`` holds every survivor whose line isn't a resolvable
388
+ plain ``int`` — ``None`` (e.g. mutmut's no-resolvable-line case), a
389
+ non-int value, a ``bool`` (``bool`` is an ``int`` subclass in Python
390
+ but is never a real line number), or a missing/non-dict ``location``/
391
+ ``start`` value of any shape — such a survivor never forms or joins a
392
+ cluster. This matches the module's never-raise-on-bad-input posture
393
+ (see the module docstring, ``load_report``'s "never raises" note, and
394
+ ``parse_mutmut_junitxml``'s "malformed input returns empty rather
395
+ than raising"): no ``location``/``start``/``line`` shape, however
396
+ malformed, raises ``AttributeError``/``TypeError`` here.
397
+ - Returns ``{"clusters": [], "unclustered": []}`` when the file is not in
398
+ the report or has no survivors.
399
+ """
400
+ # skip_static is deliberately never read below — see the docstring's
401
+ # "no-op on the returned clusters" note. It is accepted purely so the
402
+ # CLI can pass it through instead of rejecting the combination.
403
+ info = _find_file_info(data, file_path)
404
+ if info is None:
405
+ return {"clusters": [], "unclustered": []}
406
+
407
+ by_line: dict[int, list[dict]] = {}
408
+ unclustered: list[dict] = []
409
+ for mutant in info.get("mutants", []):
410
+ if mutant.get("status") != STATUS_SURVIVED:
411
+ continue
412
+ line = _resolve_survivor_line(mutant)
413
+ if line is None:
414
+ unclustered.append(mutant)
415
+ continue
416
+ by_line.setdefault(line, []).append(mutant)
417
+
418
+ clusters = [
419
+ {"line": line, "survivors": survivors} for line, survivors in by_line.items()
420
+ ]
421
+ clusters.sort(key=lambda cluster: (-len(cluster["survivors"]), cluster["line"]))
422
+
423
+ return {"clusters": clusters, "unclustered": unclustered}
424
+
425
+
426
+ def survivors_by_line(
427
+ report_path: Path, file_path: str, skip_static: bool = False
428
+ ) -> dict:
429
+ """Return the Survived mutants for one source file, clustered by source
430
+ line, from a Stryker-shaped mutation report on disk.
431
+
432
+ See ``_survivors_by_line_from_data`` for the return shape, ordering rule,
433
+ and the ``skip_static`` no-op-on-clustering behavior (#1948).
434
+ """
435
+ return _survivors_by_line_from_data(
436
+ load_report(report_path), file_path, skip_static=skip_static
437
+ )
438
+
439
+
440
+ def survivor_lines(report_path: Path, file_path: str) -> list[int]:
441
+ """Return the sorted, de-duplicated source lines carrying a Survived
442
+ mutant for one file — the set a #2031 confirm run needs to re-test.
443
+
444
+ Built on :func:`survivors_by_line` rather than re-deriving the clustering,
445
+ so the confirm scope and the generation targets can never disagree about
446
+ which lines had survivors. ``unclustered`` survivors (no resolvable line)
447
+ are deliberately excluded: they cannot be expressed as a line-scoped
448
+ mutate span, and a caller that cannot scope them must fall back to a full
449
+ scoped run rather than silently dropping them — see
450
+ :func:`confirm_scope_is_complete`.
451
+ """
452
+ clustered = survivors_by_line(report_path, file_path)
453
+ return sorted({c["line"] for c in clustered.get("clusters", [])})
454
+
455
+
456
+ def confirm_scope_is_complete(report_path: Path, file_path: str) -> bool:
457
+ """Whether every Survived mutant for ``file_path`` has a resolvable line.
458
+
459
+ A confirm run narrowed to line spans can only re-test mutants whose line
460
+ is known. If any survivor is ``unclustered``, narrowing would silently
461
+ stop re-testing it, and its absence from the confirm result would read as
462
+ "killed" once spliced. Callers must run the ordinary full scoped round in
463
+ that case: correctness first, saving second.
464
+ """
465
+ clustered = survivors_by_line(report_path, file_path)
466
+ return not clustered.get("unclustered")
467
+
468
+
469
+ def splice_confirm_over_prior(prior: dict, confirm: dict, file_path: str) -> dict:
470
+ """Overlay a survivor-scoped confirm result onto the prior round's report.
471
+
472
+ A confirm run mutates only the lines that had survivors last round, so its
473
+ report is a strict subset of the file's mutant set. Reporting it alone
474
+ would understate the file (every mutant outside the confirm scope would
475
+ vanish, inflating or deflating the score depending on their status). This
476
+ splices instead — the same pattern Phase 8 (``/quality-targets-converge``,
477
+ #1208) uses to report a whole-repo score from freshly-measured changed
478
+ files over a persisted baseline.
479
+
480
+ Mutants are matched by ``id`` where both sides carry one, falling back to
481
+ ``(line, mutatorName, replacement)`` — Stryker ids are stable within a run
482
+ but not guaranteed across runs. A confirm mutant replaces its prior
483
+ counterpart; a prior mutant the confirm run did not cover is carried
484
+ forward unchanged; a confirm mutant with no prior counterpart is added.
485
+
486
+ Never raises on malformed input, matching this module's standing posture:
487
+ an unusable side is treated as absent rather than fatal.
488
+ """
489
+
490
+ def _key(mutant: dict) -> tuple:
491
+ if not isinstance(mutant, dict):
492
+ return ("", "", "", "")
493
+ mid = mutant.get("id")
494
+ if isinstance(mid, (str, int)) and not isinstance(mid, bool):
495
+ return ("id", str(mid))
496
+ return (
497
+ "shape",
498
+ str(_resolve_survivor_line(mutant)),
499
+ str(mutant.get("mutatorName")),
500
+ str(mutant.get("replacement")),
501
+ )
502
+
503
+ prior_info = _find_file_info(prior if isinstance(prior, dict) else {}, file_path)
504
+ confirm_info = _find_file_info(
505
+ confirm if isinstance(confirm, dict) else {}, file_path
506
+ )
507
+ if not isinstance(prior_info, dict):
508
+ return confirm if isinstance(confirm, dict) else {}
509
+ prior_mutants = prior_info.get("mutants")
510
+ prior_mutants = prior_mutants if isinstance(prior_mutants, list) else []
511
+ confirm_mutants = (
512
+ confirm_info.get("mutants") if isinstance(confirm_info, dict) else None
513
+ )
514
+ confirm_mutants = confirm_mutants if isinstance(confirm_mutants, list) else []
515
+
516
+ merged = {_key(m): m for m in prior_mutants if isinstance(m, dict)}
517
+ for mutant in confirm_mutants:
518
+ if isinstance(mutant, dict):
519
+ merged[_key(mutant)] = mutant
520
+
521
+ spliced = json.loads(json.dumps(prior))
522
+ spliced_info = _find_file_info(spliced, file_path)
523
+ if isinstance(spliced_info, dict):
524
+ spliced_info["mutants"] = list(merged.values())
525
+ return spliced
526
+
527
+
528
+ def _accepted_static_survivors_from_data(
529
+ data: dict, file_path: str, *, skip_static_active: bool
530
+ ) -> list[dict]:
531
+ """Return each Survived mutant carrying ``static: true`` for one source
532
+ file as an accepted-survivor entry, from an already-parsed report dict.
533
+
534
+ ``skip_static_active`` is the required evidence that a static-flagged
535
+ survivor was *deliberately* deferred, not merely present. Stryker's
536
+ ``static: true`` flag alone proves nothing about operator intent — the
537
+ deliberateness lives one layer up, in whether the CLI's own
538
+ ``--skip-static`` flag was active for this run. When
539
+ ``skip_static_active`` is ``False``, this function returns ``[]``
540
+ immediately, before any other computation: a run where the skip was
541
+ never active has no accepted survivors to report, regardless of how many
542
+ mutants carry ``static: true``. When ``True``, behavior matches the
543
+ unconditional version this function used to be.
544
+
545
+ Matches ``file_path`` against report keys the same way
546
+ ``_survivors_from_data`` does (via ``_find_file_entry`` — exact key, then
547
+ basename). Only a mutant with ``status == STATUS_SURVIVED`` *and*
548
+ ``mutant.get(MUTANT_STATIC_KEY) is True`` qualifies — a
549
+ Killed/Timeout/NoCoverage/CompileError mutant carrying ``static: true``
550
+ is never returned, and a Survived mutant without the static flag is
551
+ never returned either.
552
+
553
+ Each entry has the shape ``{"id": ..., "file": str, "line": int | None,
554
+ "operator": str, "status": ACCEPTED_STATIC_ENTRY_STATUS, "reason":
555
+ ACCEPTED_STATIC_REASON}``. ``id`` is the mutant's own ``"id"`` field
556
+ (``None`` when absent) — stable mutant identity, not just a file/line
557
+ pair. ``file`` is the *matched report key* (from ``_find_file_entry``),
558
+ not an echo of the caller's ``file_path`` argument — a caller that
559
+ passed a basename gets back the full report key. ``line`` is resolved
560
+ via ``_resolve_survivor_line`` (``None`` for any missing/malformed
561
+ location/start/line shape — never raises).
562
+
563
+ Returns ``[]`` when the file is not in the report or has no matching
564
+ survivors.
565
+ """
566
+ if not skip_static_active:
567
+ return []
568
+
569
+ entry = _find_file_entry(data, file_path)
570
+ if entry is None:
571
+ return []
572
+ matched_key, info = entry
573
+
574
+ return [
575
+ {
576
+ "id": mutant.get("id"),
577
+ "file": matched_key,
578
+ "line": _resolve_survivor_line(mutant),
579
+ "operator": mutant.get("mutatorName", ""),
580
+ "status": ACCEPTED_STATIC_ENTRY_STATUS,
581
+ "reason": ACCEPTED_STATIC_REASON,
582
+ }
583
+ for mutant in info.get("mutants", [])
584
+ if mutant.get("status") == STATUS_SURVIVED
585
+ and mutant.get(MUTANT_STATIC_KEY) is True
586
+ ]
587
+
588
+
589
+ def accepted_static_survivors(
590
+ report_path: Path, file_path: str, *, skip_static_active: bool
591
+ ) -> list[dict]:
592
+ """Return each Survived mutant carrying ``static: true`` for one source
593
+ file, as accepted-survivor entries, from a Stryker-shaped mutation
594
+ report on disk.
595
+
596
+ See ``_accepted_static_survivors_from_data`` for the return shape,
597
+ matching rule, and the required ``skip_static_active`` gating.
598
+ """
599
+ return _accepted_static_survivors_from_data(
600
+ load_report(report_path), file_path, skip_static_active=skip_static_active
601
+ )
602
+
603
+
604
+ def accepted_static_survivors_from_data(
605
+ data: dict, file_path: str, *, skip_static_active: bool
606
+ ) -> list[dict]:
607
+ """Return each Survived mutant carrying ``static: true`` for one source
608
+ file, as accepted-survivor entries, from an already-parsed report dict.
609
+
610
+ Thin public wrapper around ``_accepted_static_survivors_from_data``,
611
+ mirroring the established pair-per-function convention set by
612
+ ``survivors_by_mutator``/``survivors_by_mutator_from_data``. Called by
613
+ the CLI's ``--accepted-static-survivors`` branch, which already holds a
614
+ parsed report (loaded once to run
615
+ ``_maybe_warn_skip_static_inapplicable`` first) and reuses that dict
616
+ here instead of triggering a second, redundant ``load_report`` call.
617
+ """
618
+ return _accepted_static_survivors_from_data(
619
+ data, file_path, skip_static_active=skip_static_active
620
+ )
621
+
622
+
623
+ def _files_with_status_from_data(data: dict, status: str) -> list[str]:
624
+ """Return the sorted report file keys having >= 1 mutant of ``status``,
625
+ from an already-parsed report dict.
626
+
627
+ The report keys are the file identifiers exactly as the tool emits them
628
+ (relative source paths); the caller decides how to interpret them.
629
+ """
630
+ return sorted(
631
+ key
632
+ for key, info in data.get("files", {}).items()
633
+ if any(m.get("status") == status for m in info.get("mutants", []))
634
+ )
635
+
636
+
637
+ def _files_with_status(report_path: Path, status: str) -> list[str]:
638
+ """Return the sorted report file keys having >= 1 mutant of ``status``,
639
+ from a Stryker-shaped mutation report on disk. Returns ``[]`` for an
640
+ absent/empty report — never raises."""
641
+ return _files_with_status_from_data(load_report(report_path), status)
642
+
643
+
644
+ def files_with_survivors(report_path: Path) -> list[str]:
645
+ """Return the sorted report file keys that have >= 1 Survived mutant.
646
+
647
+ This is the single source of truth for survivor-file discovery — sibling
648
+ modules call it instead of re-walking the report (AC4).
649
+ """
650
+ return _files_with_status(report_path, STATUS_SURVIVED)
651
+
652
+
653
+ def files_with_timeouts(report_path: Path) -> list[str]:
654
+ """Return the sorted report file keys that have >= 1 Timeout mutant.
655
+
656
+ This is the single source of truth for timeout-file discovery — sibling
657
+ modules call it instead of re-walking the report (AC4).
658
+ """
659
+ return _files_with_status(report_path, STATUS_TIMEOUT)
660
+
661
+
662
+ # =============================================================================
663
+ # mutmut — no native JSON report; junitxml is its only structured output.
664
+ # =============================================================================
665
+ def _testcase_to_entry(testcase) -> tuple[str, dict] | None:
666
+ """Derive the status and build the mutant dict for one ``<testcase>``
667
+ element from ``mutmut junitxml`` output.
668
+
669
+ Returns ``None`` when the testcase carries no ``file`` attribute — there
670
+ is nothing to key it into the ``files`` dict by, so it is skipped.
671
+
672
+ mutmut marks a survived mutant with a ``<failure>`` child
673
+ (``message="bad_survived"``) and a timed-out mutant with a distinct
674
+ ``<error>`` child (``message="bad_timeout"``, ``error_type="timeout"``) —
675
+ the two are never conflated, so a real Timeout is never miscounted as a
676
+ Survived. Every other ``<testcase>`` — including a suspicious-but-ignored
677
+ mutant under mutmut's default ``suspicious_policy="ignore"`` — is Killed;
678
+ mutmut's default reporting granularity cannot distinguish "cleanly
679
+ killed" from "suspicious but ignored" any further than that.
680
+ """
681
+ file_key = testcase.get("file")
682
+ if not file_key:
683
+ return None
684
+ if testcase.find("failure") is not None:
685
+ status = STATUS_SURVIVED
686
+ elif testcase.find("error") is not None:
687
+ status = STATUS_TIMEOUT
688
+ else:
689
+ status = STATUS_KILLED
690
+ line = testcase.get("line")
691
+ mutant = {
692
+ "id": testcase.get("name", "?"),
693
+ "mutatorName": "mutmut",
694
+ "status": status,
695
+ "location": {
696
+ "start": {"line": int(line) if line and line.isdigit() else None}
697
+ },
698
+ "replacement": "",
699
+ }
700
+ return file_key, mutant
701
+
702
+
703
+ def parse_mutmut_junitxml(xml_text: str) -> dict:
704
+ """Convert ``mutmut junitxml`` output into the internal ``{"files": {...}}``
705
+ shape every scoring/survivor function above already consumes.
706
+
707
+ mutmut has no NoCoverage concept (it always runs the full scoped test
708
+ command against every mutant), so that count is always zero here.
709
+
710
+ Malformed or empty input returns an empty ``{"files": {}}`` rather than
711
+ raising — callers see it as an empty report, same as a missing file.
712
+ """
713
+ files: dict[str, dict] = {}
714
+ if not xml_text.strip():
715
+ return {"files": files}
716
+ try:
717
+ root = ET.fromstring(xml_text)
718
+ except ET.ParseError:
719
+ return {"files": files}
720
+
721
+ for testcase in root.iter("testcase"):
722
+ entry = _testcase_to_entry(testcase)
723
+ if entry is None:
724
+ continue
725
+ file_key, mutant = entry
726
+ files.setdefault(file_key, {"mutants": []})["mutants"].append(mutant)
727
+
728
+ return {"files": files}
729
+
730
+
731
+ def score_mutmut_junitxml(xml_text: str) -> ScoreSummary:
732
+ """Compute the honest and reported scores directly from ``mutmut
733
+ junitxml`` output (no report file on disk needed)."""
734
+ return _score_data(parse_mutmut_junitxml(xml_text))
735
+
736
+
737
+ def survivors_from_mutmut_junitxml(
738
+ xml_text: str, file_path: str
739
+ ) -> dict[str, list[dict]]:
740
+ """Return the Survived mutants for one file from ``mutmut junitxml``
741
+ output, grouped by mutator name (always ``"mutmut"`` — the tool names no
742
+ per-mutation operator the way Stryker/pitest do)."""
743
+ return _survivors_from_data(parse_mutmut_junitxml(xml_text), file_path)