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,631 @@
1
+ #!/usr/bin/env python3
2
+ """Language-neutral internal-double detector with waiver support (#2127).
3
+
4
+ Implements the unconditional rule in `knowledge/internal-collaborator-doubling.md`
5
+ (#2124): a collaborator declared in the project's own first-party source
6
+ stays real; a double is admissible only when the collaborator matches one
7
+ of three named blockers (B1 out-of-process handle, B2 ambient state, B3
8
+ prohibitive real cost), marked with an inline `double-waiver: B<n> — <reason>`
9
+ comment at the double site.
10
+
11
+ **No test-type gate.** The rule is unconditional, so this detector reads no
12
+ test-type declaration (solitary/sociable/unit/component/anything else) —
13
+ that is what closes the undeclared-file and relabel bypasses the epic's
14
+ architecture notes describe. A file is scanned the same way regardless of
15
+ what it calls itself.
16
+
17
+ **Language-neutral by construction.** One first-party declaration index plus
18
+ per-stack *data* tables (`_EXTRACTION_TABLES`, `_AMBIENT_API_MARKERS`) feed a
19
+ single shared pipeline (`extract_by_table`, `analyze`). There is no
20
+ per-language branching logic — a new stack is a new data-table row, not a
21
+ new code path. If this module exceeds ~500 lines, split the extraction
22
+ tables into a submodule rather than growing this file indefinitely (mirrors
23
+ `repo_invariants.py`'s own "start narrow, expand opportunistically"
24
+ convention) — not yet warranted at this size.
25
+
26
+ **What this detector verifies, and what it does not:**
27
+
28
+ - Resolution is **name-based, not fully-qualified-path-based**. Building a
29
+ real cross-file import graph per language is out of scope for a
30
+ stdlib-only script with no per-language parser. A name match against the
31
+ first-party declaration index is treated as resolved. Two same-named
32
+ types in different first-party modules are indistinguishable; a name
33
+ collision with an unrelated third-party type of the same name could
34
+ false-positive. Precision-first per the epic means under-report, not zero
35
+ false positives.
36
+ - **Waiver syntax, not waiver truth**, for B1 and B3. A waiver naming B1 or
37
+ B3 is `informational` once its code is syntactically valid — this
38
+ detector cannot decide whether the claimed out-of-process handle or
39
+ prohibitive cost is real (the epic's own architecture notes: B1 is "not
40
+ decidable", B3 "not statically detectable at all"). `test-smell-review`
41
+ owns that judgment.
42
+ - **B2 gets one mechanical evidence check**, not full verification: the
43
+ epic states B2 (unlike B1/B3) is "detectable against a per-stack table of
44
+ ambient APIs." A B2 waiver is downgraded from `informational` to `high`
45
+ when the doubled collaborator's own declaring file shows no reference to
46
+ any per-stack ambient-API marker (clock, RNG/GUID, env, hostname, cwd,
47
+ locale) — a presence-of-reference check, not a behavioral proof.
48
+ `test-smell-review` still owns the final call even when this check
49
+ passes.
50
+ - Gate-wiring into a blocking hook or CI check is a separate issue (#2128).
51
+ This script is a standalone CLI, proven via direct invocation and its own
52
+ test suite.
53
+
54
+ Stdlib-only. See docs/python-hook-contract.md.
55
+ """
56
+
57
+ from __future__ import annotations
58
+
59
+ import argparse
60
+ import json
61
+ import re
62
+ import subprocess
63
+ import sys
64
+ from collections.abc import Callable
65
+ from pathlib import Path
66
+
67
+ # --- First-party type resolution --------------------------------------------
68
+
69
+ #: Directory names excluded from vendored/generated/build-output code for
70
+ #: *both* first-party indexing and double scanning — nothing useful lives
71
+ #: there for either purpose.
72
+ _EXCLUDED_DIR_NAMES = frozenset(
73
+ {
74
+ "__pycache__",
75
+ "node_modules",
76
+ "dist",
77
+ "build",
78
+ "vendor",
79
+ "vendored",
80
+ ".git",
81
+ # #2128: a local, uncommitted virtualenv (CLAUDE.md's own
82
+ # documented PEP-668 dev-setup fallback) must never change the
83
+ # required whole-repo CI check's result depending on whether a
84
+ # given contributor's checkout happens to have one.
85
+ ".venv",
86
+ "venv",
87
+ ".tox",
88
+ "site-packages",
89
+ }
90
+ )
91
+
92
+ #: Test-directory names excluded from first-party resolution ONLY — a type
93
+ #: declared under a test tree isn't a first-party collaborator (#2124's
94
+ #: rule is about the project's own *non-test* source). These directories
95
+ #: are NOT excluded from double-scanning: that's exactly where doubles live.
96
+ _TEST_DIR_NAMES = frozenset({"test", "tests"})
97
+
98
+ #: (stack, compiled_regex, target_group) rows for declaring a first-party
99
+ #: type. One row per declaration *form*, not one combined regex per stack
100
+ #: with post-hoc form selection (design-review fix) — a new form is a new
101
+ #: row, never a new branch.
102
+ _TYPE_DECLARATION_TABLE: tuple[tuple[str, re.Pattern, int], ...] = (
103
+ ("python", re.compile(r"^\s*class\s+([A-Za-z_][A-Za-z0-9_]*)"), 1),
104
+ ("csharp", re.compile(r"^\s*(?:public|internal|private)?\s*(?:class|interface|record)\s+([A-Za-z_][A-Za-z0-9_]*)"), 1),
105
+ ("java", re.compile(r"^\s*(?:public|private|protected)?\s*(?:class|interface|record)\s+([A-Za-z_][A-Za-z0-9_]*)"), 1),
106
+ ("js_ts_class", re.compile(r"^\s*(?:export\s+)?class\s+([A-Za-z_][A-Za-z0-9_]*)"), 1),
107
+ ("js_ts_interface", re.compile(r"^\s*(?:export\s+)?interface\s+([A-Za-z_][A-Za-z0-9_]*)"), 1),
108
+ ("js_ts_type_alias", re.compile(r"^\s*(?:export\s+)?type\s+([A-Za-z_][A-Za-z0-9_]*)\s*="), 1),
109
+ )
110
+
111
+
112
+ def _is_vendored_or_build(path: Path) -> bool:
113
+ return any(part.lower() in _EXCLUDED_DIR_NAMES for part in path.parts)
114
+
115
+
116
+ def _is_test_tree(path: Path) -> bool:
117
+ return any(part.lower() in _TEST_DIR_NAMES for part in path.parts)
118
+
119
+
120
+ def _read_text(path: Path) -> str:
121
+ try:
122
+ return path.read_text(encoding="utf-8")
123
+ except (OSError, UnicodeDecodeError):
124
+ return ""
125
+
126
+
127
+ #: Source file extensions this detector reads at all — first-party
128
+ #: resolution and every extraction vector below are scoped to these.
129
+ _SOURCE_EXTENSIONS = frozenset({".py", ".cs", ".java", ".ts", ".tsx", ".js", ".jsx"})
130
+
131
+ #: Which file extensions each stack's patterns are allowed to run against.
132
+ #: Without this, a stack's regex can spuriously match inside another
133
+ #: stack's file (e.g. a C# "class X : Base" pattern's trailing `\s*` can
134
+ #: cross a newline and match text in an unrelated Python file) — extension
135
+ #: gating is what actually keeps the per-stack data tables from bleeding
136
+ #: into each other, not just their labels.
137
+ _STACK_EXTENSIONS: dict[str, frozenset[str]] = {
138
+ "python": frozenset({".py"}),
139
+ "csharp": frozenset({".cs"}),
140
+ "java": frozenset({".java"}),
141
+ "js_ts": frozenset({".ts", ".tsx", ".js", ".jsx"}),
142
+ "js_ts_class": frozenset({".ts", ".tsx", ".js", ".jsx"}),
143
+ "js_ts_interface": frozenset({".ts", ".tsx", ".js", ".jsx"}),
144
+ "js_ts_type_alias": frozenset({".ts", ".tsx", ".js", ".jsx"}),
145
+ }
146
+
147
+
148
+ def resolve_first_party_types(root: Path) -> dict[str, Path]:
149
+ """Index every type name declared in non-test, non-vendored,
150
+ non-generated source under `root`. Returns `{type_name: declaring_path}`
151
+ — first declaration wins on a name collision (a recall bound stated in
152
+ the module docstring, not silently hidden).
153
+ """
154
+ index: dict[str, Path] = {}
155
+ for path in sorted(root.rglob("*")):
156
+ if not path.is_file() or path.suffix not in _SOURCE_EXTENSIONS:
157
+ continue
158
+ rel = path.relative_to(root)
159
+ if _is_vendored_or_build(rel) or _is_test_tree(rel):
160
+ continue
161
+ text = _read_text(path)
162
+ if not text:
163
+ continue
164
+ for stack, pattern, group in _TYPE_DECLARATION_TABLE:
165
+ if path.suffix not in _STACK_EXTENSIONS.get(stack, _SOURCE_EXTENSIONS):
166
+ continue
167
+ for line in text.splitlines():
168
+ m = pattern.match(line)
169
+ if m:
170
+ name = m.group(group)
171
+ index.setdefault(name, path)
172
+ return index
173
+
174
+
175
+ # --- Extraction tables (doubling constructs, hand-rolled, DI-override) -----
176
+
177
+ def _identity(name: str) -> str:
178
+ return name
179
+
180
+
181
+ def _last_path_segment(module_path: str) -> str:
182
+ """`./services/SmtpGateway` -> `SmtpGateway` — `vi.mock`/`jest.mock`
183
+ capture a module *path*, not a type name, so the target must be
184
+ normalized to a plausible type-name form before it can be looked up in
185
+ the name-keyed first-party index (correctness-review fix: these rows
186
+ previously could never resolve to anything but `advisory`)."""
187
+ stem = module_path.rsplit("/", 1)[-1]
188
+ for ext in (".ts", ".tsx", ".js", ".jsx"):
189
+ stem = stem.removesuffix(ext)
190
+ return stem
191
+
192
+
193
+ def _last_dotted_segment(dotted: str) -> str:
194
+ """`billing.SmtpGateway` -> `SmtpGateway` — `vi.spyOn` can capture a
195
+ dotted expression; only the final segment is a plausible type name."""
196
+ return dotted.rsplit(".", 1)[-1]
197
+
198
+
199
+ #: One shared schema for every extraction vector: a tuple of
200
+ #: `(stack, compiled_regex, target_group, normalize)` rows, where
201
+ #: `normalize` maps the raw captured string to a plausible type name before
202
+ #: first-party lookup (identity for rows that already capture a bare type
203
+ #: name). `extract_by_table` is the single iteration helper every vector
204
+ #: uses — no vector gets its own bespoke loop (design-review fix).
205
+ _EXTRACTION_TABLES: dict[str, tuple[tuple[str, re.Pattern, int, Callable[[str], str]], ...]] = {
206
+ "mock_construct": (
207
+ ("csharp", re.compile(r"\bMock<([A-Za-z_][A-Za-z0-9_]*)>"), 1, _identity),
208
+ ("csharp", re.compile(r"\bMock\.Of<([A-Za-z_][A-Za-z0-9_]*)>"), 1, _identity),
209
+ ("csharp", re.compile(r"\bSubstitute\.For<([A-Za-z_][A-Za-z0-9_]*)>"), 1, _identity),
210
+ ("csharp", re.compile(r"\bA\.Fake<([A-Za-z_][A-Za-z0-9_]*)>"), 1, _identity),
211
+ ("java", re.compile(r"\bmock\(([A-Za-z_][A-Za-z0-9_]*)\.class\)"), 1, _identity),
212
+ ("java", re.compile(r"@Mock(?:Bean)?\s*\n?\s*(?:private|public)?\s*([A-Za-z_][A-Za-z0-9_]*)\s+\w+;"), 1, _identity),
213
+ ("js_ts", re.compile(r"\bvi\.mock\(\s*['\"]([^'\"]+)['\"]"), 1, _last_path_segment),
214
+ ("js_ts", re.compile(r"\bjest\.mock\(\s*['\"]([^'\"]+)['\"]"), 1, _last_path_segment),
215
+ ("js_ts", re.compile(r"\bvi\.spyOn\(\s*([A-Za-z_][A-Za-z0-9_.]*)"), 1, _last_dotted_segment),
216
+ ("python", re.compile(r"\bpatch\(\s*['\"][\w.]*\.([A-Za-z_][A-Za-z0-9_]*)['\"]"), 1, _identity),
217
+ ("python", re.compile(r"\bMagicMock\(\s*spec\s*=\s*([A-Za-z_][A-Za-z0-9_]*)"), 1, _identity),
218
+ ),
219
+ "hand_rolled": (
220
+ # re.MULTILINE is required (correctness-review fix): extract_by_table
221
+ # runs finditer over the WHOLE file, not line-by-line, so a bare `^`
222
+ # only anchors to offset 0 without it — silently matching nothing
223
+ # in any file with content before the fake class.
224
+ ("python", re.compile(r"^\s*class\s+[A-Za-z_][A-Za-z0-9_]*\(([A-Za-z_][A-Za-z0-9_]*)\)", re.MULTILINE), 1, _identity),
225
+ ("java", re.compile(r"^\s*class\s+[A-Za-z_][A-Za-z0-9_]*\s+implements\s+([A-Za-z_][A-Za-z0-9_]*)", re.MULTILINE), 1, _identity),
226
+ ("js_ts", re.compile(r"^\s*class\s+[A-Za-z_][A-Za-z0-9_]*\s+implements\s+([A-Za-z_][A-Za-z0-9_]*)", re.MULTILINE), 1, _identity),
227
+ ("csharp", re.compile(r"^\s*class\s+[A-Za-z_][A-Za-z0-9_]*\s*:\s*([A-Za-z_][A-Za-z0-9_]*)", re.MULTILINE), 1, _identity),
228
+ ),
229
+ "di_override": (
230
+ ("csharp", re.compile(r"\bRemoveAll<([A-Za-z_][A-Za-z0-9_]*)>"), 1, _identity),
231
+ ("csharp", re.compile(r"\bAddSingleton<([A-Za-z_][A-Za-z0-9_]*)>"), 1, _identity),
232
+ ("java", re.compile(r"@Bean\s*\n?\s*(?:public|private)?\s*([A-Za-z_][A-Za-z0-9_]*)\s+\w+\("), 1, _identity),
233
+ ("python", re.compile(r"\bdependency_overrides\[([A-Za-z_][A-Za-z0-9_]*)\]"), 1, _identity),
234
+ ("js_ts", re.compile(r"\boverrideProvider\(([A-Za-z_][A-Za-z0-9_]*)\)"), 1, _identity),
235
+ ),
236
+ }
237
+
238
+
239
+ def extract_by_table(
240
+ text: str,
241
+ table: tuple[tuple[str, re.Pattern, int, Callable[[str], str]], ...],
242
+ file_ext: str | None = None,
243
+ ) -> list[tuple[int, str, str]]:
244
+ """Run every `(stack, regex, group, normalize)` row in `table` against
245
+ `text`, returning `(line_no, stack, normalized_target_name)` for each
246
+ match. Matches over the full text (not line-by-line) so a row's own
247
+ regex controls whether it spans one line or several (e.g. a `@Bean`
248
+ annotation on the line before the method it decorates) — the line
249
+ number is derived from the match's start offset. When `file_ext` is
250
+ given, a row only runs if its stack's extension set includes it — this
251
+ is what stops one stack's pattern from matching inside another stack's
252
+ file (see `_STACK_EXTENSIONS`'s docstring). Shared by every extraction
253
+ vector — mock constructs, hand-rolled subclasses, and DI-override
254
+ bindings alike."""
255
+ out: list[tuple[int, str, str]] = []
256
+ for stack, pattern, group, normalize in table:
257
+ if file_ext is not None and file_ext not in _STACK_EXTENSIONS.get(
258
+ stack, _SOURCE_EXTENSIONS
259
+ ):
260
+ continue
261
+ for m in pattern.finditer(text):
262
+ lineno = text.count("\n", 0, m.start()) + 1
263
+ out.append((lineno, stack, normalize(m.group(group))))
264
+ return out
265
+
266
+
267
+ def find_doubles(text: str, file_ext: str | None = None) -> list[tuple[int, str, str]]:
268
+ return extract_by_table(text, _EXTRACTION_TABLES["mock_construct"], file_ext)
269
+
270
+
271
+ # --- Waiver parsing ----------------------------------------------------------
272
+
273
+ _WAIVER_RE = re.compile(r"double-waiver:\s*([A-Za-z0-9]+)\s*[—-]\s*(.+)")
274
+ _VALID_BLOCKERS = frozenset({"B1", "B2", "B3"})
275
+
276
+
277
+ def find_waiver(lines: list[str], double_line_no: int) -> tuple[str, str] | None:
278
+ """Look at the double's own line and the line immediately before it for
279
+ a `double-waiver: B<n> — <reason>` comment. Returns `(code, reason)` or
280
+ `None`."""
281
+ for idx in (double_line_no - 1, double_line_no - 2):
282
+ if 0 <= idx < len(lines):
283
+ m = _WAIVER_RE.search(lines[idx])
284
+ if m:
285
+ return m.group(1), m.group(2).strip()
286
+ return None
287
+
288
+
289
+ def valid_blocker(code: str) -> bool:
290
+ return code in _VALID_BLOCKERS
291
+
292
+
293
+ # --- Ambient-API evidence (B2 only) ------------------------------------------
294
+
295
+ #: Per-stack markers whose presence in a collaborator's own declaring file
296
+ #: is evidence it plausibly reads ambient state (clock, RNG/GUID, env,
297
+ #: hostname, cwd, locale). Presence-of-reference only — not a behavioral
298
+ #: proof. B1 and B3 have no equivalent table: the epic states both are
299
+ #: undecidable/not-statically-detectable, so this asymmetry is deliberate.
300
+ _AMBIENT_API_MARKERS: dict[str, tuple[str, ...]] = {
301
+ "python": ("time.", "datetime.", "random.", "uuid.", "os.environ", "socket.gethostname", "os.getcwd", "locale."),
302
+ "csharp": ("DateTime.Now", "Guid.NewGuid", "Environment."),
303
+ "java": ("System.currentTimeMillis", "UUID.randomUUID", "System.getenv"),
304
+ "js_ts": ("Date.now", "Math.random", "process.env"),
305
+ }
306
+
307
+
308
+ def references_ambient_api(collaborator_source: str, stack: str) -> bool:
309
+ markers = _AMBIENT_API_MARKERS.get(stack, ())
310
+ return any(marker in collaborator_source for marker in markers)
311
+
312
+
313
+ # --- Verdict computation ------------------------------------------------------
314
+
315
+ #: Findings dict shape: {line, stack, target, vector, verdict, waiver, message}
316
+ Finding = dict
317
+
318
+
319
+ def _verdict_for(
320
+ waiver: tuple[str, str] | None,
321
+ stack: str,
322
+ collaborator_path: Path | None,
323
+ ) -> str:
324
+ if waiver is None:
325
+ return "high"
326
+ code, _reason = waiver
327
+ if not valid_blocker(code):
328
+ return "high"
329
+ if code == "B2":
330
+ collaborator_source = _read_text(collaborator_path) if collaborator_path else ""
331
+ if not references_ambient_api(collaborator_source, stack):
332
+ return "high"
333
+ return "informational"
334
+
335
+
336
+ def _remediation_hint(target: str) -> str:
337
+ return (
338
+ f"add a `double-waiver: B<n> — <reason>` comment at the double site "
339
+ f"naming why {target} can't stay real, or use the real collaborator. "
340
+ "See knowledge/internal-collaborator-doubling.md."
341
+ )
342
+
343
+
344
+ def analyze(root: Path) -> list[Finding]:
345
+ """Scan every test-tree source file under `root` for doubles of
346
+ first-party collaborators across all three vectors (mock constructs,
347
+ hand-rolled subclasses, DI-overrides), and compute each one's verdict.
348
+
349
+ Scoped to test-tree files (correctness-review fix): the epic's own
350
+ language for both hand-rolled doubles ("a test-tree class implementing
351
+ a first-party abstraction") and DI-overrides ("a type declared under
352
+ the test tree") makes this explicit — a "double" is a test-context
353
+ concept, so ordinary production subclassing of a first-party class is
354
+ not in scope for any of the three vectors, not just these two.
355
+ """
356
+ first_party = resolve_first_party_types(root)
357
+ findings: list[Finding] = []
358
+
359
+ for path in sorted(root.rglob("*")):
360
+ if not path.is_file() or path.suffix not in _SOURCE_EXTENSIONS:
361
+ continue
362
+ rel_for_scope = path.relative_to(root)
363
+ if _is_vendored_or_build(rel_for_scope) or not _is_test_tree(rel_for_scope):
364
+ continue
365
+ text = _read_text(path)
366
+ if not text:
367
+ continue
368
+ lines = text.splitlines()
369
+
370
+ for vector in ("mock_construct", "hand_rolled", "di_override"):
371
+ matches = extract_by_table(text, _EXTRACTION_TABLES[vector], path.suffix)
372
+ for lineno, stack, target in matches:
373
+ collaborator_path = first_party.get(target)
374
+ if collaborator_path is None:
375
+ verdict = "advisory"
376
+ else:
377
+ waiver = find_waiver(lines, lineno)
378
+ verdict = _verdict_for(waiver, stack, collaborator_path)
379
+
380
+ rel = str(path)
381
+ findings.append(
382
+ {
383
+ "file": rel,
384
+ "line": lineno,
385
+ "stack": stack,
386
+ "vector": vector,
387
+ "target": target,
388
+ "verdict": verdict,
389
+ "message": (
390
+ f"{rel}:{lineno}: {verdict} — {vector} of {target!r}"
391
+ + (
392
+ f". {_remediation_hint(target)}"
393
+ if verdict == "high"
394
+ else ""
395
+ )
396
+ ),
397
+ }
398
+ )
399
+ return findings
400
+
401
+
402
+ # --- Changed-file scoping (#2128) --------------------------------------------
403
+ #
404
+ # analyze()'s own signature, full-tree walk, and full-tree first-party
405
+ # resolution are DELIBERATELY untouched by everything below — a first-party
406
+ # class declared in a file the current diff never touched must still resolve.
407
+ # Scoping only ever filters the FINDINGS analyze() already produced; it never
408
+ # narrows what analyze() itself scans.
409
+
410
+
411
+ def changed_files_since(ref: str, root: Path) -> set[Path] | None:
412
+ """Absolute paths of files changed vs `ref` (`git diff --name-only
413
+ ref...HEAD`, run with cwd=root). Returns `None` on ANY git failure — not
414
+ a repo, a bad ref, no merge-base (e.g. a shallow clone) — never an empty
415
+ set standing in for "the git call itself failed": an empty set means
416
+ "the diff is genuinely empty", which callers must be able to tell apart
417
+ from "scoping could not be determined at all".
418
+
419
+ `git diff --name-only` always emits paths relative to the repo's TOP
420
+ LEVEL, never relative to the cwd it was run from (there is no implicit
421
+ `--relative`) — so the output must be joined onto the actual top-level
422
+ directory (resolved via `git rev-parse --show-toplevel`), NOT onto
423
+ `root` itself. `root` is legitimately a subdirectory of the repo (e.g.
424
+ a monorepo sub-project's cwd); joining git's repo-root-relative output
425
+ onto that subdirectory would silently double the sub-path and match
426
+ nothing real, which is exactly the false-negative correctness-review
427
+ caught before this landed (#2128). `-c core.quotePath=false` +
428
+ `-z` (NUL-separated, no shell-quoting ambiguity) makes this correct
429
+ for filenames with spaces, quotes, or non-ASCII bytes too."""
430
+ try:
431
+ toplevel_completed = subprocess.run(
432
+ ["git", "rev-parse", "--show-toplevel"],
433
+ cwd=str(root),
434
+ capture_output=True,
435
+ text=True,
436
+ timeout=30,
437
+ check=False,
438
+ )
439
+ except (OSError, subprocess.TimeoutExpired):
440
+ return None
441
+ if toplevel_completed.returncode != 0:
442
+ return None
443
+ toplevel = Path(toplevel_completed.stdout.strip()).resolve()
444
+
445
+ try:
446
+ completed = subprocess.run(
447
+ [
448
+ "git",
449
+ "-c",
450
+ "core.quotePath=false",
451
+ "diff",
452
+ "--name-only",
453
+ "-z",
454
+ f"{ref}...HEAD",
455
+ ],
456
+ cwd=str(root),
457
+ capture_output=True,
458
+ text=True,
459
+ timeout=30,
460
+ check=False,
461
+ )
462
+ except (OSError, subprocess.TimeoutExpired):
463
+ return None
464
+ if completed.returncode != 0:
465
+ return None
466
+ return {(toplevel / part).resolve() for part in completed.stdout.split("\0") if part}
467
+
468
+
469
+ def _findings_matching_paths(
470
+ findings: list[Finding], target_paths: set[Path], root: Path
471
+ ) -> list[Finding]:
472
+ """Keep only findings whose file resolves into `target_paths`. Both
473
+ sides are resolved to absolute paths before comparison — this function
474
+ resolves `target_paths` itself (never trusts a caller to have done so
475
+ already), so it's correct whether `root` (and therefore every
476
+ finding's `file` string, which inherits root's absolute/relative-ness
477
+ directly from `analyze()`'s own `root.rglob()` walk) was passed in as
478
+ absolute or relative, and regardless of whether `target_paths` arrived
479
+ pre-resolved. Shared by `filter_since` (a git-diff-derived path set)
480
+ and `filter_by_files` (a caller-supplied path list) so the
481
+ path-matching logic exists exactly once."""
482
+ root_abs = root.resolve()
483
+ resolved_targets = {p.resolve() for p in target_paths}
484
+ return [
485
+ f
486
+ for f in findings
487
+ if (root_abs / f["file"]).resolve() in resolved_targets
488
+ or Path(f["file"]).resolve() in resolved_targets
489
+ ]
490
+
491
+
492
+ def filter_since(findings: list[Finding], changed: set[Path], root: Path) -> list[Finding]:
493
+ """Keep only findings whose file is in `changed`. See
494
+ `_findings_matching_paths` for the matching contract."""
495
+ return _findings_matching_paths(findings, changed, root)
496
+
497
+
498
+ def filter_by_files(findings: list[Finding], files: list[str], root: Path) -> list[Finding]:
499
+ """Keep only findings whose file is in `files` (repo-relative or
500
+ absolute paths, e.g. from `/code-review`'s own changed-file list —
501
+ the same list `repo_invariants.py --files` already receives). Both a
502
+ root-anchored and a bare interpretation of each entry are offered to
503
+ `_findings_matching_paths` (which resolves everything itself) so a
504
+ relative path resolves correctly whether `root` is the repo root
505
+ (the documented `/code-review` invocation) or a subdirectory. See
506
+ `_findings_matching_paths` for the full matching contract."""
507
+ target_paths = {root / f for f in files} | {Path(f) for f in files}
508
+ return _findings_matching_paths(findings, target_paths, root)
509
+
510
+
511
+ def analyze_files(root: Path, files: list[str]) -> list[Finding]:
512
+ """Runs the FULL `analyze(root)` (unchanged full walk, full first-party
513
+ index — same invariant `analyze_since` follows: scoping only ever
514
+ filters findings, never narrows what `analyze()` itself scans), then
515
+ filters to `files`. Unlike `analyze_since`, there is no git-dependent
516
+ failure mode here — `files` is a plain caller-supplied list, so this
517
+ always succeeds."""
518
+ return filter_by_files(analyze(root), files, root)
519
+
520
+
521
+ def analyze_since(root: Path, ref: str) -> tuple[list[Finding], str | None]:
522
+ """Runs the FULL `analyze(root)` (unchanged full walk, full first-party
523
+ index), then either filters it to `ref`'s changed-file set or, on a git
524
+ failure, returns the full unfiltered findings alongside a non-None
525
+ advisory reason.
526
+
527
+ The two possible return shapes are NOT interchangeable for a caller:
528
+ `(filtered_findings, None)` means "scoping succeeded, findings are
529
+ accurate to the diff"; `(unfiltered_findings, "<reason>")` means "git
530
+ could not answer the scoping question at all" — a caller that treats a
531
+ non-None reason as just noise and evaluates the findings anyway is
532
+ choosing to fall back to whole-repo behavior, which is the CLI's
533
+ deliberate, disclosed policy (see `main()`'s `--changed-since`) and NOT
534
+ the hook's (see `hooks/internal_double_gate.py`, which always fails open
535
+ to no-block on a non-None reason instead).
536
+
537
+ Disclosed scope note: `analyze(root)` reads the WORKING TREE, while
538
+ `changed_files_since()`'s file list comes from the COMMITTED diff
539
+ (`ref...HEAD`). A finding is kept whenever its FILE is in the committed
540
+ diff, even if the doubling line itself only exists in an uncommitted
541
+ edit to that same file — narrow in practice (`/pr` requires a clean
542
+ tree before opening), and errs toward blocking rather than passing."""
543
+ findings = analyze(root)
544
+ changed = changed_files_since(ref, root)
545
+ if changed is None:
546
+ return findings, f"could not resolve changed files since {ref!r}"
547
+ return filter_since(findings, changed, root), None
548
+
549
+
550
+ # --- CLI ----------------------------------------------------------------------
551
+
552
+ _RECALL_BOUNDS_STATEMENT = (
553
+ "Recall bounds: type resolution is name-based, not fully-qualified — a "
554
+ "same-named third-party type can false-positive, and a re-exported/"
555
+ "aliased first-party type can be missed. Waiver checks verify syntax "
556
+ "for B1/B3 (undecidable per the epic) and one mechanical ambient-API "
557
+ "evidence check for B2 only — never full waiver truth. "
558
+ "test-smell-review owns the final judgment call in every case."
559
+ )
560
+
561
+
562
+ def _summarize(findings: list[Finding]) -> str:
563
+ counts: dict[str, int] = {}
564
+ for f in findings:
565
+ counts[f["verdict"]] = counts.get(f["verdict"], 0) + 1
566
+ if not counts:
567
+ return "0 findings"
568
+ return ", ".join(f"{n} {v}" for v, n in sorted(counts.items()))
569
+
570
+
571
+ def main(argv: list[str] | None = None) -> int:
572
+ parser = argparse.ArgumentParser(description=__doc__)
573
+ parser.add_argument("path", help="Directory to scan")
574
+ parser.add_argument(
575
+ "--strict", action="store_true", help="Exit nonzero if any 'high' finding exists"
576
+ )
577
+ parser.add_argument("--json", action="store_true", help="Emit findings as JSON")
578
+ scope_group = parser.add_mutually_exclusive_group()
579
+ scope_group.add_argument(
580
+ "--changed-since",
581
+ metavar="REF",
582
+ help=(
583
+ "Scope findings to files changed vs REF (git diff --name-only "
584
+ "REF...HEAD). On a git failure (bad ref, no merge-base, not a "
585
+ "repo), falls back to the full unfiltered scan with an ADVISORY "
586
+ "line — this fallback is CLI-only; the blocking hook never "
587
+ "adopts it (see hooks/internal_double_gate.py)."
588
+ ),
589
+ )
590
+ scope_group.add_argument(
591
+ "--files",
592
+ nargs="*",
593
+ default=None,
594
+ help=(
595
+ "Scope findings to this explicit file list (repo-relative or "
596
+ "absolute) — the same list a caller like /code-review's step 2b "
597
+ "already computed for repo_invariants.py --files. Unlike "
598
+ "--changed-since, this has no git-dependent failure mode."
599
+ ),
600
+ )
601
+ args = parser.parse_args(argv)
602
+
603
+ root = Path(args.path)
604
+ if not root.exists():
605
+ print(f"error: path not found: {root}", file=sys.stderr)
606
+ return 2
607
+
608
+ if args.changed_since:
609
+ findings, advisory_reason = analyze_since(root, args.changed_since)
610
+ if advisory_reason:
611
+ print(f"ADVISORY: internal_double_detector: {advisory_reason} — falling back to an unscoped full-tree scan")
612
+ elif args.files is not None:
613
+ findings = analyze_files(root, args.files)
614
+ else:
615
+ findings = analyze(root)
616
+
617
+ if args.json:
618
+ print(json.dumps({"findings": findings}, sort_keys=True))
619
+ else:
620
+ print(_summarize(findings))
621
+ for f in findings:
622
+ print(f["message"])
623
+ print(_RECALL_BOUNDS_STATEMENT)
624
+
625
+ if args.strict and any(f["verdict"] == "high" for f in findings):
626
+ return 1
627
+ return 0
628
+
629
+
630
+ if __name__ == "__main__":
631
+ raise SystemExit(main(sys.argv[1:]))