claude-dev-env 4.2.0 → 7.0.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 (350) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +7 -0
  12. package/.agents/skills/pull-request/SKILL.md +216 -0
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
  15. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
  16. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +69 -0
  17. package/.agents/skills/pull-request/scripts/pull_request.py +265 -0
  18. package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
  19. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +58 -0
  20. package/.agents/skills/pull-request/scripts/test_pull_request.py +396 -0
  21. package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/test-runner/SKILL.md +69 -0
  24. package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
  25. package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
  26. package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
  27. package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
  28. package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
  29. package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
  30. package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
  31. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  32. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  33. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  34. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  35. package/bin/ever-shipped-skills.mjs +2 -0
  36. package/bin/ever-shipped-skills.test.mjs +8 -0
  37. package/bin/install.agents-home.test.mjs +15 -13
  38. package/bin/install.mjs +37 -1
  39. package/bin/install.policy-lint.test.mjs +78 -0
  40. package/bin/install.prune.test.mjs +149 -20
  41. package/bin/install.pstack-plugin.test.mjs +114 -0
  42. package/bin/install.test.mjs +61 -7
  43. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  44. package/hooks/advisory/pr_done_reminder.py +342 -0
  45. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  46. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  47. package/hooks/blocking/code_rules_paired_test.py +22 -15
  48. package/hooks/blocking/pii_prevention_blocker.py +32 -51
  49. package/hooks/blocking/test_bash_post_call_dispatcher.py +55 -17
  50. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
  51. package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
  52. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  53. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  54. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  55. package/hooks/blocking/test_pii_prevention_blocker.py +37 -89
  56. package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
  57. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  58. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
  59. package/hooks/gh_post_body_texts.py +195 -0
  60. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  61. package/hooks/git-hooks/pre_commit.py +52 -11
  62. package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
  63. package/hooks/git-hooks/test_native_policy_commit.py +87 -0
  64. package/hooks/git-hooks/test_pre_commit.py +246 -0
  65. package/hooks/hooks.json +1 -16
  66. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -2
  67. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
  68. package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
  69. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  70. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
  71. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +17 -7
  72. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
  73. package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
  74. package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
  75. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  76. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
  77. package/hooks/observability/test_failure_recorder.py +1 -1
  78. package/hooks/pyproject.toml +1 -1
  79. package/hooks/test_gh_post_body_texts.py +79 -0
  80. package/package.json +2 -2
  81. package/rules/durable-post-artifacts.md +32 -20
  82. package/rules/gh-cli-conventions.md +23 -13
  83. package/rules/git-workflow.md +21 -8
  84. package/scripts/check.ps1 +0 -1
  85. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  86. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  87. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  88. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  89. package/scripts/durable_post_lint.py +206 -0
  90. package/scripts/durable_post_lint_config/config/constants.py +96 -0
  91. package/scripts/durable_post_lint_config/config/test_constants.py +25 -0
  92. package/scripts/policy_lint/adapter_configuration.py +53 -12
  93. package/scripts/policy_lint/adapter_detectors.py +13 -6
  94. package/scripts/policy_lint/adapters.py +7 -4
  95. package/scripts/policy_lint/config/constants.py +5 -0
  96. package/scripts/policy_lint/engine.py +48 -4
  97. package/scripts/policy_lint/model.py +1 -0
  98. package/scripts/policy_lint/registry.py +1 -1
  99. package/scripts/policy_lint/selection.py +2 -1
  100. package/scripts/policy_lint/selection_git.py +2 -0
  101. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  102. package/scripts/repository_checks/config/constants.py +0 -5
  103. package/scripts/shared_tree_paths.py +32 -3
  104. package/scripts/test_durable_post_lint.py +332 -0
  105. package/scripts/test_shared_tree_paths.py +33 -0
  106. package/scripts/tests/test_adapter_configuration.py +54 -0
  107. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  108. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  109. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  110. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  111. package/scripts/tests/test_policy_lint_selection.py +25 -2
  112. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  113. package/scripts/tests/test_rules.py +3 -7
  114. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  115. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
  116. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  117. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  118. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  119. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  120. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  121. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  122. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  123. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  124. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  125. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  126. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  127. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  128. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  129. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  130. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  131. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  132. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  133. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  134. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  135. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  136. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  137. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  138. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  139. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  140. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  141. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  142. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  143. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  144. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  145. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  146. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  147. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  148. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  149. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  150. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  151. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  152. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  153. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  154. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  155. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  156. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  157. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  158. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  159. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  160. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  161. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  162. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  163. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  164. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  165. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  166. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  167. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  168. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  169. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  170. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  171. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  172. package/.agents/skills/autoconverge/SKILL.md +0 -431
  173. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  174. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  175. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  176. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  177. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  178. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  179. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  180. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  181. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  182. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  183. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  184. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  185. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  186. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  187. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  188. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  189. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  190. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  191. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  192. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  193. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  194. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  195. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  196. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  197. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  198. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  199. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  200. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  201. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  202. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  203. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  204. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  205. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  206. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  207. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  208. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  209. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  210. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  211. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  212. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  213. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  214. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  215. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  216. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  217. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  218. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  219. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  220. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  221. package/.agents/skills/hitl/SKILL.md +0 -45
  222. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  223. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  224. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  225. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  226. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  227. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  228. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  229. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  230. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  231. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  232. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  233. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  234. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  235. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  236. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  237. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  238. package/.agents/skills/prototype/SKILL.md +0 -85
  239. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  240. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  241. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  242. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  243. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  244. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  245. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  246. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  247. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  248. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  249. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  250. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  251. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  252. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  253. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  254. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  255. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  256. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  257. package/.agents/skills/rebase/AGENTS.md +0 -1
  258. package/.agents/skills/rebase/SKILL.md +0 -178
  259. package/.agents/skills/review-router/SKILL.md +0 -51
  260. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  261. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  262. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  263. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  264. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  265. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  266. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  267. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  268. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  269. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  270. package/.agents/skills/review-tier/SKILL.md +0 -56
  271. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  272. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  273. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  274. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  275. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  276. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  277. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  278. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  279. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  280. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  281. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/session-log/AGENTS.md +0 -1
  283. package/.agents/skills/session-log/SKILL.md +0 -170
  284. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  285. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  286. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  287. package/.agents/skills/session-tidy/SKILL.md +0 -191
  288. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  289. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  290. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  291. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  292. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  293. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  294. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  295. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  296. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  297. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  298. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  299. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  300. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  301. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  302. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  303. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  304. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  305. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  306. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  307. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  308. package/.agents/skills/update/AGENTS.md +0 -1
  309. package/.agents/skills/update/SKILL.md +0 -190
  310. package/_shared/pr-loop/AGENTS.md +0 -1
  311. package/_shared/pr-loop/CLAUDE.md +0 -1
  312. package/_shared/pr-loop/audit-contract.md +0 -183
  313. package/_shared/pr-loop/audit-reply-template.md +0 -151
  314. package/_shared/pr-loop/code-rules-gate.md +0 -65
  315. package/_shared/pr-loop/fix-protocol.md +0 -60
  316. package/_shared/pr-loop/gh-payloads.md +0 -75
  317. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  318. package/_shared/pr-loop/precatch-rubric.md +0 -66
  319. package/_shared/pr-loop/preflight-proposal.md +0 -56
  320. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  321. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  322. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  323. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  324. package/_shared/pr-loop/state-schema.md +0 -45
  325. package/_shared/pr-loop/worker-spawn.md +0 -189
  326. package/hooks/_gh_pr_author_swap_utils.py +0 -1225
  327. package/hooks/blocking/conventional_pr_title_gate.py +0 -443
  328. package/hooks/blocking/gh_body_arg_blocker.py +0 -176
  329. package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
  330. package/hooks/blocking/gh_pr_author_restore.py +0 -105
  331. package/hooks/blocking/pr_description_writer_gate.py +0 -256
  332. package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
  333. package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
  334. package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
  335. package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
  336. package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
  337. package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
  338. package/hooks/blocking/test_preflight_check.py +0 -343
  339. package/hooks/blocking/test_test_preflight_check.py +0 -80
  340. package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
  341. package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
  342. package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
  343. package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
  344. package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
  345. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
  346. package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
  347. package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
  348. package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
  349. package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
  350. package/hooks/test__gh_pr_author_swap_utils.py +0 -333
@@ -42,6 +42,14 @@ SYNTHETIC_REAL_EMAIL = "owner.fixture@acme-corp.example.io"
42
42
  SYNTHETIC_HOME_PATH = r"C:\Users\fixture_commit_user\secret.txt"
43
43
  SYNTHETIC_PRIVATE_IP = "10.44.12.9"
44
44
  SYNTHETIC_SAFE_BODY = "All checks pass. Contact user@example.com for docs."
45
+ HASH_MARKER = "#"
46
+
47
+
48
+ def test_post_body_extraction_has_a_neutral_module_owner() -> None:
49
+ assert (
50
+ blocker_module.extract_gh_post_body_texts_for_privacy_gate.__module__
51
+ == "gh_post_body_texts"
52
+ )
45
53
 
46
54
 
47
55
  def _run_hook(payload: dict[str, object]) -> tuple[int, str]:
@@ -370,10 +378,7 @@ def test_dispatcher_roster_hosts_pii_blocker() -> None:
370
378
  if each_entry.script_relative_path == "blocking/pii_prevention_blocker.py"
371
379
  ]
372
380
  assert len(matching_bash_entries) == 1
373
- assert (
374
- matching_bash_entries[0].applicable_tool_names
375
- == ALL_BASH_AND_POWERSHELL_TOOL_NAMES
376
- )
381
+ assert matching_bash_entries[0].applicable_tool_names == ALL_BASH_AND_POWERSHELL_TOOL_NAMES
377
382
 
378
383
 
379
384
  def test_is_git_commit_shell_command_covers_windows_and_flag_forms() -> None:
@@ -531,9 +536,7 @@ def test_is_git_commit_shell_command_detects_glued_and_newline_separators() -> N
531
536
  assert is_git_commit_shell_command("cd repo&&git commit -m x")
532
537
  assert is_git_commit_shell_command("git add notes.md\ngit commit -m update")
533
538
  assert is_git_commit_shell_command("git status\ngit commit -m x")
534
- assert not is_git_commit_shell_command(
535
- "gh pr comment 1 --body 'notes\ngit commit -m x'"
536
- )
539
+ assert not is_git_commit_shell_command("gh pr comment 1 --body 'notes\ngit commit -m x'")
537
540
 
538
541
 
539
542
  def test_is_git_commit_shell_command_detects_backgrounded_commit() -> None:
@@ -681,11 +684,9 @@ def test_argument_bearing_wrapper_commit_scans_staged_pii(tmp_path: Path) -> Non
681
684
 
682
685
 
683
686
  def test_is_git_commit_shell_command_detects_commit_after_hash_bearing_token() -> None:
684
- assert is_git_commit_shell_command(
685
- "curl https://example.com/page#section && git commit -am x"
686
- )
687
+ command = f"curl https://example.com/page{HASH_MARKER}section && git commit -am x"
688
+ assert is_git_commit_shell_command(command)
687
689
  assert is_git_commit_shell_command("echo done#note && git commit -m x")
688
- assert is_git_commit_shell_command('git commit -m "fix #123"')
689
690
 
690
691
 
691
692
  def test_hash_bearing_token_before_commit_scans_staged_pii(tmp_path: Path) -> None:
@@ -721,21 +722,13 @@ def test_is_git_commit_shell_command_detects_unquoted_powershell_command() -> No
721
722
 
722
723
 
723
724
  def test_is_git_commit_shell_command_detects_powershell_preflags_before_command() -> None:
724
- assert is_git_commit_shell_command(
725
- 'pwsh -ExecutionPolicy Bypass -Command "git commit -m x"'
726
- )
727
- assert is_git_commit_shell_command(
728
- 'pwsh -NonInteractive -Command "git commit -m x"'
729
- )
725
+ assert is_git_commit_shell_command('pwsh -ExecutionPolicy Bypass -Command "git commit -m x"')
726
+ assert is_git_commit_shell_command('pwsh -NonInteractive -Command "git commit -m x"')
730
727
  assert is_git_commit_shell_command(
731
728
  'powershell -ExecutionPolicy Bypass -Command "git commit -m x"'
732
729
  )
733
- assert is_git_commit_shell_command(
734
- 'pwsh -NoProfile -NonInteractive -Command "git commit -m x"'
735
- )
736
- assert not is_git_commit_shell_command(
737
- 'pwsh -NonInteractive -Command "git status"'
738
- )
730
+ assert is_git_commit_shell_command('pwsh -NoProfile -NonInteractive -Command "git commit -m x"')
731
+ assert not is_git_commit_shell_command('pwsh -NonInteractive -Command "git status"')
739
732
 
740
733
 
741
734
  def test_is_git_commit_shell_command_detects_subshell_grouped_commit() -> None:
@@ -835,9 +828,7 @@ def test_is_git_commit_shell_command_detects_commit_after_apostrophe_line() -> N
835
828
 
836
829
 
837
830
  def test_is_git_commit_shell_command_ignores_commit_inside_quoted_body() -> None:
838
- assert not is_git_commit_shell_command(
839
- "gh pr comment 1 --body 'notes\ngit commit -m x'"
840
- )
831
+ assert not is_git_commit_shell_command("gh pr comment 1 --body 'notes\ngit commit -m x'")
841
832
 
842
833
 
843
834
  def test_apostrophe_line_before_commit_scans_staged_pii(tmp_path: Path) -> None:
@@ -873,17 +864,9 @@ def test_powershell_backtick_continuation_commit_scans_staged_pii(
873
864
 
874
865
 
875
866
  def test_extract_git_commit_working_directory_resolves_dash_c_forms() -> None:
876
- assert (
877
- extract_git_commit_working_directory(r"git.exe -C /repoA commit -m x")
878
- == "/repoA"
879
- )
880
- assert (
881
- extract_git_commit_working_directory(r"git -C /repoA commit -m x") == "/repoA"
882
- )
883
- assert (
884
- extract_git_commit_working_directory(r'git -C "C:/repo" commit -m x')
885
- == "C:/repo"
886
- )
867
+ assert extract_git_commit_working_directory(r"git.exe -C /repoA commit -m x") == "/repoA"
868
+ assert extract_git_commit_working_directory(r"git -C /repoA commit -m x") == "/repoA"
869
+ assert extract_git_commit_working_directory(r'git -C "C:/repo" commit -m x') == "C:/repo"
887
870
  assert extract_git_commit_working_directory("git commit -m x") is None
888
871
 
889
872
 
@@ -921,9 +904,7 @@ def test_exempt_repository_commit_skips_staged_pii_scan(
921
904
  ) -> None:
922
905
  repository_root = tmp_path / "repo"
923
906
  _init_repo_with_staged_email(repository_root)
924
- _add_repository_origin(
925
- repository_root, "https://github.com/ExemptOwner/exempt-repo.git"
926
- )
907
+ _add_repository_origin(repository_root, "https://github.com/ExemptOwner/exempt-repo.git")
927
908
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "ExemptOwner/exempt-repo")
928
909
  deny_reason = evaluate_bash_command(
929
910
  "git commit -m test",
@@ -937,9 +918,7 @@ def test_exempt_repository_matches_ssh_origin_url(
937
918
  ) -> None:
938
919
  repository_root = tmp_path / "repo"
939
920
  _init_repo_with_staged_email(repository_root)
940
- _add_repository_origin(
941
- repository_root, "git@github.com:ExemptOwner/exempt-repo.git"
942
- )
921
+ _add_repository_origin(repository_root, "git@github.com:ExemptOwner/exempt-repo.git")
943
922
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "exemptowner/EXEMPT-REPO")
944
923
  deny_reason = evaluate_bash_command(
945
924
  "git commit -m test",
@@ -953,9 +932,7 @@ def test_non_exempt_repository_commit_still_scans_staged_pii(
953
932
  ) -> None:
954
933
  repository_root = tmp_path / "repo"
955
934
  _init_repo_with_staged_email(repository_root)
956
- _add_repository_origin(
957
- repository_root, "https://github.com/OtherOwner/other-repo.git"
958
- )
935
+ _add_repository_origin(repository_root, "https://github.com/OtherOwner/other-repo.git")
959
936
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "ExemptOwner/exempt-repo")
960
937
  deny_reason = evaluate_bash_command(
961
938
  "git commit -m test",
@@ -981,45 +958,35 @@ def test_repository_without_origin_is_not_exempt(
981
958
 
982
959
  def test_owner_repo_slug_from_https_origin_with_git_suffix() -> None:
983
960
  assert (
984
- blocker_module._owner_repo_slug_from_origin_url(
985
- "https://github.com/Owner/repo.git"
986
- )
961
+ blocker_module._owner_repo_slug_from_origin_url("https://github.com/Owner/repo.git")
987
962
  == "owner/repo"
988
963
  )
989
964
 
990
965
 
991
966
  def test_owner_repo_slug_from_https_origin_without_git_suffix() -> None:
992
967
  assert (
993
- blocker_module._owner_repo_slug_from_origin_url(
994
- "https://github.com/Owner/repo"
995
- )
968
+ blocker_module._owner_repo_slug_from_origin_url("https://github.com/Owner/repo")
996
969
  == "owner/repo"
997
970
  )
998
971
 
999
972
 
1000
973
  def test_owner_repo_slug_from_scp_ssh_origin() -> None:
1001
974
  assert (
1002
- blocker_module._owner_repo_slug_from_origin_url(
1003
- "git@github.com:Owner/repo.git"
1004
- )
975
+ blocker_module._owner_repo_slug_from_origin_url("git@github.com:Owner/repo.git")
1005
976
  == "owner/repo"
1006
977
  )
1007
978
 
1008
979
 
1009
980
  def test_owner_repo_slug_from_ssh_scheme_origin() -> None:
1010
981
  assert (
1011
- blocker_module._owner_repo_slug_from_origin_url(
1012
- "ssh://git@github.com/Owner/repo.git"
1013
- )
982
+ blocker_module._owner_repo_slug_from_origin_url("ssh://git@github.com/Owner/repo.git")
1014
983
  == "owner/repo"
1015
984
  )
1016
985
 
1017
986
 
1018
987
  def test_owner_repo_slug_from_trailing_slash_git_suffix() -> None:
1019
988
  assert (
1020
- blocker_module._owner_repo_slug_from_origin_url(
1021
- "https://github.com/Owner/repo.git/"
1022
- )
989
+ blocker_module._owner_repo_slug_from_origin_url("https://github.com/Owner/repo.git/")
1023
990
  == "owner/repo"
1024
991
  )
1025
992
 
@@ -1035,9 +1002,7 @@ def test_owner_repo_slug_rejects_spoofed_evil_host() -> None:
1035
1002
 
1036
1003
  def test_owner_repo_slug_rejects_notgithub_com_host() -> None:
1037
1004
  assert (
1038
- blocker_module._owner_repo_slug_from_origin_url(
1039
- "https://notgithub.com/Owner/repo.git"
1040
- )
1005
+ blocker_module._owner_repo_slug_from_origin_url("https://notgithub.com/Owner/repo.git")
1041
1006
  is None
1042
1007
  )
1043
1008
 
@@ -1071,28 +1036,21 @@ def test_owner_repo_slug_rejects_invalid_ssh_port_authority() -> None:
1071
1036
 
1072
1037
  def test_owner_repo_slug_from_https_origin_with_valid_port() -> None:
1073
1038
  assert (
1074
- blocker_module._owner_repo_slug_from_origin_url(
1075
- "https://github.com:443/Owner/repo.git"
1076
- )
1039
+ blocker_module._owner_repo_slug_from_origin_url("https://github.com:443/Owner/repo.git")
1077
1040
  == "owner/repo"
1078
1041
  )
1079
1042
 
1080
1043
 
1081
1044
  def test_owner_repo_slug_from_ssh_origin_with_valid_port() -> None:
1082
1045
  assert (
1083
- blocker_module._owner_repo_slug_from_origin_url(
1084
- "ssh://git@github.com:22/Owner/repo.git"
1085
- )
1046
+ blocker_module._owner_repo_slug_from_origin_url("ssh://git@github.com:22/Owner/repo.git")
1086
1047
  == "owner/repo"
1087
1048
  )
1088
1049
 
1089
1050
 
1090
1051
  def test_owner_repo_slug_rejects_path_shaped_origin() -> None:
1091
1052
  assert (
1092
- blocker_module._owner_repo_slug_from_origin_url(
1093
- "C:/repos/ExemptOwner/exempt-repo"
1094
- )
1095
- is None
1053
+ blocker_module._owner_repo_slug_from_origin_url("C:/repos/ExemptOwner/exempt-repo") is None
1096
1054
  )
1097
1055
 
1098
1056
 
@@ -1101,9 +1059,7 @@ def test_spoofed_origin_host_still_denies_staged_email(
1101
1059
  ) -> None:
1102
1060
  repository_root = tmp_path / "repo"
1103
1061
  _init_repo_with_staged_email(repository_root)
1104
- _add_repository_origin(
1105
- repository_root, "https://evil.test/ExemptOwner/exempt-repo.git"
1106
- )
1062
+ _add_repository_origin(repository_root, "https://evil.test/ExemptOwner/exempt-repo.git")
1107
1063
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "ExemptOwner/exempt-repo")
1108
1064
  deny_reason = evaluate_bash_command(
1109
1065
  "git commit -m test",
@@ -1151,9 +1107,7 @@ def test_trailing_slash_git_suffix_origin_still_exempts(
1151
1107
  ) -> None:
1152
1108
  repository_root = tmp_path / "repo"
1153
1109
  _init_repo_with_staged_email(repository_root)
1154
- _add_repository_origin(
1155
- repository_root, "https://github.com/ExemptOwner/exempt-repo.git/"
1156
- )
1110
+ _add_repository_origin(repository_root, "https://github.com/ExemptOwner/exempt-repo.git/")
1157
1111
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "ExemptOwner/exempt-repo")
1158
1112
  deny_reason = evaluate_bash_command(
1159
1113
  "git commit -m test",
@@ -1162,14 +1116,10 @@ def test_trailing_slash_git_suffix_origin_still_exempts(
1162
1116
  assert deny_reason is None
1163
1117
 
1164
1118
 
1165
- def test_ssh_scheme_origin_still_exempts(
1166
- monkeypatch: pytest.MonkeyPatch, tmp_path: Path
1167
- ) -> None:
1119
+ def test_ssh_scheme_origin_still_exempts(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
1168
1120
  repository_root = tmp_path / "repo"
1169
1121
  _init_repo_with_staged_email(repository_root)
1170
- _add_repository_origin(
1171
- repository_root, "ssh://git@github.com/ExemptOwner/exempt-repo.git"
1172
- )
1122
+ _add_repository_origin(repository_root, "ssh://git@github.com/ExemptOwner/exempt-repo.git")
1173
1123
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "ExemptOwner/exempt-repo")
1174
1124
  deny_reason = evaluate_bash_command(
1175
1125
  "git commit -m test",
@@ -1197,9 +1147,7 @@ def test_post_body_still_scans_when_repository_is_exempt(
1197
1147
  monkeypatch: pytest.MonkeyPatch,
1198
1148
  ) -> None:
1199
1149
  monkeypatch.setenv("CLAUDE_PII_EXEMPT_REPOS", "ExemptOwner/exempt-repo")
1200
- deny_reason = evaluate_post_body_texts(
1201
- [f"Reach me at {SYNTHETIC_REAL_EMAIL}"]
1202
- )
1150
+ deny_reason = evaluate_post_body_texts([f"Reach me at {SYNTHETIC_REAL_EMAIL}"])
1203
1151
  assert deny_reason is not None
1204
1152
  assert "email" in deny_reason
1205
1153
 
@@ -428,44 +428,30 @@ def test_context_survives_alongside_deny_reason(
428
428
  assert dispatcher_result.stdout.strip(), "Dispatcher must emit output when denying"
429
429
 
430
430
 
431
- def test_all_deny_reasons_present_when_multiple_hooks_deny() -> None:
432
- """When two or more hooks deny, all their reasons appear in the dispatcher output.
433
-
434
- Uses a Write to an existing markdown path with a historical phrase
435
- ("previously") so write_existing_file_blocker and state_description_blocker
436
- both deny.
437
- """
438
- existing_markdown_path = str(Path(__file__).resolve().parent / "CLAUDE.md")
439
- multi_deny_content = (
440
- "# Guide\n\n"
441
- "Previously the system used a different mechanism.\n"
442
- )
443
- payload_text = _write_payload(existing_markdown_path, multi_deny_content)
431
+ def test_all_deny_reasons_present_when_multiple_hooks_deny(
432
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path,
433
+ ) -> None:
434
+ """Preserve every deny reason from the retained file-boundary checks."""
435
+ monkeypatch.setenv("HOME", str(tmp_path))
436
+ monkeypatch.setenv("USERPROFILE", str(tmp_path))
437
+ existing_sensitive_path = tmp_path / ".env"
438
+ existing_sensitive_path.write_text("VALUE=fixture\n", encoding="utf-8")
439
+ payload_text = _write_payload(str(existing_sensitive_path), "VALUE=changed\n")
444
440
  _assert_dispatcher_matches_individual_hooks(payload_text, WRITE_TOOL_NAME)
445
-
446
441
  dispatcher_result = _run_dispatcher(payload_text)
447
442
  dispatcher_is_deny, dispatcher_reason = _parse_hook_decision(dispatcher_result)
448
- assert dispatcher_is_deny, "Dispatcher must deny when any hook denies"
449
-
450
- applicable_entries = _applicable_entries_for_tool(WRITE_TOOL_NAME)
443
+ assert dispatcher_is_deny
451
444
  all_expected_deny_reasons: list[str] = []
452
- for each_entry in applicable_entries:
445
+ for each_entry in _applicable_entries_for_tool(WRITE_TOOL_NAME):
453
446
  completed_process = _run_hook_subprocess(each_entry.script_relative_path, payload_text)
454
447
  is_deny, reason_text = _parse_hook_decision(completed_process)
455
448
  if is_deny and reason_text:
456
449
  all_expected_deny_reasons.append(reason_text)
457
-
458
450
  assert len(all_expected_deny_reasons) >= 2, (
459
- f"Test payload must trip at least two hooks — got {len(all_expected_deny_reasons)}. "
460
- "Check that an existing path triggers write_existing_file_blocker and "
461
- "'previously' triggers state_description_blocker on a Write."
451
+ "Existing-file and sensitive-file checks must both deny the fixture"
462
452
  )
463
453
  for each_reason in all_expected_deny_reasons:
464
- assert each_reason in dispatcher_reason, (
465
- f"Missing deny reason in dispatcher output.\n"
466
- f"Expected reason: {each_reason!r}\n"
467
- f"Dispatcher reason: {dispatcher_reason!r}"
468
- )
454
+ assert each_reason in dispatcher_reason
469
455
 
470
456
 
471
457
  def test_aggregate_exit_code_two_signals_deny() -> None:
@@ -686,18 +672,12 @@ def test_emit_allow_preserves_system_message_and_additional_context(
686
672
  assert parsed["systemMessage"] == "system-a\nsystem-b"
687
673
 
688
674
 
689
- def test_later_hook_deny_survives_early_hook_exit() -> None:
690
- """Dispatcher denies even when an earlier hook exits cleanly before a later hook denies.
691
-
692
- state_description_blocker denies a markdown write with a historical phrase.
693
- Earlier hooks exit 0 (allow). The dispatcher must catch each hook's
694
- SystemExit and continue, so the later denial reaches the aggregator.
695
- """
696
- payload_text = _write_payload(
697
- _MARKDOWN_FILE_PATH,
698
- "# Doc\n\nPreviously this section used a different mechanism.\n",
699
- )
675
+ def test_later_hook_deny_survives_early_hook_exit(tmp_path: Path) -> None:
676
+ """A retained document-reference denial survives earlier successful checks."""
677
+ payload_text = _orphan_claude_md_payload(tmp_path)
700
678
  _assert_dispatcher_matches_individual_hooks(payload_text, WRITE_TOOL_NAME)
679
+ is_deny, reason = _parse_hook_decision(_run_dispatcher(payload_text))
680
+ assert is_deny, reason
701
681
 
702
682
 
703
683
  def test_dispatcher_write_applies_both_groups() -> None:
@@ -712,8 +692,8 @@ def test_dispatcher_write_applies_both_groups() -> None:
712
692
  assert "blocking/write_existing_file_blocker.py" in all_write_script_paths, (
713
693
  "write_existing_file_blocker (Group A) must be in Write applicable set"
714
694
  )
715
- assert len(all_write_entries) == 18, (
716
- f"Write tool must apply to all 18 hosted hooks, got {len(all_write_entries)}"
695
+ assert len(all_write_entries) == 11, (
696
+ f"Write tool must apply to the 11 retained write hooks, got {len(all_write_entries)}"
717
697
  )
718
698
 
719
699
 
@@ -727,8 +707,8 @@ def test_dispatcher_edit_applies_both_groups() -> None:
727
707
  assert "advisory/refactor_guard.py" in all_edit_script_paths, (
728
708
  "refactor_guard is Edit-scoped and hosted, so it belongs in the Edit applicable set"
729
709
  )
730
- assert len(all_edit_entries) == 20, (
731
- f"expected 20 Edit entries, got {len(all_edit_entries)}"
710
+ assert len(all_edit_entries) == 13, (
711
+ f"expected 13 retained Edit entries, got {len(all_edit_entries)}"
732
712
  )
733
713
 
734
714
 
@@ -751,7 +731,7 @@ def test_dispatcher_multi_edit_reaches_the_sensitive_protector() -> None:
751
731
  def test_dispatcher_apply_patch_applies_immediate_harm_hooks() -> None:
752
732
  """apply_patch applies to exactly the immediate-harm roster.
753
733
 
754
- apply_patch reaches only the immediate-harm-plus-TDD roster the constants
734
+ apply_patch reaches only the retained immediate-harm roster the constants
755
735
  module names, not the full Write/Edit/MultiEdit lint surface.
756
736
  """
757
737
  all_apply_patch_entries = _applicable_entries_for_tool(APPLY_PATCH_TOOL_NAME)
@@ -885,45 +865,19 @@ def _parse_hook_allow(completed_process: subprocess.CompletedProcess[str]) -> bo
885
865
  return hook_specific.get("permissionDecision") == "allow"
886
866
 
887
867
 
888
- def test_dispatcher_reemits_explicit_allow_from_tdd_enforcer(tmp_path: Path) -> None:
889
- """The dispatcher re-emits an explicit allow when tdd_enforcer's allow branch fires.
890
-
891
- tdd_enforcer writes an explicit allow payload for a constants-only Python
892
- Write, which auto-approves the write standalone. Run against the dispatcher,
893
- the same payload must produce an explicit allow decision identical to the
894
- standalone tdd_enforcer output, rather than a silent fall-back to the default
895
- permission flow.
896
-
897
- Args:
898
- tmp_path: Pytest temp directory hosting the fresh config target path.
899
- """
868
+ def test_retired_tdd_hook_cannot_auto_approve_the_write(tmp_path: Path) -> None:
869
+ """Pairing belongs to staged lint; its edit-time auto-approval is absent."""
900
870
  config_target_path = str(tmp_path / "config" / "timing.py")
901
871
  constants_only_content = (
902
872
  '"""Timing constants."""\n\nMAXIMUM_RETRIES = 3\nRETRY_DELAY_SECONDS = 5\n'
903
873
  )
904
874
  payload_text = _write_payload(config_target_path, constants_only_content)
905
-
906
875
  standalone_result = _run_hook_subprocess("blocking/tdd_enforcer.py", payload_text)
907
- assert _parse_hook_allow(standalone_result), (
908
- "tdd_enforcer must emit an explicit allow for a constants-only Python Write — "
909
- "if it does not, this fixture no longer exercises the allow branch"
910
- )
911
-
876
+ assert _parse_hook_allow(standalone_result)
912
877
  dispatcher_result = _run_dispatcher(payload_text)
913
- dispatcher_is_deny, _reason = _parse_hook_decision(dispatcher_result)
914
- assert not dispatcher_is_deny, "dispatcher must not deny a payload tdd_enforcer allows"
915
- assert _parse_hook_allow(dispatcher_result), (
916
- "dispatcher must re-emit an explicit allow when a hosted hook allows explicitly "
917
- "and no hook denies, matching the standalone tdd_enforcer behavior — "
918
- f"got stdout {dispatcher_result.stdout.strip()!r}"
919
- )
920
- dispatcher_payload = json.loads(dispatcher_result.stdout.strip())
921
- standalone_payload = json.loads(standalone_result.stdout.strip())
922
- assert dispatcher_payload == standalone_payload, (
923
- "dispatcher allow payload must match the standalone tdd_enforcer allow payload.\n"
924
- f"Standalone: {standalone_payload!r}\n"
925
- f"Dispatcher: {dispatcher_payload!r}"
926
- )
878
+ dispatcher_is_deny, reason = _parse_hook_decision(dispatcher_result)
879
+ assert not dispatcher_is_deny, reason
880
+ assert not _parse_hook_allow(dispatcher_result)
927
881
 
928
882
 
929
883
  def test_runpy_hosted_hook_sees_its_own_argv_not_the_dispatchers(tmp_path: Path) -> None:
@@ -1007,13 +961,7 @@ def _dispatcher_matchers(all_pre_tool_use_groups: list[dict]) -> list[str]:
1007
961
 
1008
962
 
1009
963
  def test_apply_patch_has_no_standalone_code_rules_enforcer_hooks_json_entry() -> None:
1010
- """apply_patch's code_rules_enforcer coverage comes only from the dispatcher.
1011
-
1012
- hooks.json once registered a standalone apply_patch to code_rules_enforcer.py
1013
- PreToolUse command. That entry is now folded into the dispatcher's own
1014
- Write|Edit|MultiEdit|apply_patch matcher, so no separate command may still
1015
- spawn code_rules_enforcer.py for apply_patch on its own.
1016
- """
964
+ """The retired code-rules hook has no direct apply_patch registration."""
1017
965
  hooks_json_text = (_HOOKS_ROOT / "hooks.json").read_text(encoding="utf-8")
1018
966
  all_pre_tool_use_groups = json.loads(hooks_json_text)["hooks"]["PreToolUse"]
1019
967
 
@@ -1115,60 +1063,47 @@ def test_dispatcher_denies_apply_patch_add_onto_an_existing_path(tmp_path: Path)
1115
1063
 
1116
1064
 
1117
1065
  def test_dispatcher_allows_clean_apply_patch_add(tmp_path: Path) -> None:
1118
- """The dispatcher allows an apply_patch "add" whose paired test already exists.
1119
-
1120
- tdd_enforcer now reaches apply_patch the same way it reaches Write: an
1121
- untested production file is exactly the immediate-harm case the apply_patch
1122
- boundary rule names, so "clean" here means test-first was honored before
1123
- the tool call, not that TDD is skipped for this tool. The paired test file
1124
- is written to disk first, matching how test_dispatcher_surfaces_migration_
1125
- warning_for_edit proves a fresh Edit passes tdd_enforcer.
1126
- """
1127
- (tmp_path / "test_services.py").write_text(
1128
- "def test_add_one():\n assert True\n", encoding="utf-8"
1129
- )
1066
+ """A clean patch can proceed before the staged linter evaluates test pairing."""
1130
1067
  patch_command = _codex_add_patch(
1131
- "services.py",
1132
- "def add_one(value: int) -> int:\n return value + 1\n",
1068
+ "services.py", "def add_one(value: int) -> int:\n return value + 1\n",
1133
1069
  )
1134
1070
  payload_text = _apply_patch_payload(str(tmp_path), patch_command)
1135
-
1136
1071
  dispatcher_result = _run_dispatcher(payload_text)
1137
1072
  is_deny, reason_text = _parse_hook_decision(dispatcher_result)
1138
- assert not is_deny, (
1139
- f"dispatcher must allow an apply_patch add with a fresh paired test, "
1140
- f"got deny reason {reason_text!r}"
1141
- )
1142
-
1143
-
1144
- def test_hosted_hook_set_covers_all_write_edit_blocking_hooks() -> None:
1145
- """The hosted hook set covers all previously-registered Write/Edit blocking hooks.
1146
-
1147
- Verifies that removing the standalone gate entries from hooks.json did not
1148
- silently drop coverage: every script path that was registered as a blocking
1149
- PreToolUse hook for Write/Edit is present in the dispatcher's hosted set.
1150
- """
1151
- all_hosted_script_paths = frozenset(
1073
+ assert not is_deny, reason_text
1074
+
1075
+
1076
+ ALL_RETAINED_PATHS = {
1077
+ "blocking/write_existing_file_blocker.py",
1078
+ "blocking/sensitive_file_protector.py",
1079
+ "blocking/pii_prevention_blocker.py",
1080
+ "validation/hook_format_validator.py",
1081
+ "blocking/duplicate_rmtree_helper_blocker.py",
1082
+ "blocking/claude_md_orphan_file_blocker.py",
1083
+ "blocking/package_inventory_stale_blocker.py",
1084
+ "blocking/env_var_table_code_drift_blocker.py",
1085
+ "blocking/pytest_testpaths_orphan_blocker.py",
1086
+ "blocking/open_questions_in_plans_blocker.py",
1087
+ "blocking/docstring_rule_gate_count_blocker.py",
1088
+ "advisory/refactor_guard.py",
1089
+ "advisory/migration_safety_advisor.py",
1090
+ }
1091
+ ALL_RETIRED_PATHS = {
1092
+ "blocking/code_rules_enforcer.py",
1093
+ "blocking/tdd_enforcer.py",
1094
+ "blocking/windows_rmtree_blocker.py",
1095
+ "blocking/state_description_blocker.py",
1096
+ "blocking/subprocess_budget_completeness.py",
1097
+ "blocking/hook_prose_detector_consistency.py",
1098
+ "blocking/workflow_substitution_slot_blocker.py",
1099
+ }
1100
+
1101
+ def test_hosted_hook_set_retains_boundaries_and_excludes_replaced_file_checks() -> None:
1102
+ """Keep unresolved controls and exclude the seven staged-linter replacements."""
1103
+ all_hosted_script_paths = {
1152
1104
  each_entry.script_relative_path for each_entry in ALL_HOSTED_HOOK_ENTRIES
1153
- )
1154
- previously_registered_blocking_hooks: frozenset[str] = frozenset({
1155
- "blocking/write_existing_file_blocker.py",
1156
- "blocking/sensitive_file_protector.py",
1157
- "validation/hook_format_validator.py",
1158
- "blocking/code_rules_enforcer.py",
1159
- "blocking/tdd_enforcer.py",
1160
- "blocking/windows_rmtree_blocker.py",
1161
- "blocking/state_description_blocker.py",
1162
- "blocking/subprocess_budget_completeness.py",
1163
- "blocking/hook_prose_detector_consistency.py",
1164
- "blocking/workflow_substitution_slot_blocker.py",
1165
- "blocking/claude_md_orphan_file_blocker.py",
1166
- "blocking/pytest_testpaths_orphan_blocker.py",
1167
- "blocking/open_questions_in_plans_blocker.py",
1168
- })
1169
- for each_script_path in previously_registered_blocking_hooks:
1170
- assert each_script_path in all_hosted_script_paths, (
1171
- f"Previously-registered blocking hook {each_script_path!r} is missing "
1172
- "from the dispatcher's hosted hook set — coverage was lost when the "
1173
- "standalone entry was removed from hooks.json"
1174
- )
1105
+ }
1106
+ assert all_hosted_script_paths == ALL_RETAINED_PATHS
1107
+ assert not all_hosted_script_paths.intersection(ALL_RETIRED_PATHS)
1108
+ for each_relative_path in ALL_RETIRED_PATHS:
1109
+ assert (_HOOKS_ROOT / each_relative_path).is_file()