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,330 @@
1
+ #!/usr/bin/env python3
2
+ """coverage_delta_steering.py — turn per-Story coverage deltas into a live
3
+ Phase-5 steering signal (issue #1790).
4
+
5
+ Why this exists. `/coverage-delta` already records one snapshot per Story into
6
+ `coverage-history.json`, but nothing read that history *during* the phase: a
7
+ Pass-1 run kept closing Stories and reporting mutation-kill progress while
8
+ line coverage stayed flat at 74.2%, and nobody saw it until the final report —
9
+ by which point the phase's whole budget had gone into the wrong layer. This
10
+ script is the mid-phase check: after each Story's delta lands, it measures the
11
+ movement of the trailing Stories and fails (exit 3) once several consecutive
12
+ Stories have moved line coverage by less than the minimum expected per-Story
13
+ delta, so `/test-improve` can prompt for a Phase-1 targeting re-check before
14
+ spending the next Story.
15
+
16
+ Movement, per Story snapshot, is the change in `line_pct` since the previous
17
+ Story snapshot — or, for the first Story, since the run's
18
+ `baseline_line_pct`. A **negative** movement counts as flat: a Story that made
19
+ coverage worse is certainly not progress. A snapshot with no comparable
20
+ predecessor (no previous Story and no recorded baseline) has movement `None`
21
+ and **breaks** the streak rather than extending it — absence of measurement is
22
+ not evidence of flatness.
23
+
24
+ Branch movement is reported alongside but does **not** drive the verdict:
25
+ branch coverage is not natively available on every stack (Go), so keying the
26
+ gate on it would make the check silently mode-dependent.
27
+
28
+ Statuses (and exit codes):
29
+
30
+ - `flat_streak` (3) — `--consecutive` or more trailing Stories each moved less
31
+ than `--min-line-delta`. The caller prompts; this script does not decide
32
+ policy.
33
+ - `flat_streak_forming` (0) — the trailing Story moved less than the minimum
34
+ but the streak is still shorter than `--consecutive`. A distinct status
35
+ rather than `ok`, because "not yet a streak" is not "coverage is moving".
36
+ - `insufficient_history` (0) — fewer measurable Stories than `--consecutive`,
37
+ or a trailing Story whose movement could not be measured at all, so no
38
+ streak verdict is possible yet.
39
+ - `ok` (0) — the trailing Story moved coverage by at least the minimum.
40
+ - exit `2` — the history file is missing, unreadable, not JSON, not a JSON
41
+ array, or carries a non-snapshot element; also an out-of-range
42
+ `--consecutive`/`--min-line-delta`. Never reported as an all-clear.
43
+
44
+ Stdlib-only (ADR 0014/0015), Python 3.10+ floor (ADR 0031).
45
+
46
+ Usage:
47
+ python3 coverage_delta_steering.py \\
48
+ --history .dev-team-reports/test-improve/<slug>/data/coverage-history.json
49
+ python3 coverage_delta_steering.py --history <path> \\
50
+ --min-line-delta 0.25 --consecutive 3 --json
51
+ """
52
+
53
+ from __future__ import annotations
54
+
55
+ import argparse
56
+ import json
57
+ import sys
58
+ from pathlib import Path
59
+
60
+ DEFAULT_MIN_LINE_DELTA = 0.1
61
+ DEFAULT_CONSECUTIVE = 3
62
+
63
+ STATUS_FLAT = "flat_streak"
64
+ STATUS_FORMING = "flat_streak_forming"
65
+ STATUS_OK = "ok"
66
+ STATUS_INSUFFICIENT = "insufficient_history"
67
+
68
+ # Movements are rounded only to kill binary-float artifacts (72.4 - 72.0 =
69
+ # 0.40000000000000568), NOT to two decimals: rounding to 2dp before the
70
+ # threshold comparison would let a true 0.096-point move clear a 0.1 minimum,
71
+ # making a display concern decide the gate.
72
+ _MOVEMENT_PRECISION = 10
73
+
74
+
75
+ class HistoryError(ValueError):
76
+ """The coverage history could not be read or is not a snapshot array."""
77
+
78
+
79
+ def load_history(path: Path) -> list[dict]:
80
+ """Read `coverage-history.json`, raising HistoryError on anything that is
81
+ not a readable JSON array of snapshot objects."""
82
+ if not path.is_file():
83
+ raise HistoryError(f"coverage history not found: {path}")
84
+ try:
85
+ payload = json.loads(path.read_text(encoding="utf-8"))
86
+ except (OSError, json.JSONDecodeError) as exc:
87
+ raise HistoryError(f"{path}: unreadable coverage history ({exc})") from exc
88
+ if not isinstance(payload, list):
89
+ raise HistoryError(
90
+ f"{path}: coverage history must be a JSON array of snapshots, "
91
+ f"got {type(payload).__name__}"
92
+ )
93
+ # A non-dict element means the file is corrupt or half-rewritten. Dropping
94
+ # it silently would leave a corrupt history indistinguishable from a phase
95
+ # that simply hasn't closed enough Stories yet — a clean verdict from
96
+ # unusable evidence, which is the one thing this gate must never produce.
97
+ for index, entry in enumerate(payload):
98
+ if not isinstance(entry, dict):
99
+ raise HistoryError(
100
+ f"{path}: coverage history entry at index {index} is a "
101
+ f"{type(entry).__name__}, not a snapshot object"
102
+ )
103
+ return list(payload)
104
+
105
+
106
+ def story_snapshots(history: list[dict]) -> list[dict]:
107
+ """Keep only Story-attributed snapshots — the per-Story Phase-5 deltas.
108
+ Phase-less snapshots (`/quality-targets-converge`'s whole-suite call
109
+ passes no `--story`) are not Stories and must not enter the streak."""
110
+ return [entry for entry in history if entry.get("story")]
111
+
112
+
113
+ def _movement(current: object, previous: object) -> float | None:
114
+ if isinstance(current, bool) or isinstance(previous, bool):
115
+ return None
116
+ if not isinstance(current, (int, float)) or not isinstance(previous, (int, float)):
117
+ return None
118
+ return round(float(current) - float(previous), _MOVEMENT_PRECISION)
119
+
120
+
121
+ def _story_rows(snapshots: list[dict]) -> list[dict]:
122
+ rows = []
123
+ for index, snapshot in enumerate(snapshots):
124
+ if index == 0:
125
+ prev_line = snapshot.get("baseline_line_pct")
126
+ prev_branch = snapshot.get("baseline_branch_pct")
127
+ else:
128
+ prev_line = snapshots[index - 1].get("line_pct")
129
+ prev_branch = snapshots[index - 1].get("branch_pct")
130
+ rows.append(
131
+ {
132
+ "story": snapshot.get("story"),
133
+ "captured_at": snapshot.get("captured_at"),
134
+ "line_pct": snapshot.get("line_pct"),
135
+ "branch_pct": snapshot.get("branch_pct"),
136
+ "line_movement": _movement(snapshot.get("line_pct"), prev_line),
137
+ "branch_movement": _movement(snapshot.get("branch_pct"), prev_branch),
138
+ }
139
+ )
140
+ return rows
141
+
142
+
143
+ def _trailing_flat_streak(rows: list[dict], min_line_delta: float) -> list[dict]:
144
+ """The trailing run of Stories whose measured line movement is below the
145
+ minimum. Stops at the first Story that moved enough OR whose movement is
146
+ unmeasurable."""
147
+ streak: list[dict] = []
148
+ for row in reversed(rows):
149
+ movement = row["line_movement"]
150
+ if movement is None or movement >= min_line_delta:
151
+ break
152
+ streak.append(row)
153
+ streak.reverse()
154
+ return streak
155
+
156
+
157
+ def evaluate(
158
+ snapshots: list[dict],
159
+ min_line_delta: float = DEFAULT_MIN_LINE_DELTA,
160
+ consecutive: int = DEFAULT_CONSECUTIVE,
161
+ ) -> dict:
162
+ """Evaluate Story-attributed `snapshots` for a flat-coverage streak."""
163
+ rows = _story_rows(snapshots)
164
+ measured = [r for r in rows if r["line_movement"] is not None]
165
+ streak = _trailing_flat_streak(rows, min_line_delta)
166
+
167
+ if len(streak) >= consecutive:
168
+ status = STATUS_FLAT
169
+ message = (
170
+ f"{len(streak)} consecutive Stories moved line coverage by less than "
171
+ f"{min_line_delta} points ("
172
+ + ", ".join(f"{r['story']}: {r['line_movement']:+}" for r in streak)
173
+ + "). Re-check Phase-1 targeting against "
174
+ "coverage-gap-ranking.json before spending the next Story — "
175
+ "mutation-kill progress on an already-covered layer cannot move "
176
+ "these numbers."
177
+ )
178
+ elif len(measured) < consecutive:
179
+ status = STATUS_INSUFFICIENT
180
+ message = (
181
+ f"{len(measured)} Story delta(s) measured; {consecutive} are needed "
182
+ "before a flat-coverage streak can be judged."
183
+ )
184
+ elif rows[-1]["line_movement"] is None:
185
+ # The trailing Story has no comparable predecessor (a null/absent
186
+ # line_pct in the appended snapshot), so its movement could not be
187
+ # measured. Neither `ok` nor `flat_streak` is honest here.
188
+ status = STATUS_INSUFFICIENT
189
+ message = (
190
+ f"The latest Story ({rows[-1]['story']}) could not be measured — "
191
+ "its snapshot carries no comparable line_pct, so no coverage "
192
+ "movement verdict is possible for it."
193
+ )
194
+ elif streak:
195
+ # Below the streak threshold but still not moving: reporting this as
196
+ # `ok` would claim "targeting is producing coverage movement" about a
197
+ # Story that moved less than the minimum — the false all-clear this
198
+ # gate exists to prevent. Exit code stays 0; only the wording and
199
+ # status carry the warning.
200
+ status = STATUS_FORMING
201
+ message = (
202
+ f"The latest {len(streak)} Story/Stories moved line coverage by less "
203
+ f"than {min_line_delta} points ({len(streak)} of {consecutive} "
204
+ "needed for a flat-coverage streak). Watch the next Story's delta "
205
+ "before spending more of the phase on this layer."
206
+ )
207
+ else:
208
+ status = STATUS_OK
209
+ message = (
210
+ f"Latest Story moved line coverage by "
211
+ f"{rows[-1]['line_movement']:+} points — targeting is producing "
212
+ "coverage movement."
213
+ )
214
+
215
+ average = (
216
+ round(sum(r["line_movement"] for r in measured) / len(measured), 2)
217
+ if measured
218
+ else None
219
+ )
220
+ return {
221
+ "status": status,
222
+ "stories_evaluated": len(rows),
223
+ "stories_measured": len(measured),
224
+ "min_line_delta": min_line_delta,
225
+ "consecutive_threshold": consecutive,
226
+ "streak": len(streak),
227
+ "flat_stories": streak,
228
+ "running_average_line_movement": average,
229
+ "stories": rows,
230
+ "message": message,
231
+ }
232
+
233
+
234
+ def render_text(result: dict) -> str:
235
+ lines = [
236
+ f"Coverage-delta steering: {result['status']}",
237
+ result["message"],
238
+ "",
239
+ f"{'story':<24} {'line %':>8} {'Δ line':>8} {'Δ branch':>9}",
240
+ ]
241
+ for row in result["stories"]:
242
+ line_move = row["line_movement"]
243
+ branch_move = row["branch_movement"]
244
+ lines.append(
245
+ f"{row['story']!s:<24} "
246
+ f"{row['line_pct'] if row['line_pct'] is not None else '-':>8} "
247
+ f"{line_move if line_move is not None else '-':>8} "
248
+ f"{branch_move if branch_move is not None else '-':>9}"
249
+ )
250
+ lines.append("")
251
+ lines.append(
252
+ f"running average line movement: {result['running_average_line_movement']}"
253
+ )
254
+ return "\n".join(lines)
255
+
256
+
257
+ def main(argv: list[str] | None = None) -> int:
258
+ parser = argparse.ArgumentParser(
259
+ prog="coverage_delta_steering.py",
260
+ description=(
261
+ "Flag several consecutive Stories whose coverage delta is "
262
+ "near zero, so Phase-1 targeting can be re-checked mid-phase."
263
+ ),
264
+ )
265
+ parser.add_argument(
266
+ "--history",
267
+ type=Path,
268
+ required=True,
269
+ help="Path to coverage-history.json.",
270
+ )
271
+ parser.add_argument(
272
+ "--min-line-delta",
273
+ type=float,
274
+ default=DEFAULT_MIN_LINE_DELTA,
275
+ help=(
276
+ "Minimum expected per-Story line-coverage movement in percentage "
277
+ f"points (default: {DEFAULT_MIN_LINE_DELTA})."
278
+ ),
279
+ )
280
+ parser.add_argument(
281
+ "--consecutive",
282
+ type=int,
283
+ default=DEFAULT_CONSECUTIVE,
284
+ help=(
285
+ "How many consecutive below-minimum Stories constitute a flat "
286
+ f"streak (default: {DEFAULT_CONSECUTIVE})."
287
+ ),
288
+ )
289
+ parser.add_argument("--json", action="store_true", help="machine-readable output")
290
+ args = parser.parse_args(argv)
291
+
292
+ # An out-of-range threshold must fail loudly rather than produce a nonsense
293
+ # verdict: `--consecutive 0` makes `len(streak) >= consecutive` trivially
294
+ # true for any history (including an empty one), and a negative
295
+ # `--min-line-delta` makes every movement — regressions included — count
296
+ # as progress.
297
+ if args.consecutive < 1:
298
+ parser.error("--consecutive must be >= 1")
299
+ if args.min_line_delta < 0:
300
+ parser.error("--min-line-delta must be >= 0")
301
+
302
+ try:
303
+ history = load_history(args.history)
304
+ except HistoryError as exc:
305
+ if args.json:
306
+ print(json.dumps({"error": str(exc)}, indent=2))
307
+ else:
308
+ print(f"ERROR: {exc}", file=sys.stderr)
309
+ return 2
310
+
311
+ result = evaluate(
312
+ story_snapshots(history),
313
+ min_line_delta=args.min_line_delta,
314
+ consecutive=args.consecutive,
315
+ )
316
+ print(json.dumps(result, indent=2) if args.json else render_text(result))
317
+ return 3 if result["status"] == STATUS_FLAT else 0
318
+
319
+
320
+ if __name__ == "__main__": # pragma: no cover
321
+ # render_text()'s "Δ line"/"Δ branch" headers crash with
322
+ # UnicodeEncodeError on a non-UTF-8 console (e.g. Windows' default
323
+ # cp1252) before any output reaches the caller. Forcing UTF-8 here only
324
+ # affects real script invocation (this guard never runs for an in-process
325
+ # `import coverage_delta_steering; coverage_delta_steering.main(...)`
326
+ # caller, e.g. a test using capsys), matching every other script's
327
+ # console-encoding independence.
328
+ if hasattr(sys.stdout, "reconfigure"):
329
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
330
+ sys.exit(main())
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env python3
2
+ """coverage_discovery_dotnet.py — discover and classify every project in a
3
+ .NET solution for multi-project coverage discovery (issue #1759, Slice 2).
4
+
5
+ Enumerates every project via `dotnet sln <solution> list` and classifies
6
+ each `.csproj` using `coverage_config.TestClassification`, checking in
7
+ order:
8
+
9
+ 1. An inline `Microsoft.NET.Test.Sdk` `PackageReference` in the `.csproj`
10
+ itself (matched on `Include="Microsoft.NET.Test.Sdk"`, case-insensitively
11
+ per NuGet package ID convention, regardless of attribute order or
12
+ whether a `Version` attribute is present — this also covers Central
13
+ Package Management's version-less inline form) -> `TEST`.
14
+ 2. If absent inline, walk every `Directory.Build.props` from the project's
15
+ directory up to the solution root (inclusive). An **unconditioned** match
16
+ (no `Condition` attribute on the `PackageReference` itself or its
17
+ enclosing `ItemGroup`) -> `TEST`. A match that exists only inside a
18
+ `Condition` -> `AMBIGUOUS` (this script deliberately never evaluates
19
+ MSBuild conditions, matching this repo's stdlib-only,
20
+ no-runner-execution convention already established by
21
+ `coverage_readiness.py`).
22
+ 3. No match anywhere -> `NOT_TEST`.
23
+
24
+ **Known, documented limitation.** A `GlobalPackageReference` declared in
25
+ `Directory.Packages.props` (MSBuild Central Package Management's other,
26
+ zero-inline-reference mechanism, distinct from the `Directory.Build.props`
27
+ ancestor walk above) is not walked. A project relying solely on it
28
+ classifies `NOT_TEST` — see the plan's Risks section.
29
+
30
+ **Security hardening.** Every resolved project path is verified to stay
31
+ within the resolved repository root before it is read or classified — a
32
+ `.sln` entry containing `..` or an absolute path can never cause this
33
+ module to read or XML-parse a file outside the repository. Separator
34
+ normalization (see `_parse_sln_list_output`, issue #1828) happens before
35
+ that check, so a backslash-separated `..\\outside\\Evil.csproj` is refused
36
+ like its forward-slash form instead of slipping past as one opaque
37
+ filename. Every `.csproj`
38
+ and `Directory.Build.props` file is also screened for `<!DOCTYPE`/
39
+ `<!ENTITY` declarations before being parsed (entity-expansion hardening) —
40
+ neither file legitimately carries either.
41
+
42
+ The `dotnet` subprocess boundary (`shutil.which("dotnet")` and
43
+ `subprocess.run([...])`) is mocked/stubbed in this module's unit tests — no
44
+ real .NET SDK is required to run them, keeping this module inside the
45
+ required `ci-local.sh`/pre-push pytest gate.
46
+
47
+ Stdlib-only (ADR 0014/0015).
48
+ """
49
+
50
+ from __future__ import annotations
51
+
52
+ import os
53
+ import shutil
54
+ import subprocess
55
+ import sys
56
+ import xml.etree.ElementTree as ET
57
+ from pathlib import Path
58
+
59
+ _HERE = Path(__file__).resolve().parent
60
+ sys.path.insert(0, str(_HERE))
61
+
62
+ import coverage_config
63
+ from coverage_config import TestClassification
64
+
65
+ _TEST_SDK_PACKAGE = "Microsoft.NET.Test.Sdk"
66
+
67
+ # `dotnet sln ... list` per-run wall-clock timeout, matching this repo's
68
+ # convention in sibling scripts (e.g. codebase_recon.py).
69
+ _SLN_LIST_TIMEOUT_SECONDS = 120
70
+
71
+ # The disallowed-XML-marker tuple is shared: coverage_config.UNSAFE_XML_MARKERS.
72
+
73
+
74
+ def discover_dotnet_projects(repo_root):
75
+ """Discover and classify every project in `repo_root`'s `.sln`.
76
+
77
+ Returns a list of `{"path": <repo-relative project path as returned by
78
+ "dotnet sln list">, "classification": TestClassification}` entries on
79
+ success, `coverage_config.DISCOVERY_NOT_APPLICABLE` when no `.sln` is
80
+ present, or `coverage_config.discovery_error(...)` for any
81
+ tooling/parsing failure.
82
+ """
83
+ root = Path(repo_root).resolve()
84
+
85
+ sln_files = sorted(root.glob("*.sln"))
86
+ if not sln_files:
87
+ return coverage_config.DISCOVERY_NOT_APPLICABLE
88
+ if len(sln_files) > 1:
89
+ names = ", ".join(f.name for f in sln_files)
90
+ return coverage_config.discovery_error(
91
+ f"Multiple .sln files found at repository root: {names}. "
92
+ "Multi-solution repositories are not supported for discovery; "
93
+ "leave exactly one .sln at the repository root."
94
+ )
95
+ sln_path = sln_files[0]
96
+
97
+ dotnet_path = shutil.which("dotnet")
98
+ if dotnet_path is None:
99
+ return coverage_config.discovery_error(
100
+ "The `dotnet` CLI is not on PATH; install the .NET SDK to "
101
+ "enable solution discovery."
102
+ )
103
+
104
+ try:
105
+ result = subprocess.run( # noqa: PLW1510 — returncode is checked explicitly below to return a discovery_error rather than raise
106
+ [dotnet_path, "sln", str(sln_path.resolve()), "list"],
107
+ cwd=str(root),
108
+ capture_output=True,
109
+ text=True,
110
+ env={**os.environ, "DOTNET_CLI_UI_LANGUAGE": "en"},
111
+ timeout=_SLN_LIST_TIMEOUT_SECONDS,
112
+ )
113
+ except subprocess.TimeoutExpired:
114
+ return coverage_config.discovery_error(
115
+ f"`dotnet sln {sln_path.name} list` timed out after "
116
+ f"{_SLN_LIST_TIMEOUT_SECONDS}s."
117
+ )
118
+ except OSError as exc:
119
+ return coverage_config.discovery_error(
120
+ f"Failed to run `dotnet sln {sln_path.name} list`: {exc}"
121
+ )
122
+
123
+ if result.returncode != 0:
124
+ return coverage_config.discovery_error(
125
+ f"`dotnet sln {sln_path.name} list` failed: {result.stderr.strip()}"
126
+ )
127
+
128
+ project_rel_paths = _parse_sln_list_output(result.stdout)
129
+
130
+ projects = []
131
+ for rel_path in project_rel_paths:
132
+ absolute_problem = _absolute_entry_problem(rel_path)
133
+ if absolute_problem is not None:
134
+ return coverage_config.discovery_error(
135
+ f"Solution '{sln_path.name}' references project "
136
+ f"'{rel_path}', which {absolute_problem}. Only "
137
+ "solution-relative project paths are supported; refusing to "
138
+ "read or classify it."
139
+ )
140
+ csproj_path = (root / rel_path).resolve()
141
+ if not coverage_config.is_within(csproj_path, root):
142
+ return coverage_config.discovery_error(
143
+ f"Solution '{sln_path.name}' references project "
144
+ f"'{rel_path}', which resolves outside the repository "
145
+ f"root ('{root}'); refusing to read or classify it."
146
+ )
147
+ if not csproj_path.is_file():
148
+ return coverage_config.discovery_error(
149
+ f"Solution '{sln_path.name}' references project "
150
+ f"'{rel_path}' but the file does not exist on disk."
151
+ )
152
+ classification = _classify_project(csproj_path, root)
153
+ if isinstance(classification, dict):
154
+ return classification # a discovery_error propagated up
155
+ projects.append({"path": rel_path, "classification": classification})
156
+
157
+ return projects
158
+
159
+
160
+ def _absolute_entry_problem(rel_path: str) -> str | None:
161
+ """Return a reason when a (already separator-normalized) `.sln` entry is an
162
+ absolute path rather than a solution-relative one, else `None`.
163
+
164
+ Checked explicitly rather than left to `PurePosixPath.is_absolute()`,
165
+ which is platform-divergent for exactly the shapes a Windows-authored
166
+ solution produces: a drive-letter path like `C:/Projects/App.csproj` is
167
+ NOT absolute on POSIX, so it would silently become a nonsense subpath of
168
+ the repo root and fail with a misleading "does not exist on disk" instead
169
+ of naming the real problem. A UNC path normalizes to a leading `//`, which
170
+ IS absolute on POSIX — so without this check the same input produces two
171
+ different diagnostics depending on the runner's OS."""
172
+ if len(rel_path) >= 2 and rel_path[1] == ":" and rel_path[0].isalpha():
173
+ return "is an absolute Windows path (drive letter)"
174
+ if rel_path.startswith("//"):
175
+ return "is an absolute UNC network path"
176
+ if rel_path.startswith("/"):
177
+ return "is an absolute path"
178
+ return None
179
+
180
+
181
+ def _parse_sln_list_output(stdout: str) -> list:
182
+ """Parse `dotnet sln list`'s stdout into a list of repo-relative project
183
+ paths in POSIX form, skipping the `Project(s)` header and its underline.
184
+
185
+ **Separator normalization (issue #1828).** `dotnet sln list` echoes the
186
+ separators the `.sln` was authored with, and a Windows-authored solution
187
+ emits backslashes. On POSIX `pathlib` treats `src\\App\\App.csproj` as a
188
+ single opaque filename — `PurePath(...).name` returns the whole string —
189
+ so the `.csproj` is never found and every project in the solution fails
190
+ the "does not exist on disk" check even though the file is present. Since
191
+ most enterprise .NET solutions are Windows-authored and their CI is
192
+ commonly Linux, that made .NET discovery non-functional for them.
193
+
194
+ Backslashes are therefore normalized to `/` here, once, at parse time.
195
+ This is deliberately unconditional rather than POSIX-only: it keeps the
196
+ recorded path identity — the string every caller compares against
197
+ `included`/`excluded` entries — byte-identical across platforms, so a
198
+ `coverage-config.json` written on Windows still reconciles on Linux.
199
+ Normalizing before the caller's containment check also keeps the `..`
200
+ traversal guard effective on backslash-separated escapes, which
201
+ previously slipped past it as opaque filenames.
202
+
203
+ Accepted limitation: a genuine POSIX filename containing a literal
204
+ backslash would be rewritten. MSBuild solution files always use `\\` as a
205
+ separator, so such a path does not occur in `.sln` output."""
206
+ projects = []
207
+ for raw_line in stdout.splitlines():
208
+ line = raw_line.strip()
209
+ if not line:
210
+ continue
211
+ if line == "Project(s)":
212
+ continue
213
+ if set(line) == {"-"}:
214
+ continue
215
+ projects.append(line.replace("\\", "/"))
216
+ return projects
217
+
218
+
219
+ # The entity-expansion screen this module introduced now lives in
220
+ # `coverage_config` so every discovery stack that parses repository-supplied
221
+ # XML shares ONE implementation — the Java stack (#1765) originally shipped
222
+ # without a screen at all, which is exactly the drift a shared helper
223
+ # prevents. Call sites below use `coverage_config.read_and_screen_xml`
224
+ # directly: private re-export aliases would give one shared symbol two names
225
+ # depending on which file you are reading.
226
+
227
+
228
+ def _classify_project(csproj_path: Path, solution_root: Path):
229
+ """Classify a single project's `.csproj`, walking ancestor
230
+ `Directory.Build.props` files (never climbing above `solution_root`)
231
+ when no inline marker is found. Each `Directory.Build.props` found is
232
+ resolved and containment-checked against `solution_root` before being
233
+ read or parsed — mirroring the JS module's symlinked-manifest handling
234
+ (`coverage_discovery_js._classify_package`) — so a symlinked
235
+ `Directory.Build.props` pointing outside the solution root is refused
236
+ rather than read. Returns a `TestClassification` value, or
237
+ `coverage_config.discovery_error(...)` if the `.csproj` (or an ancestor
238
+ `Directory.Build.props`) is not well-formed XML, is unreadable, escapes
239
+ `solution_root`, or fails the entity-expansion safety check."""
240
+ data, unsafe = coverage_config.read_and_screen_xml(csproj_path)
241
+ if unsafe is not None:
242
+ return unsafe
243
+ try:
244
+ csproj_root = ET.fromstring(data)
245
+ except ET.ParseError as exc:
246
+ return coverage_config.discovery_error(
247
+ f"Could not parse '{csproj_path}' as XML: {exc}"
248
+ )
249
+
250
+ if _test_sdk_reference_kind(csproj_root) is not None:
251
+ return TestClassification.TEST
252
+
253
+ found_conditioned = False
254
+ current_dir = csproj_path.parent
255
+ while coverage_config.is_within(current_dir, solution_root):
256
+ props_path = current_dir / "Directory.Build.props"
257
+ if props_path.is_file():
258
+ try:
259
+ resolved_props_path = props_path.resolve()
260
+ except OSError as exc:
261
+ return coverage_config.discovery_error(
262
+ f"Could not resolve '{props_path}': {exc}"
263
+ )
264
+ if not coverage_config.is_within(resolved_props_path, solution_root):
265
+ return coverage_config.discovery_error(
266
+ f"'{props_path}' resolves to '{resolved_props_path}', "
267
+ f"which is outside the solution root "
268
+ f"('{solution_root}'); refusing to read or parse it."
269
+ )
270
+ props_data, unsafe = coverage_config.read_and_screen_xml(resolved_props_path)
271
+ if unsafe is not None:
272
+ return unsafe
273
+ try:
274
+ props_root = ET.fromstring(props_data)
275
+ except ET.ParseError as exc:
276
+ return coverage_config.discovery_error(
277
+ f"Could not parse '{props_path}' as XML: {exc}"
278
+ )
279
+ kind = _test_sdk_reference_kind(props_root)
280
+ if kind == "unconditioned":
281
+ return TestClassification.TEST
282
+ if kind == "conditioned":
283
+ found_conditioned = True
284
+ if current_dir == solution_root:
285
+ break
286
+ current_dir = current_dir.parent
287
+
288
+ if found_conditioned:
289
+ return TestClassification.AMBIGUOUS
290
+ return TestClassification.NOT_TEST
291
+
292
+
293
+ def _test_sdk_reference_kind(root) -> str | None:
294
+ """Return `"unconditioned"`, `"conditioned"`, or `None` for whether
295
+ `root` contains a `Microsoft.NET.Test.Sdk` `PackageReference` (matched
296
+ case-insensitively, per NuGet package ID convention), and whether it (or
297
+ its enclosing `ItemGroup`) carries an MSBuild `Condition` attribute.
298
+ This script deliberately never evaluates the condition's truth value —
299
+ presence alone drives the distinction."""
300
+ found_conditioned = False
301
+ for item_group in root.iter():
302
+ if coverage_config.local_name(item_group.tag) != "ItemGroup":
303
+ continue
304
+ group_conditioned = "Condition" in item_group.attrib
305
+ for child in item_group:
306
+ if coverage_config.local_name(child.tag) != "PackageReference":
307
+ continue
308
+ include = (child.get("Include") or "").strip().casefold()
309
+ if include != _TEST_SDK_PACKAGE.casefold():
310
+ continue
311
+ if group_conditioned or "Condition" in child.attrib:
312
+ found_conditioned = True
313
+ else:
314
+ return "unconditioned"
315
+ return "conditioned" if found_conditioned else None