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,557 @@
1
+ """atomic_state — shared atomic-write + cross-OS advisory-lock helpers for hooks.
2
+
3
+ Several advisory hooks persist tiny per-session/per-project state with a
4
+ read-modify-write cycle against a shared on-disk file. Two concurrent
5
+ invocations (two Bash tool calls firing close together, or two
6
+ sessions/worktrees touching the same repo) can race and lose an update
7
+ (#1501). This module centralizes the two primitives that close that race:
8
+
9
+ - `atomic_write(path, text)` — write via a same-directory tempfile + rename
10
+ so a concurrent reader never observes a torn/partial file.
11
+ - `locked_state(path)` — a cross-OS exclusive advisory lock held for the
12
+ full read-modify-write cycle so counters increment exactly once per
13
+ invocation instead of clobbering each other.
14
+
15
+ The lock idiom (`fcntl.flock` on POSIX, `msvcrt.locking` on Windows,
16
+ fail-open when neither is available) started as a copy of
17
+ `hooks/code_intelligence_turn_mark.py::_sentinel_lock` — #1888 (below) bounded
18
+ the acquire wait here but deliberately left that sibling's blocking wait
19
+ alone (a PostToolUse nudge sentinel, not a safety-critical guard verdict), so
20
+ the two no longer agree on acquire behavior; only the release-path idiom
21
+ still matches. Everything here is fail-open: any I/O or locking failure runs
22
+ the critical section unlocked / skips the persist rather than raising —
23
+ these are advisory nudges, never gates — unless a caller opts into
24
+ `locked_state(path, strict=True)`, in which case the same failure raises
25
+ `RuntimeError` instead of running unlocked.
26
+
27
+ Lock *acquisition* is bounded, not a blocking wait (#1888): a hung sibling
28
+ process holding the lock must never leave a caller blocked indefinitely,
29
+ since some callers (e.g. `boundary_events.py`, per #1874) sit in front of a
30
+ safety-critical guard hook's verdict. `locked_state` polls a non-blocking
31
+ acquisition for a bounded total budget and falls through to running the
32
+ critical section UNLOCKED once that budget elapses — the same fail-open
33
+ posture as every other failure mode here. This bounds contention on an
34
+ already-held lock specifically; it does NOT bound a stall inside `open()` on
35
+ the lock file itself, or inside a caller's own I/O in the critical section —
36
+ a wedged mount can still block there. Every one of these fail-open points is
37
+ skippable per call: pass `strict=True` to `locked_state` and the same failure
38
+ raises `RuntimeError` instead of running unlocked — for a caller (e.g. a
39
+ read-modify-write whose lost update is a correctness bug, not an advisory
40
+ nudge) that must never silently proceed without the lock.
41
+
42
+ A second use case (#1889) reuses `locked_state` for pure append-serialization
43
+ rather than read-modify-write: several `.claude/metrics/*.jsonl` telemetry
44
+ streams have concurrent writers of their own — sliced-mode code-review's 2-3
45
+ parallel slices, `/build`'s wave-parallel fan-out
46
+ (`DEV_TEAM_MAX_PARALLEL_BUILDS`), or two sessions/worktrees appending to the
47
+ same shared log. Without the lock, two processes' `write()` calls can
48
+ interleave mid-line and corrupt the stream for every downstream consumer.
49
+ `append_line_locked(path, line)` below is the shared helper for that shape.
50
+
51
+ The lock file is opened with `O_NOFOLLOW` (where the platform defines it)
52
+ and created at mode `0600` (security review, #1889): a pre-planted symlink
53
+ at `<path>.lock` would otherwise be followed by a plain `open()`, and a
54
+ world/group-writable lock file would let another local user on a shared
55
+ host acquire and hold it (an `flock`/`msvcrt.locking` caller needs no write
56
+ access to the *data* file to do this — only to the lock file). `O_NOFOLLOW`
57
+ makes `open()` fail closed (OSError) on a symlinked lock path, which this
58
+ function's existing fail-open contract already turns into an unlocked
59
+ critical section — the same outcome as any other lock-open failure.
60
+
61
+ Stdlib-only.
62
+ """
63
+
64
+ from __future__ import annotations
65
+
66
+ import contextlib
67
+ import errno
68
+ import os
69
+ import sys
70
+ import tempfile
71
+ import time
72
+ from collections.abc import Iterator
73
+ from pathlib import Path
74
+
75
+ try:
76
+ import fcntl # type: ignore[import-not-found]
77
+ except ImportError: # pragma: no cover — Windows has no fcntl
78
+ fcntl = None # type: ignore[assignment]
79
+
80
+ try:
81
+ import msvcrt # type: ignore[import-not-found]
82
+ except ImportError: # pragma: no cover — POSIX has no msvcrt
83
+ msvcrt = None # type: ignore[assignment]
84
+
85
+
86
+ # Total time `locked_state` will poll for the lock before giving up and
87
+ # running the critical section unlocked (#1888). Sized comfortably above any
88
+ # legitimate contention chain this module's consumers exercise — the shared
89
+ # concurrency regression test's worst case is 8 workers x 60ms holds (~480ms
90
+ # of legitimate queueing; see test_lock_acquire_budget_exceeds_worst_case_
91
+ # contention, which pins this relationship mechanically) — so normal
92
+ # contention still fully serializes and only a genuinely stalled holder ever
93
+ # trips the give-up path.
94
+ _DEFAULT_LOCK_ACQUIRE_BUDGET_SECONDS = 2.0
95
+ _LOCK_POLL_INTERVAL_SECONDS = 0.01
96
+
97
+ # Hard ceiling on the test-only override below (security review, #1888): the
98
+ # override has a floor to stop a misconfigured `0` from removing the retry,
99
+ # but nothing stopped the OTHER direction — an arbitrarily large value would
100
+ # reinstate exactly the unbounded wait this module exists to remove. No
101
+ # configuration can raise the give-up bound past this.
102
+ _MAX_LOCK_ACQUIRE_BUDGET_SECONDS = 10.0
103
+
104
+ # `_try_acquire`'s non-blocking attempt raises OSError both for ordinary lock
105
+ # contention (retry-worthy) and for conditions retrying can never fix — e.g.
106
+ # ENOLCK/EOPNOTSUPP when flock isn't supported on the underlying filesystem
107
+ # (some NFS/FUSE/CIFS mounts). Without this distinction, `_acquire_bounded`
108
+ # burned the full budget on every single call on such a filesystem, which is
109
+ # strictly worse than the pre-#1888 behavior (an immediate, un-retried
110
+ # failure) it must not regress. Only these errnos — genuine "someone else
111
+ # holds it" signals on POSIX (`flock`) and Windows (`msvcrt.locking`) — are
112
+ # worth retrying; anything else gives up immediately.
113
+ #
114
+ # EDEADLOCK/EDEADLK (#1904 item 9, defensive — no recorded Windows probe):
115
+ # Windows CRT `_locking` (the backend behind `msvcrt.locking`) documents
116
+ # surfacing a deadlock-shaped errno on contention in some circumstances. Both
117
+ # are POSIX-optional attributes (absent on some platforms), hence the same
118
+ # `getattr(..., None)` + filter idiom already used for EWOULDBLOCK above.
119
+ _LOCK_CONTENTION_ERRNOS = frozenset(
120
+ e
121
+ for e in (
122
+ errno.EACCES,
123
+ errno.EAGAIN,
124
+ getattr(errno, "EWOULDBLOCK", None),
125
+ getattr(errno, "EDEADLOCK", None),
126
+ getattr(errno, "EDEADLK", None),
127
+ )
128
+ if e is not None
129
+ )
130
+
131
+
132
+ class LockAcquisitionError(RuntimeError):
133
+ """Raised by `locked_state(path, strict=True)` when the lock could not be
134
+ acquired (directory/lock-file creation failure, fdopen failure, or
135
+ acquire-budget exhaustion) — never for a caller-raised exception from
136
+ inside the critical section (#1892 still applies unchanged). Subclasses
137
+ `RuntimeError` so pre-existing `pytest.raises(RuntimeError, ...)`
138
+ assertions against strict-mode callers keep passing; new callers should
139
+ narrow to this type specifically so a broad `except (OSError,
140
+ RuntimeError)` doesn't also swallow an unrelated `RuntimeError` raised by
141
+ the critical section body itself (#1920 correctness review)."""
142
+
143
+
144
+ def _reraise_unless_fail_open(exc: OSError, fail_open: bool) -> None:
145
+ """Re-raise `exc` unless `fail_open` — the shared decision behind each
146
+ of `atomic_write`'s OSError sites (#1920 structure review: this
147
+ collapses the same duplication `_refuse_or_warn` below was extracted to
148
+ eliminate for `locked_state`'s four fail-open sites). Takes the
149
+ exception explicitly, rather than relying on a bare `raise` re-raising
150
+ the caller's currently-handled exception, so this helper is an ordinary
151
+ function call rather than something that only works from inside a
152
+ lexical `except` block.
153
+ """
154
+ if not fail_open:
155
+ raise exc
156
+
157
+
158
+ def atomic_write(path: Path, text: str, *, fail_open: bool = True) -> None:
159
+ """Write `text` to `path` atomically via tempfile-in-same-dir + rename.
160
+
161
+ A concurrent reader sees either the old file or the fully-written new one,
162
+ never a partial write.
163
+
164
+ `fail_open` (default `True`, matching every pre-existing caller's call
165
+ shape): any OSError — creating the parent directory, obtaining the temp
166
+ file, opening/writing it, or the final `os.replace` — short-circuits to a
167
+ silent no-op; persistence is best-effort for these advisory hooks. Pass
168
+ `fail_open=False` for a caller that must never silently proceed without
169
+ having actually written the file (mirrors `append_line_locked`'s own
170
+ `fail_open` parameter, #1920): the same OSError re-raises instead, after
171
+ the same best-effort tmp-file cleanup runs either way.
172
+ """
173
+ try:
174
+ path.parent.mkdir(parents=True, exist_ok=True)
175
+ except OSError as exc:
176
+ _reraise_unless_fail_open(exc, fail_open)
177
+ return
178
+ try:
179
+ fd, tmp_name = tempfile.mkstemp(
180
+ prefix=f".{path.name}-", suffix=".tmp", dir=str(path.parent)
181
+ )
182
+ except OSError as exc:
183
+ _reraise_unless_fail_open(exc, fail_open)
184
+ return
185
+ try:
186
+ try:
187
+ handle = os.fdopen(fd, "w", encoding="utf-8")
188
+ except OSError:
189
+ # fdopen() failed without ever wrapping fd in a file object, so
190
+ # nothing else will close it — leaking the fd otherwise (#1920
191
+ # correctness review; mirrors locked_state's own fdopen-failure
192
+ # fd-leak fix).
193
+ with contextlib.suppress(OSError):
194
+ os.close(fd)
195
+ raise
196
+ with handle:
197
+ handle.write(text)
198
+ os.replace(tmp_name, path)
199
+ except OSError as exc:
200
+ with contextlib.suppress(OSError):
201
+ os.unlink(tmp_name)
202
+ _reraise_unless_fail_open(exc, fail_open)
203
+
204
+
205
+ def _lock_acquire_budget_seconds() -> float:
206
+ """Test-only override for the give-up budget, mirroring
207
+ `race_window_delay`'s env-var injection pattern below. Unset in
208
+ production; an unset, negative, or non-integer value (the var is
209
+ documented in whole milliseconds) falls back to the default (fail-open).
210
+ Clamped to `[_LOCK_POLL_INTERVAL_SECONDS,
211
+ _MAX_LOCK_ACQUIRE_BUDGET_SECONDS]`: the floor guarantees a nonzero
212
+ budget (not a guaranteed retry count — a first attempt that itself takes
213
+ longer than the floor can still see zero retries), and the ceiling
214
+ guarantees no override can reinstate an effectively-unbounded wait.
215
+ """
216
+ raw = os.environ.get("DEV_TEAM_LOCK_ACQUIRE_BUDGET_TEST_MS")
217
+ if not raw:
218
+ return _DEFAULT_LOCK_ACQUIRE_BUDGET_SECONDS
219
+ try:
220
+ value_seconds = int(raw) / 1000
221
+ except ValueError:
222
+ return _DEFAULT_LOCK_ACQUIRE_BUDGET_SECONDS
223
+ if value_seconds < 0:
224
+ return _DEFAULT_LOCK_ACQUIRE_BUDGET_SECONDS
225
+ return min(
226
+ _MAX_LOCK_ACQUIRE_BUDGET_SECONDS,
227
+ max(_LOCK_POLL_INTERVAL_SECONDS, value_seconds),
228
+ )
229
+
230
+
231
+ def _try_acquire(handle) -> bool:
232
+ """One non-blocking acquisition attempt on `handle`.
233
+
234
+ Returns True on success, False if nothing raised but no lock backend is
235
+ available (fcntl/msvcrt both missing) — this is the one case where
236
+ `_acquire_bounded` below returns without ever polling or sleeping, since
237
+ retrying an operation that can't succeed either way buys nothing. Raises
238
+ OSError when the lock is currently held elsewhere — the caller decides
239
+ whether to retry or give up.
240
+ """
241
+ if fcntl is not None:
242
+ fcntl.flock(handle.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
243
+ return True
244
+ if msvcrt is not None:
245
+ handle.seek(0)
246
+ msvcrt.locking(handle.fileno(), msvcrt.LK_NBLCK, 1)
247
+ return True
248
+ return False
249
+
250
+
251
+ def _release(handle) -> None:
252
+ """Release a lock previously acquired via `_try_acquire`, mirroring its
253
+ guard-clause style. Can raise OSError; the caller (`locked_state`) wraps
254
+ this call in its own try/except, matching the fail-open contract."""
255
+ if fcntl is not None:
256
+ fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
257
+ return
258
+ if msvcrt is not None:
259
+ handle.seek(0)
260
+ msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
261
+
262
+
263
+ def _acquire_bounded(handle) -> bool:
264
+ """Poll `_try_acquire` until it succeeds, the acquire budget elapses, or
265
+ (immediately, no polling) `_try_acquire` signals a condition retrying
266
+ can't fix — no lock backend at all, or an OSError that isn't ordinary
267
+ contention (see `_LOCK_CONTENTION_ERRNOS`).
268
+
269
+ Returns True once the lock is held, False if the budget ran out with the
270
+ lock still held elsewhere, no backend exists, or a non-contention
271
+ OSError occurred (the caller runs unlocked in every False case).
272
+ """
273
+ deadline = time.monotonic() + _lock_acquire_budget_seconds()
274
+ while True:
275
+ try:
276
+ return _try_acquire(handle)
277
+ except OSError as exc:
278
+ if exc.errno not in _LOCK_CONTENTION_ERRNOS:
279
+ return False
280
+ if time.monotonic() >= deadline:
281
+ return False
282
+ time.sleep(_LOCK_POLL_INTERVAL_SECONDS)
283
+
284
+
285
+ def _refuse_or_warn(
286
+ reason: str, path: Path, strict: bool, exc: BaseException | None = None
287
+ ) -> None:
288
+ """Shared decision behind each of `locked_state`'s four fail-open
289
+ points (#1892's scoping invariant is unaffected — every call site below
290
+ still runs its own `yield`/`return` immediately after this returns; this
291
+ helper never yields itself). `strict=True` raises
292
+ `LockAcquisitionError`, chaining `exc` as its cause when one is given
293
+ (the budget-exhaustion site has no underlying exception to chain, so it
294
+ omits `exc`). `strict=False` prints the fail-open diagnostic to stderr
295
+ and returns, leaving the caller to fall through to running the critical
296
+ section unlocked.
297
+ """
298
+ if strict:
299
+ message = (
300
+ f"{reason}; refusing to run the critical section unlocked for {path}"
301
+ )
302
+ if exc is not None:
303
+ raise LockAcquisitionError(message) from exc
304
+ raise LockAcquisitionError(message)
305
+ print(f"{reason}; proceeding WITHOUT lock for {path}", file=sys.stderr)
306
+
307
+
308
+ @contextlib.contextmanager
309
+ def locked_state(path: Path, *, strict: bool = False) -> Iterator[None]:
310
+ """Hold an exclusive advisory lock for a full read-modify-write cycle.
311
+
312
+ Prevents the lost-update race where two concurrent invocations both read
313
+ the same stale value, both increment it, and one write clobbers the
314
+ other. `fcntl.flock` on POSIX, `msvcrt.locking` on Windows (this repo's
315
+ hooks target both — ADR 0014/0015). The lock file lives beside `path` as
316
+ `<path>.lock`.
317
+
318
+ Acquisition is bounded, not a blocking wait (#1888): a stalled holder
319
+ trips a fail-open give-up after `_lock_acquire_budget_seconds()` of
320
+ polling rather than blocking a caller indefinitely.
321
+
322
+ Fail-open (default, `strict=False`): a missing `fcntl` *and* `msvcrt`, an
323
+ OSError creating the lock file's parent directory or opening the lock
324
+ file, or a lock that can't be acquired within budget, all fall through to
325
+ running the critical section UNLOCKED rather than raising or blocking
326
+ forever. An unlocked lost-update is no worse than the pre-#1501 behavior;
327
+ a crash — or an undelivered guard-hook verdict — would be worse, and
328
+ these hooks are advisory.
329
+
330
+ `strict=True`: every one of the same four fail-open points above raises
331
+ `LockAcquisitionError` (a `RuntimeError` subclass, carrying the same
332
+ diagnostic detail the fail-open path would otherwise print to stderr,
333
+ reworded to say the critical section is being refused rather than run
334
+ unlocked) instead of printing and running the critical section unlocked
335
+ — the critical section body never runs when the lock could not be
336
+ acquired. For a caller where a lost update is a correctness bug rather
337
+ than an advisory best-effort nudge. Every existing caller passes the
338
+ default `False` and is completely unaffected; this is an additive,
339
+ backward-compatible parameter.
340
+
341
+ A caller-raised OSError from inside the critical section propagates
342
+ normally (through the `finally`/release, out of this generator's
343
+ `yield`) rather than being swallowed (#1892): the `except OSError` that
344
+ guards the `os.fdopen()` call is scoped to that call alone, not to the
345
+ `with` block around the `yield` — a prior version wrapped both, which
346
+ meant a caller's OSError landed in that same `except`, fell through to a
347
+ second bare `yield`, and made `contextlib` raise
348
+ `RuntimeError("generator didn't stop after throw()")` instead of the
349
+ OSError the caller actually raised.
350
+ """
351
+ lock_path = path.parent / (path.name + ".lock")
352
+ try:
353
+ lock_path.parent.mkdir(parents=True, exist_ok=True)
354
+ except OSError as exc:
355
+ reason = (
356
+ f"atomic_state: lock acquisition failed (could not create lock "
357
+ f"directory {lock_path.parent}: {exc})"
358
+ )
359
+ _refuse_or_warn(reason, path, strict, exc)
360
+ yield
361
+ return
362
+
363
+ # os.O_NOFOLLOW is undefined on Windows; getattr(..., 0) makes the flag
364
+ # a no-op there rather than an AttributeError (which isn't an OSError
365
+ # and would escape the except below).
366
+ try:
367
+ lock_fd = os.open(
368
+ str(lock_path), os.O_RDWR | os.O_CREAT | getattr(os, "O_NOFOLLOW", 0), 0o600
369
+ )
370
+ # The mode argument to os.open() only applies when O_CREAT actually
371
+ # creates the file — POSIX ignores it for a path that already
372
+ # exists. Every checkout that already has a pre-#1889 lock file on
373
+ # disk (created at the old, wider default permissions) would
374
+ # otherwise keep it forever. fchmod on the already-open fd (no
375
+ # TOCTOU) enforces 0600 either way (security review, #1889).
376
+ with contextlib.suppress(OSError, AttributeError):
377
+ os.fchmod(lock_fd, 0o600)
378
+ except OSError as exc:
379
+ reason = (
380
+ f"atomic_state: lock acquisition failed (could not open lock "
381
+ f"file {lock_path}: {exc})"
382
+ )
383
+ _refuse_or_warn(reason, path, strict, exc)
384
+ yield
385
+ return
386
+
387
+ # This except is scoped to the fdopen() call ONLY (#1892) — it must NOT
388
+ # also wrap the `with` block below, or a caller-raised OSError from
389
+ # inside the critical section (at the `yield`) would be caught here too
390
+ # and mishandled (see the CONTRACT note above).
391
+ try:
392
+ handle_cm = os.fdopen(lock_fd, "r+")
393
+ except OSError as exc:
394
+ # fdopen() failed without ever wrapping lock_fd in a file object, so
395
+ # nothing else will close it — leaking the fd otherwise (#1920
396
+ # correctness review).
397
+ with contextlib.suppress(OSError):
398
+ os.close(lock_fd)
399
+ reason = (
400
+ f"atomic_state: lock acquisition failed (fdopen on "
401
+ f"{lock_path} failed: {exc})"
402
+ )
403
+ _refuse_or_warn(reason, path, strict, exc)
404
+ yield
405
+ return
406
+
407
+ with handle_cm as handle:
408
+ locked = False
409
+ try:
410
+ try:
411
+ locked = _acquire_bounded(handle)
412
+ except OSError:
413
+ locked = False
414
+ if not locked:
415
+ reason = (
416
+ f"atomic_state: lock acquisition failed (budget exhausted "
417
+ f"or non-contention error acquiring {lock_path})"
418
+ )
419
+ _refuse_or_warn(reason, path, strict)
420
+ yield
421
+ finally:
422
+ if locked:
423
+ try:
424
+ _release(handle)
425
+ except OSError:
426
+ pass
427
+
428
+
429
+ def _write_maybe_delayed(handle, line: str, delay_env_var: str | None) -> None:
430
+ """Write `line` in one call, unless `delay_env_var` names a set env var.
431
+
432
+ Test-only split path: see `append_line_locked`'s docstring for why a
433
+ single small `write()` needs splitting, not just delaying, to simulate a
434
+ genuinely non-atomic write. A value of ``"0"`` (or anything else that
435
+ parses to <= 0, or fails to parse at all) takes the plain single-write
436
+ path — matching this repo's `DEV_TEAM_*` numeric-knob convention where a
437
+ non-positive value means "disabled" — rather than bare `os.environ.get()`
438
+ truthiness, which would treat the *string* ``"0"`` as "enabled with a
439
+ zero-length delay" and needlessly split the write.
440
+ """
441
+ delay_ms = 0
442
+ if delay_env_var:
443
+ raw = os.environ.get(delay_env_var)
444
+ if raw:
445
+ try:
446
+ delay_ms = int(raw)
447
+ except ValueError:
448
+ delay_ms = 0
449
+ if delay_ms <= 0:
450
+ handle.write(line)
451
+ return
452
+ mid = len(line) // 2
453
+ handle.write(line[:mid])
454
+ handle.flush()
455
+ race_window_delay(delay_env_var)
456
+ handle.write(line[mid:])
457
+
458
+
459
+ def append_line_locked(
460
+ path: Path,
461
+ line: str,
462
+ *,
463
+ delay_env_var: str | None = None,
464
+ fail_open: bool = True,
465
+ ) -> None:
466
+ """Append `line` (already newline-terminated) to `path` under `locked_state`.
467
+
468
+ Serializes concurrent appends to a shared JSONL stream: while the lock is
469
+ held (see `locked_state`'s own fail-open fallback above for when it might
470
+ not be), two processes writing at once cannot interleave their bytes
471
+ into one corrupted line (see module docstring, #1889) — the append-only
472
+ counterpart to `locked_state`'s original read-modify-write use case. The
473
+ lock is what provides this, regardless of how many underlying
474
+ `os.write()` syscalls a single logical `.write()` call happens to
475
+ decompose into (e.g. a line long enough to cross the `BufferedWriter`'s
476
+ internal buffer boundary) — a caller does not need to reason about
477
+ syscall-level atomicity separately.
478
+
479
+ `fail_open` (default `True`): OSError from `open()`/`write()` is caught
480
+ *inside* the lock and discarded, matching this repo's fail-open posture
481
+ for advisory telemetry. Pass `fail_open=False` for a caller that needs
482
+ the write failure to propagate instead (e.g. a round-log CLI that must
483
+ crash rather than silently record nothing) — the OSError is still
484
+ caught *inside* the lock (avoiding a caller-raised exception crossing
485
+ the generator's `yield`), just re-raised after the lock releases rather
486
+ than discarded.
487
+
488
+ `delay_env_var`, when set and naming a set env var, splits the write into
489
+ two separate `write()` calls with `race_window_delay` between them,
490
+ instead of the usual single call. This is a test-only hook, needed
491
+ because a single small `write()` to an `O_APPEND`-opened file is
492
+ typically atomic with respect to other processes on mainstream local
493
+ filesystems (not independently verified here — no recorded runtime
494
+ probe), so simply sleeping *before* an unsplit write may not force real
495
+ interleaving in a test. The split instead simulates a genuinely
496
+ non-atomic write directly (long enough to cross a write-buffer
497
+ boundary, or a filesystem, e.g. some NFS configurations, that doesn't
498
+ honor `O_APPEND`'s atomicity guarantee) — the actual failure mode the
499
+ lock protects against regardless of whether any given production write
500
+ happens to be single-syscall-atomic on its own. Unset (the default) in
501
+ production: exactly one `.write()` call at this Python API level,
502
+ unchanged from the code this helper replaced.
503
+ """
504
+ write_error: OSError | None = None
505
+ with locked_state(path):
506
+ try:
507
+ # O_NOFOLLOW (security review, #1889): the data file sits beside
508
+ # the now-hardened `<path>.lock` in the same predictable,
509
+ # repo-relative directory — a pre-planted symlink here would
510
+ # otherwise redirect every append the same way the pre-fix lock
511
+ # file did. Mode is the historical default (not 0600): unlike
512
+ # the lock file, this file's readers are other tooling that
513
+ # expects normal permissions.
514
+ fd = os.open(
515
+ str(path),
516
+ os.O_WRONLY | os.O_APPEND | os.O_CREAT | getattr(os, "O_NOFOLLOW", 0),
517
+ 0o644,
518
+ )
519
+ with os.fdopen(fd, "a", encoding="utf-8") as handle:
520
+ _write_maybe_delayed(handle, line, delay_env_var)
521
+ except OSError as exc:
522
+ if not fail_open:
523
+ write_error = exc
524
+ if write_error is not None:
525
+ raise write_error
526
+
527
+
528
+ _MAX_RACE_WINDOW_DELAY_MS = 500
529
+
530
+
531
+ def race_window_delay(env_var: str) -> None:
532
+ """Test-only injection point: sleep inside a locked critical section when
533
+ `env_var` names a millisecond delay, widening the race window so a
534
+ concurrency test can deterministically force two invocations to overlap
535
+ rather than relying on timing luck. Unset in production; a bad/missing
536
+ value is a no-op (fail-open). NOT the same implementation as
537
+ `code_intelligence_turn_mark._test_delay` despite the similar shape —
538
+ that sibling still has an uncapped sleep inside its own blocking lock.
539
+
540
+ Capped at `_MAX_RACE_WINDOW_DELAY_MS` (security review, #1889): several
541
+ callers now invoke this from inside a held exclusive lock on a shared,
542
+ frequently-hit stream (`append_line_locked`'s `_write_maybe_delayed`), so
543
+ an unbounded value here would let anything that can influence the process
544
+ environment hold that lock for an attacker-chosen duration. Every real
545
+ test uses 60ms; the cap costs them nothing. This bounds one call, not a
546
+ whole critical section — a caller invoking it more than once, or from
547
+ inside a loop, multiplies the total; today's callers all call it exactly
548
+ once per critical section, but that is a property of the call sites,
549
+ not an invariant this function enforces.
550
+ """
551
+ raw = os.environ.get(env_var)
552
+ if not raw:
553
+ return
554
+ try:
555
+ time.sleep(min(int(raw), _MAX_RACE_WINDOW_DELAY_MS) / 1000)
556
+ except (ValueError, OSError):
557
+ pass
@@ -0,0 +1,103 @@
1
+ """Detect whether harness autocompact is configured (#2177, slice 2).
2
+
3
+ Single decider used by both `scripts/set_autocompact_env.py` (the `/setup`
4
+ writer) and `hooks/autocompact_setup_nudge.py`. Import direction is
5
+ scripts -> hooks/lib; this module never imports from `scripts/` or
6
+ `.claude/lib`.
7
+
8
+ The harness env var `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` is an integer 1-100
9
+ (percent of the auto-compact window). Values above the harness default are
10
+ ignored, so the variable can only lower the threshold. `HARNESS_DEFAULT_PCT`
11
+ (~83) is REPORTED, not confirmed against primary docs.
12
+
13
+ Sources consulted, highest precedence first: the process environment,
14
+ `.claude/settings.local.json`, `.claude/settings.json`, then the user
15
+ settings (`$CLAUDE_CONFIG_DIR/settings.json`, else `~/.claude/settings.json`).
16
+ The first source that defines the key decides. An unreadable or malformed
17
+ settings file is skipped, never raised.
18
+
19
+ Stdlib only.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import os
26
+ import re
27
+ from collections.abc import Mapping
28
+ from dataclasses import dataclass
29
+ from enum import Enum
30
+ from pathlib import Path
31
+
32
+ KEY = "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE"
33
+ DEFAULT_PCT = 40
34
+ HARNESS_DEFAULT_PCT = 83 # reported, not verified against primary docs
35
+
36
+ # Plain ASCII integer 1-100: no sign, no whitespace, no decimal point, no leading zero.
37
+ _PCT_RE = re.compile(r"(?:100|[1-9][0-9]?)")
38
+
39
+
40
+ class Status(Enum):
41
+ CONFIGURED = "configured"
42
+ INVALID = "invalid"
43
+ ABSENT = "absent"
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class Detection:
48
+ status: Status
49
+ raw: object = None
50
+ source: str | None = None
51
+
52
+
53
+ def validate_pct(raw: object) -> int | None:
54
+ """Return the integer percent for a valid string 1-100, else None."""
55
+ if not isinstance(raw, str) or not _PCT_RE.fullmatch(raw):
56
+ return None
57
+ return int(raw)
58
+
59
+
60
+ def user_settings_path(env: Mapping[str, str]) -> Path:
61
+ cfg = env.get("CLAUDE_CONFIG_DIR")
62
+ if cfg:
63
+ return Path(cfg) / "settings.json"
64
+ home = env.get("HOME") or os.path.expanduser("~")
65
+ return Path(home) / ".claude" / "settings.json"
66
+
67
+
68
+ def _env_block(path: Path) -> Mapping[str, object] | None:
69
+ try:
70
+ data = json.loads(path.read_text(encoding="utf-8"))
71
+ except (OSError, ValueError, RecursionError, RuntimeError):
72
+ return None
73
+ block = data.get("env") if isinstance(data, dict) else None
74
+ return block if isinstance(block, dict) else None
75
+
76
+
77
+ def detect(project_dir: str | Path, env: Mapping[str, str]) -> Detection:
78
+ """Find the first source defining the key and classify its value."""
79
+ if KEY in env:
80
+ raw = env[KEY]
81
+ return Detection(
82
+ Status.CONFIGURED if validate_pct(raw) is not None else Status.INVALID,
83
+ raw,
84
+ "process env",
85
+ )
86
+ root = Path(project_dir)
87
+ candidates = (
88
+ ("settings.local.json", root / ".claude" / "settings.local.json"),
89
+ ("settings.json", root / ".claude" / "settings.json"),
90
+ ("user settings.json", user_settings_path(env)),
91
+ )
92
+ for label, path in candidates:
93
+ block = _env_block(path)
94
+ if block is not None and KEY in block:
95
+ raw = block[KEY]
96
+ ok = validate_pct(raw) is not None
97
+ return Detection(Status.CONFIGURED if ok else Status.INVALID, raw, label)
98
+ return Detection(Status.ABSENT)
99
+
100
+
101
+ def autocompact_configured(project_dir: str | Path, env: Mapping[str, str]) -> Status:
102
+ """CONFIGURED (valid), INVALID (present, bad value) or ABSENT."""
103
+ return detect(project_dir, env).status