claude-dev-env 2.12.0 → 2.14.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 (399) hide show
  1. package/AGENTS.md +61 -0
  2. package/CLAUDE.md +1 -57
  3. package/_shared/AGENTS.md +15 -0
  4. package/_shared/CLAUDE.md +1 -15
  5. package/_shared/advisor/AGENTS.md +22 -0
  6. package/_shared/advisor/CLAUDE.md +1 -22
  7. package/_shared/pr-loop/AGENTS.md +27 -0
  8. package/_shared/pr-loop/CLAUDE.md +1 -27
  9. package/_shared/pr-loop/scripts/AGENTS.md +35 -0
  10. package/_shared/pr-loop/scripts/CLAUDE.md +1 -35
  11. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/AGENTS.md +42 -0
  13. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +1 -42
  14. package/_shared/pr-loop/scripts/pr_loop_shared_constants/AGENTS.md +26 -0
  15. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -26
  16. package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
  17. package/_shared/pr-loop/scripts/tests/AGENTS.md +44 -0
  18. package/_shared/pr-loop/scripts/tests/CLAUDE.md +1 -44
  19. package/_shared/process-tree/AGENTS.md +41 -0
  20. package/_shared/process-tree/CLAUDE.md +1 -41
  21. package/agents/AGENTS.md +31 -0
  22. package/agents/CLAUDE.md +1 -32
  23. package/agents/clean-coder.md +6 -6
  24. package/agents/pr-description-writer.md +11 -81
  25. package/agents/test_agent_frontmatter.py +4 -4
  26. package/audit-rubrics/AGENTS.md +43 -0
  27. package/audit-rubrics/CLAUDE.md +1 -43
  28. package/audit-rubrics/category_rubrics/AGENTS.md +37 -0
  29. package/audit-rubrics/category_rubrics/CLAUDE.md +1 -37
  30. package/audit-rubrics/prompts/AGENTS.md +37 -0
  31. package/audit-rubrics/prompts/CLAUDE.md +1 -37
  32. package/bin/AGENTS.md +112 -0
  33. package/bin/CLAUDE.md +1 -112
  34. package/bin/ever-shipped-skills.mjs +1 -0
  35. package/bin/install-constants.mjs +6 -7
  36. package/bin/install.mjs +71 -13
  37. package/bin/install.prune.test.mjs +26 -0
  38. package/bin/install.settings-defaults.test.mjs +60 -33
  39. package/bin/install.test.mjs +36 -4
  40. package/commands/AGENTS.md +23 -0
  41. package/commands/CLAUDE.md +1 -23
  42. package/commands/hook-log-extract.md +3 -3
  43. package/commands/sr-loop.md +9 -5
  44. package/docs/AGENTS.md +32 -0
  45. package/docs/CLAUDE.md +1 -33
  46. package/docs/CODE_RULES.md +1 -1
  47. package/docs/imagegen.md +22 -0
  48. package/docs/references/AGENTS.md +17 -0
  49. package/docs/references/CLAUDE.md +1 -18
  50. package/hooks/AGENTS.md +31 -0
  51. package/hooks/CLAUDE.md +1 -31
  52. package/hooks/_gh_pr_author_swap_utils.py +14 -0
  53. package/hooks/advisory/AGENTS.md +16 -0
  54. package/hooks/advisory/CLAUDE.md +1 -16
  55. package/hooks/blocking/AGENTS.md +123 -0
  56. package/hooks/blocking/CLAUDE.md +1 -148
  57. package/hooks/blocking/claude_md_orphan_file_blocker_parts/AGENTS.md +28 -0
  58. package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +1 -28
  59. package/hooks/blocking/code_rules_banned_identifiers.py +1 -1
  60. package/hooks/blocking/code_rules_dead_module_constant.py +149 -37
  61. package/hooks/blocking/code_rules_probe_chains.py +6 -2
  62. package/hooks/blocking/config/AGENTS.md +10 -0
  63. package/hooks/blocking/config/CLAUDE.md +1 -22
  64. package/hooks/blocking/conventional_pr_title_gate.py +1 -2
  65. package/hooks/blocking/gh_pr_author_restore.py +7 -2
  66. package/hooks/blocking/intent_only_ending_blocker.py +11 -16
  67. package/hooks/blocking/inventory_intent_records/AGENTS.md +26 -0
  68. package/hooks/blocking/inventory_intent_records/CLAUDE.md +1 -26
  69. package/hooks/blocking/package_inventory_stale_blocker_parts/AGENTS.md +26 -0
  70. package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +1 -26
  71. package/hooks/blocking/pii_prevention_blocker_parts/AGENTS.md +24 -0
  72. package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +1 -24
  73. package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
  74. package/hooks/blocking/stop_dispatcher.py +4 -4
  75. package/hooks/blocking/tdd_enforcer_parts/AGENTS.md +30 -0
  76. package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +1 -30
  77. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
  78. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +1 -1
  79. package/hooks/blocking/test_code_rules_enforcer_dead_module_constant.py +171 -0
  80. package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
  81. package/hooks/blocking/test_gh_pr_author_restore.py +2 -1
  82. package/hooks/blocking/test_intent_only_ending_blocker.py +4 -0
  83. package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
  84. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
  85. package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
  86. package/hooks/diagnostic/AGENTS.md +43 -0
  87. package/hooks/diagnostic/CLAUDE.md +1 -43
  88. package/hooks/diagnostic/hook_log_extractor.py +4 -38
  89. package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
  90. package/hooks/diagnostic/migrations/AGENTS.md +16 -0
  91. package/hooks/diagnostic/migrations/CLAUDE.md +1 -16
  92. package/hooks/diagnostic/queries/AGENTS.md +19 -0
  93. package/hooks/diagnostic/queries/CLAUDE.md +1 -19
  94. package/hooks/diagnostic/test_hook_log_extractor.py +10 -23
  95. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
  96. package/hooks/git-hooks/AGENTS.md +31 -0
  97. package/hooks/git-hooks/CLAUDE.md +1 -31
  98. package/hooks/git-hooks/git_hooks_constants/AGENTS.md +21 -0
  99. package/hooks/git-hooks/git_hooks_constants/CLAUDE.md +1 -21
  100. package/hooks/git-hooks/git_hooks_constants/__init__.py +4 -12
  101. package/hooks/git-hooks/pre_push.py +49 -118
  102. package/hooks/git-hooks/test_pre_push.py +111 -137
  103. package/hooks/hooks.json +0 -35
  104. package/hooks/hooks_constants/AGENTS.md +104 -0
  105. package/hooks/hooks_constants/CLAUDE.md +1 -113
  106. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
  107. package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
  108. package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
  109. package/hooks/hooks_constants/hook_log_extractor_constants.py +1 -2
  110. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
  111. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
  112. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  113. package/hooks/hooks_constants/task_list_loop_starter_constants.py +4 -3
  114. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
  115. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
  116. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  117. package/hooks/hooks_constants/working_style_prompt_constants.py +29 -20
  118. package/hooks/lifecycle/AGENTS.md +18 -0
  119. package/hooks/lifecycle/CLAUDE.md +1 -20
  120. package/hooks/observability/AGENTS.md +20 -0
  121. package/hooks/observability/CLAUDE.md +1 -20
  122. package/hooks/session/AGENTS.md +33 -0
  123. package/hooks/session/CLAUDE.md +1 -33
  124. package/hooks/session/gh_pr_author_session_cleanup.py +7 -2
  125. package/hooks/session/task_list_loop_starter.py +3 -3
  126. package/hooks/session/test_gh_pr_author_session_cleanup.py +2 -1
  127. package/hooks/session/test_task_list_loop_starter.py +10 -2
  128. package/hooks/session/test_working_style_prompt.py +9 -4
  129. package/hooks/validation/AGENTS.md +20 -0
  130. package/hooks/validation/CLAUDE.md +1 -20
  131. package/hooks/validators/AGENTS.md +51 -0
  132. package/hooks/validators/CLAUDE.md +1 -51
  133. package/hooks/validators/health_check.py +52 -19
  134. package/hooks/validators/python_style_checks.py +1 -1
  135. package/hooks/validators/test_health_check.py +112 -27
  136. package/hooks/workflow/AGENTS.md +16 -0
  137. package/hooks/workflow/CLAUDE.md +1 -16
  138. package/installable-surfaces.manifest.json +1 -0
  139. package/output-styles/AGENTS.md +15 -0
  140. package/output-styles/CLAUDE.md +1 -15
  141. package/package.json +2 -1
  142. package/rules/AGENTS.md +61 -0
  143. package/rules/CLAUDE.md +1 -63
  144. package/rules/code-standards.md +5 -9
  145. package/rules/doc-prose-cuts.md +1 -1
  146. package/rules/gh-cli-conventions.md +0 -1
  147. package/rules/git-workflow.md +1 -3
  148. package/scripts/AGENTS.md +62 -0
  149. package/scripts/CLAUDE.md +1 -63
  150. package/scripts/_code_review_test_support.py +0 -95
  151. package/scripts/codex_compat_materializer.py +4 -0
  152. package/scripts/dev_env_scripts_constants/AGENTS.md +21 -0
  153. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -21
  154. package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
  155. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
  156. package/scripts/invoke_code_review.py +8 -451
  157. package/scripts/sync_to_cursor/AGENTS.md +23 -0
  158. package/scripts/sync_to_cursor/CLAUDE.md +1 -23
  159. package/scripts/test_invoke_code_review.py +5 -61
  160. package/scripts/test_invoke_code_review_cli.py +1 -45
  161. package/scripts/test_invoke_code_review_contract.py +1 -118
  162. package/scripts/test_validate_instruction_pairs.py +90 -0
  163. package/scripts/test_verify_installable_package.py +5 -1
  164. package/scripts/tests/AGENTS.md +33 -0
  165. package/scripts/tests/CLAUDE.md +1 -33
  166. package/scripts/tests/test_codex_compat_materializer.py +36 -1
  167. package/scripts/validate_instruction_pairs.py +183 -0
  168. package/settings.json +1 -10
  169. package/skills/AGENTS.md +72 -0
  170. package/skills/CLAUDE.md +1 -64
  171. package/skills/_shared/AGENTS.md +44 -0
  172. package/skills/_shared/CLAUDE.md +1 -44
  173. package/skills/_shared/advisor/AGENTS.md +9 -0
  174. package/skills/_shared/advisor/CLAUDE.md +1 -9
  175. package/skills/_shared/pr-loop/AGENTS.md +50 -0
  176. package/skills/_shared/pr-loop/CLAUDE.md +1 -50
  177. package/skills/_shared/pr-loop/prompts/AGENTS.md +9 -0
  178. package/skills/_shared/pr-loop/prompts/CLAUDE.md +1 -9
  179. package/skills/_shared/pr-loop/scripts/AGENTS.md +35 -0
  180. package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -35
  181. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +24 -0
  182. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -24
  183. package/skills/anthropic-plan/AGENTS.md +34 -0
  184. package/skills/anthropic-plan/CLAUDE.md +1 -34
  185. package/skills/anthropic-plan/scripts/AGENTS.md +11 -0
  186. package/skills/anthropic-plan/scripts/CLAUDE.md +1 -11
  187. package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +16 -0
  188. package/skills/anthropic-plan/scripts/anthropic_plan_scripts_constants/CLAUDE.md +1 -16
  189. package/skills/anthropic-plan/templates/AGENTS.md +13 -0
  190. package/skills/anthropic-plan/templates/CLAUDE.md +1 -13
  191. package/skills/anthropic-plan/workflow/AGENTS.md +14 -0
  192. package/skills/anthropic-plan/workflow/CLAUDE.md +1 -14
  193. package/skills/auditing-claude-config/AGENTS.md +21 -0
  194. package/skills/auditing-claude-config/CLAUDE.md +1 -21
  195. package/skills/autoconverge/AGENTS.md +36 -0
  196. package/skills/autoconverge/CLAUDE.md +1 -36
  197. package/skills/autoconverge/reference/AGENTS.md +16 -0
  198. package/skills/autoconverge/reference/CLAUDE.md +1 -16
  199. package/skills/autoconverge/reference/convergence.md +7 -7
  200. package/skills/autoconverge/reference/gotchas.md +3 -10
  201. package/skills/autoconverge/workflow/AGENTS.md +26 -0
  202. package/skills/autoconverge/workflow/CLAUDE.md +1 -26
  203. package/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +16 -0
  204. package/skills/autoconverge/workflow/autoconverge_report_constants/CLAUDE.md +1 -16
  205. package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
  206. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
  207. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
  208. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
  209. package/skills/autoconverge/workflow/converge.mjs +201 -238
  210. package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
  211. package/skills/bugteam/AGENTS.md +30 -0
  212. package/skills/bugteam/CLAUDE.md +1 -30
  213. package/skills/bugteam/CONSTRAINTS.md +1 -1
  214. package/skills/bugteam/SKILL.md +9 -0
  215. package/skills/bugteam/reference/AGENTS.md +20 -0
  216. package/skills/bugteam/reference/CLAUDE.md +1 -20
  217. package/skills/bugteam/reference/audit-and-teammates.md +10 -6
  218. package/skills/bugteam/reference/obstacles/AGENTS.md +24 -0
  219. package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -24
  220. package/skills/bugteam/scripts/AGENTS.md +30 -0
  221. package/skills/bugteam/scripts/CLAUDE.md +1 -30
  222. package/skills/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +18 -0
  223. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +1 -18
  224. package/skills/bugteam/test_skill_additions.py +9 -0
  225. package/skills/closeout/reference/handoff-prompt-template.md +1 -1
  226. package/skills/codex-review/AGENTS.md +46 -0
  227. package/skills/codex-review/CLAUDE.md +1 -46
  228. package/skills/codex-review/reference/AGENTS.md +15 -0
  229. package/skills/codex-review/reference/CLAUDE.md +1 -15
  230. package/skills/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
  231. package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +1 -18
  232. package/skills/codex-review/test_skill_scaffold.py +3 -3
  233. package/skills/comments/SKILL.md +65 -0
  234. package/skills/copilot-review/AGENTS.md +18 -0
  235. package/skills/copilot-review/CLAUDE.md +1 -18
  236. package/skills/descriptions/SKILL.md +51 -0
  237. package/skills/emergencies/SKILL.md +42 -0
  238. package/skills/everything-search/AGENTS.md +17 -0
  239. package/skills/everything-search/CLAUDE.md +1 -17
  240. package/skills/fresh-branch/AGENTS.md +14 -0
  241. package/skills/fresh-branch/CLAUDE.md +1 -14
  242. package/skills/grok-spawn/AGENTS.md +28 -0
  243. package/skills/grok-spawn/CLAUDE.md +1 -28
  244. package/skills/orchestrator/SKILL.md +2 -4
  245. package/skills/plan-to-pr/SKILL.md +6 -7
  246. package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
  247. package/skills/plan-to-pr/reference/process-inventory.md +2 -2
  248. package/skills/plan-to-pr/reference/review-loop.md +2 -2
  249. package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
  250. package/skills/plan-to-pr/reference/task-seeds.md +1 -1
  251. package/skills/plan-to-pr/reference/task-ticket.md +3 -4
  252. package/skills/plan-to-pr/scripts/config/constants.py +1 -2
  253. package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
  254. package/skills/plan-to-pr/test_skill_contract.py +2 -2
  255. package/skills/pr-converge/AGENTS.md +32 -0
  256. package/skills/pr-converge/CLAUDE.md +1 -32
  257. package/skills/pr-converge/SKILL.md +13 -16
  258. package/skills/pr-converge/pr_converge_skill_constants/AGENTS.md +26 -0
  259. package/skills/pr-converge/pr_converge_skill_constants/CLAUDE.md +1 -26
  260. package/skills/pr-converge/reference/AGENTS.md +28 -0
  261. package/skills/pr-converge/reference/CLAUDE.md +1 -28
  262. package/skills/pr-converge/reference/obstacles/AGENTS.md +23 -0
  263. package/skills/pr-converge/reference/obstacles/CLAUDE.md +1 -23
  264. package/skills/pr-converge/reference/progress-checklist.md +6 -5
  265. package/skills/pr-converge/reference/state-schema.md +1 -27
  266. package/skills/pr-converge/scripts/AGENTS.md +46 -0
  267. package/skills/pr-converge/scripts/CLAUDE.md +1 -46
  268. package/skills/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
  269. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -18
  270. package/skills/pr-converge/workflows/AGENTS.md +16 -0
  271. package/skills/pr-converge/workflows/CLAUDE.md +1 -16
  272. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  273. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
  274. package/skills/pr-loop-lifecycle/SKILL.md +1 -1
  275. package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
  276. package/skills/prototype/SKILL.md +4 -4
  277. package/skills/prototype/reference/honest-limitations.md +5 -5
  278. package/skills/prototype/reference/promotion-tasks.md +2 -2
  279. package/skills/prototype/workflows/promotion.md +4 -6
  280. package/skills/rebase/AGENTS.md +32 -0
  281. package/skills/rebase/CLAUDE.md +1 -32
  282. package/skills/recall/AGENTS.md +30 -0
  283. package/skills/recall/CLAUDE.md +1 -30
  284. package/skills/remember/AGENTS.md +31 -0
  285. package/skills/remember/CLAUDE.md +1 -31
  286. package/skills/reviews/SKILL.md +85 -0
  287. package/skills/session-log/AGENTS.md +32 -0
  288. package/skills/session-log/CLAUDE.md +1 -32
  289. package/skills/session-tidy/AGENTS.md +36 -0
  290. package/skills/session-tidy/CLAUDE.md +1 -36
  291. package/skills/skill-builder/AGENTS.md +49 -0
  292. package/skills/skill-builder/CLAUDE.md +1 -49
  293. package/skills/skill-builder/references/AGENTS.md +25 -0
  294. package/skills/skill-builder/references/CLAUDE.md +1 -25
  295. package/skills/skill-builder/references/deterministic-elements.md +1 -1
  296. package/skills/skill-builder/templates/AGENTS.md +14 -0
  297. package/skills/skill-builder/templates/CLAUDE.md +1 -14
  298. package/skills/skill-builder/workflows/AGENTS.md +19 -0
  299. package/skills/skill-builder/workflows/CLAUDE.md +1 -19
  300. package/skills/small-cl/SKILL.md +52 -0
  301. package/skills/task-build/AGENTS.md +29 -0
  302. package/skills/task-build/CLAUDE.md +1 -29
  303. package/skills/update/AGENTS.md +38 -0
  304. package/skills/update/CLAUDE.md +1 -38
  305. package/system-prompts/AGENTS.md +17 -0
  306. package/system-prompts/CLAUDE.md +1 -17
  307. package/system-prompts/software-engineer.xml +3 -3
  308. package/agents/code-verifier.md +0 -74
  309. package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
  310. package/docs/references/code-review-enforcement.md +0 -132
  311. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
  312. package/hooks/blocking/code_review_gate_deny.py +0 -74
  313. package/hooks/blocking/code_review_pr_create_gate.py +0 -198
  314. package/hooks/blocking/code_review_push_gate.py +0 -145
  315. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
  316. package/hooks/blocking/code_review_stamp_store.py +0 -233
  317. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
  318. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
  319. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
  320. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
  321. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
  322. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
  323. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
  324. package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
  325. package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
  326. package/hooks/blocking/config/verified_commit_constants.py +0 -160
  327. package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
  328. package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
  329. package/hooks/blocking/conftest.py +0 -123
  330. package/hooks/blocking/convergence_gate_blocker.py +0 -310
  331. package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
  332. package/hooks/blocking/pr_description_body_audit.py +0 -148
  333. package/hooks/blocking/pr_description_command_parser.py +0 -237
  334. package/hooks/blocking/pr_description_enforcer.py +0 -227
  335. package/hooks/blocking/pr_description_pr_number.py +0 -155
  336. package/hooks/blocking/pr_description_proof_of_work.py +0 -385
  337. package/hooks/blocking/pr_description_readability.py +0 -364
  338. package/hooks/blocking/reviewer_spawn_gate.py +0 -182
  339. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
  340. package/hooks/blocking/test_code_review_gate_deny.py +0 -54
  341. package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
  342. package/hooks/blocking/test_code_review_push_gate.py +0 -205
  343. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
  344. package/hooks/blocking/test_code_review_stamp_store.py +0 -205
  345. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
  346. package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
  347. package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
  348. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
  349. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
  350. package/hooks/blocking/test_pr_description_enforcer.py +0 -221
  351. package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
  352. package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
  353. package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
  354. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
  355. package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
  356. package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
  357. package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
  358. package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
  359. package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
  360. package/hooks/blocking/test_verification_verdict_store.py +0 -974
  361. package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
  362. package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
  363. package/hooks/blocking/test_verified_commit_gate.py +0 -581
  364. package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
  365. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
  366. package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
  367. package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
  368. package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
  369. package/hooks/blocking/verification_verdict_store.py +0 -1039
  370. package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
  371. package/hooks/blocking/verified_commit_gate.py +0 -113
  372. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
  373. package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
  374. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
  375. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
  376. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
  377. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
  378. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
  379. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
  380. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
  381. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
  382. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
  383. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
  384. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
  385. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
  386. package/hooks/blocking/verifier_verdict_minter.py +0 -280
  387. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
  388. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
  389. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
  390. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
  391. package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
  392. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
  393. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
  394. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
  395. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
  396. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
  397. package/rules/proof-of-work-pr-comments.md +0 -22
  398. package/rules/verified-commit-gate-skip.md +0 -4
  399. package/scripts/Show-Asset.ps1 +0 -106
@@ -1,804 +0,0 @@
1
- """Tests for the verdict-directory shell guard.
2
-
3
- The verified-commit gate trusts that only the minter hook writes verdict
4
- files. The Write/Edit/MultiEdit deny rules in settings.json stop the file
5
- tools, but a shell command (``python -c``, a redirect, an Out-File) reaches
6
- the same directory unless a Bash/PowerShell guard blocks it. These tests
7
- exercise that guard: every shell spelling that targets the verdict directory
8
- is denied, and commands that touch unrelated paths pass.
9
- """
10
-
11
- import importlib.util
12
- import json
13
- import pathlib
14
- import shutil
15
- import subprocess
16
- import sys
17
-
18
- _HOOK_DIR = pathlib.Path(__file__).parent
19
- _HOOKS_DIR = _HOOK_DIR.parent
20
- if str(_HOOK_DIR) not in sys.path:
21
- sys.path.insert(0, str(_HOOK_DIR))
22
- if str(_HOOKS_DIR) not in sys.path:
23
- sys.path.insert(0, str(_HOOKS_DIR))
24
-
25
- from hooks_constants.bash_pre_tool_use_dispatcher_constants import ( # noqa: E402
26
- ALL_BASH_AND_POWERSHELL_TOOL_NAMES,
27
- ALL_BASH_HOSTED_HOOK_ENTRIES,
28
- BASH_TOOL_NAME,
29
- POWERSHELL_TOOL_NAME,
30
- )
31
-
32
- guard_spec = importlib.util.spec_from_file_location(
33
- "verdict_directory_write_blocker",
34
- _HOOK_DIR / "verdict_directory_write_blocker.py",
35
- )
36
- assert guard_spec is not None
37
- assert guard_spec.loader is not None
38
- guard_module = importlib.util.module_from_spec(guard_spec)
39
- guard_spec.loader.exec_module(guard_module)
40
- references_verdict_directory = guard_module.references_verdict_directory
41
- decision_for_payload = guard_module.decision_for_payload
42
-
43
- constants_spec = importlib.util.spec_from_file_location(
44
- "verified_commit_constants",
45
- _HOOK_DIR / "config" / "verified_commit_constants.py",
46
- )
47
- assert constants_spec is not None
48
- assert constants_spec.loader is not None
49
- constants_module = importlib.util.module_from_spec(constants_spec)
50
- constants_spec.loader.exec_module(constants_module)
51
- ROOT_KEY_HEX_LENGTH = constants_module.ROOT_KEY_HEX_LENGTH
52
-
53
-
54
- def test_python_write_text_into_verdict_directory_is_flagged() -> None:
55
- command_text = (
56
- 'python -c "import pathlib; '
57
- "pathlib.Path.home().joinpath('.claude','verification','x.json')"
58
- ".write_text('{}')\""
59
- )
60
- assert references_verdict_directory(command_text) is True
61
-
62
-
63
- def test_home_var_redirect_into_verdict_directory_is_flagged() -> None:
64
- assert (
65
- references_verdict_directory(
66
- "echo forged > $HOME/.claude/verification/abc.json"
67
- )
68
- is True
69
- )
70
-
71
-
72
- def test_tilde_redirect_into_verdict_directory_is_flagged() -> None:
73
- assert (
74
- references_verdict_directory("echo forged > ~/.claude/verification/abc.json")
75
- is True
76
- )
77
-
78
-
79
- def test_powershell_out_file_into_verdict_directory_is_flagged() -> None:
80
- assert (
81
- references_verdict_directory(
82
- "'{}' | Out-File $HOME/.claude/verification/abc.json"
83
- )
84
- is True
85
- )
86
-
87
-
88
- def test_backslash_verdict_path_is_flagged() -> None:
89
- assert (
90
- references_verdict_directory(
91
- "echo forged > C:\\Users\\example\\.claude\\verification\\abc.json"
92
- )
93
- is True
94
- )
95
-
96
-
97
- def test_unrelated_claude_path_is_not_flagged() -> None:
98
- assert (
99
- references_verdict_directory(
100
- "python $HOME/.claude/hooks/blocking/verified_commit_gate.py"
101
- )
102
- is False
103
- )
104
-
105
-
106
- def test_plain_git_commit_is_not_flagged() -> None:
107
- assert references_verdict_directory("git commit -m x") is False
108
-
109
-
110
- def test_cd_then_tilde_relative_write_is_flagged() -> None:
111
- assert (
112
- references_verdict_directory(
113
- "cd ~/.claude && echo x > verification/a.json"
114
- )
115
- is True
116
- )
117
-
118
-
119
- def test_cd_then_absolute_relative_write_is_flagged() -> None:
120
- assert (
121
- references_verdict_directory(
122
- "cd /home/u/.claude && echo x > verification/a.json"
123
- )
124
- is True
125
- )
126
-
127
-
128
- def test_cd_with_trailing_slash_then_relative_write_is_flagged() -> None:
129
- assert (
130
- references_verdict_directory(
131
- "cd ~/.claude/ ; echo x > verification/x.json"
132
- )
133
- is True
134
- )
135
-
136
-
137
- def test_pushd_then_relative_write_is_flagged() -> None:
138
- assert (
139
- references_verdict_directory(
140
- "pushd $HOME/.claude; echo x > verification/abc.json"
141
- )
142
- is True
143
- )
144
-
145
-
146
- def test_set_location_then_relative_write_is_flagged() -> None:
147
- assert (
148
- references_verdict_directory(
149
- "Set-Location ~/.claude; 'x' | Out-File verification/abc.json"
150
- )
151
- is True
152
- )
153
-
154
-
155
- def test_relative_verdict_filename_shape_is_flagged() -> None:
156
- assert (
157
- references_verdict_directory(
158
- "echo forged > verification/0123456789abcdef.json"
159
- )
160
- is True
161
- )
162
-
163
-
164
- def test_verdict_filename_of_root_key_length_is_flagged() -> None:
165
- root_key_length_name = "a" * ROOT_KEY_HEX_LENGTH
166
- assert (
167
- references_verdict_directory(
168
- f"echo forged > verification/{root_key_length_name}.json"
169
- )
170
- is True
171
- )
172
-
173
-
174
- def test_verdict_filename_one_hex_short_of_root_key_length_is_not_flagged() -> None:
175
- too_short_name = "a" * (ROOT_KEY_HEX_LENGTH - 1)
176
- assert (
177
- references_verdict_directory(
178
- f"echo forged > verification/{too_short_name}.json"
179
- )
180
- is False
181
- )
182
-
183
-
184
- def test_verdict_file_pattern_quantifier_tracks_root_key_length() -> None:
185
- expected_quantifier = "{" + str(ROOT_KEY_HEX_LENGTH) + "}"
186
- assert expected_quantifier in constants_module.VERDICT_FILE_RELATIVE_REFERENCE_PATTERN
187
-
188
-
189
- def test_cd_into_verdict_directory_then_bare_write_is_flagged() -> None:
190
- assert (
191
- references_verdict_directory(
192
- "cd ~/.claude/verification && echo x > 0123456789abcdef.json"
193
- )
194
- is True
195
- )
196
-
197
-
198
- def test_pushd_into_verdict_directory_then_bare_write_is_flagged() -> None:
199
- assert (
200
- references_verdict_directory(
201
- "pushd $HOME/.claude/verification; echo x > 0123456789abcdef.json"
202
- )
203
- is True
204
- )
205
-
206
-
207
- def test_set_location_into_verdict_directory_then_bare_write_is_flagged() -> None:
208
- assert (
209
- references_verdict_directory(
210
- "Set-Location ~/.claude/verification; 'x' | Out-File 0123456789abcdef.json"
211
- )
212
- is True
213
- )
214
-
215
-
216
- def test_cd_into_verdict_directory_with_trailing_slash_is_flagged() -> None:
217
- assert (
218
- references_verdict_directory(
219
- "cd ~/.claude/verification/ && echo x > 0123456789abcdef.json"
220
- )
221
- is True
222
- )
223
-
224
-
225
- def test_set_location_path_option_into_verdict_directory_then_bare_write_is_flagged() -> None:
226
- assert (
227
- references_verdict_directory(
228
- "Set-Location -Path ~/.claude/verification; "
229
- "echo forged > 0123456789abcdef.json"
230
- )
231
- is True
232
- )
233
-
234
-
235
- def test_set_location_literal_path_option_into_verdict_directory_then_bare_write_is_flagged() -> None:
236
- assert (
237
- references_verdict_directory(
238
- "Set-Location -LiteralPath ~/.claude/verification; "
239
- "echo forged > 0123456789abcdef.json"
240
- )
241
- is True
242
- )
243
-
244
-
245
- def test_set_location_path_option_into_claude_home_then_relative_write_is_flagged() -> None:
246
- assert (
247
- references_verdict_directory(
248
- "Set-Location -Path ~/.claude; 'x' | Out-File verification/abc.json"
249
- )
250
- is True
251
- )
252
-
253
-
254
- def test_cd_double_dash_terminator_into_verdict_directory_then_bare_write_is_flagged() -> None:
255
- assert (
256
- references_verdict_directory(
257
- "cd -- ~/.claude/verification && echo forged > 0123456789abcdef.json"
258
- )
259
- is True
260
- )
261
-
262
-
263
- def test_set_location_path_option_into_unrelated_directory_then_write_is_not_flagged() -> None:
264
- assert (
265
- references_verdict_directory(
266
- "Set-Location -Path ~/.claude/hooks; echo x > note.txt"
267
- )
268
- is False
269
- )
270
-
271
-
272
- def test_cd_into_verdict_directory_then_cp_is_flagged() -> None:
273
- assert (
274
- references_verdict_directory(
275
- "cd ~/.claude/verification && cp /tmp/forged.json 0123456789abcdef.json"
276
- )
277
- is True
278
- )
279
-
280
-
281
- def test_cd_into_verdict_directory_then_mv_is_flagged() -> None:
282
- assert (
283
- references_verdict_directory(
284
- "cd ~/.claude/verification && mv /tmp/forged.json 0123456789abcdef.json"
285
- )
286
- is True
287
- )
288
-
289
-
290
- def test_cd_into_verdict_directory_then_tee_is_flagged() -> None:
291
- assert (
292
- references_verdict_directory(
293
- "cd ~/.claude/verification && echo '{}' | tee 0123456789abcdef.json"
294
- )
295
- is True
296
- )
297
-
298
-
299
- def test_cd_into_verdict_directory_then_python_write_text_is_flagged() -> None:
300
- assert (
301
- references_verdict_directory(
302
- "cd ~/.claude/verification && "
303
- "python -c \"import pathlib; "
304
- "pathlib.Path('0123456789abcdef.json').write_text('{}')\""
305
- )
306
- is True
307
- )
308
-
309
-
310
- def test_cd_into_verdict_directory_then_install_is_flagged() -> None:
311
- assert (
312
- references_verdict_directory(
313
- "cd ~/.claude/verification && install /tmp/forged.json 0123456789abcdef.json"
314
- )
315
- is True
316
- )
317
-
318
-
319
- def test_cd_into_verdict_directory_ampersand_abutting_tee_is_flagged() -> None:
320
- assert (
321
- references_verdict_directory(
322
- "cd ~/.claude/verification&& echo {} | tee 8a482d8ecd29493f.json"
323
- )
324
- is True
325
- )
326
-
327
-
328
- def test_cd_into_verdict_directory_pipe_abutting_cp_is_flagged() -> None:
329
- assert (
330
- references_verdict_directory(
331
- "cd ~/.claude/verification|cp /tmp/f.json 8a482d8ecd29493f.json"
332
- )
333
- is True
334
- )
335
-
336
-
337
- def test_cd_into_claude_home_ampersand_abutting_relative_write_is_flagged() -> None:
338
- assert (
339
- references_verdict_directory("cd ~/.claude&& echo x > verification/a.json")
340
- is True
341
- )
342
-
343
-
344
- def test_cd_into_claude_home_pipe_abutting_relative_write_is_flagged() -> None:
345
- assert (
346
- references_verdict_directory("cd ~/.claude|tee verification/a.json")
347
- is True
348
- )
349
-
350
-
351
- def test_add_content_obfuscated_path_write_is_flagged() -> None:
352
- probe_command = (
353
- "$p = bytes.fromhex('2e636c61756465').decode() ; Add-Content $p '{}'"
354
- )
355
- assert references_verdict_directory(probe_command) is True
356
-
357
-
358
- def test_char_cast_obfuscated_path_write_is_flagged() -> None:
359
- probe_command = (
360
- "$p = [char]46 + [char[]]@(99,108,97,117,100,101) ; Set-Content $p '{}'"
361
- )
362
- assert references_verdict_directory(probe_command) is True
363
-
364
-
365
- def test_chr_built_path_write_is_flagged() -> None:
366
- probe_command = (
367
- "python -c \"import os; "
368
- "open(os.path.join(os.path.expanduser(chr(126)),chr(46)+'claude',"
369
- "'verification','a.json'),'w')\""
370
- )
371
- assert references_verdict_directory(probe_command) is True
372
-
373
-
374
- def test_pure_chr_chain_verdict_path_forge_is_flagged() -> None:
375
- chr_chain = "+".join(
376
- f"chr({each_byte})"
377
- for each_byte in b"/.claude/verification/a"
378
- )
379
- probe_command = f"python -c \"open({chr_chain},chr(119)).write(chr(48))\""
380
- assert references_verdict_directory(probe_command) is True
381
-
382
-
383
- def test_pure_chr_chain_verification_segment_forge_is_flagged() -> None:
384
- chr_chain = "+".join(f"chr({each_byte})" for each_byte in b"verification")
385
- probe_command = f"python -c \"open({chr_chain},'w')\""
386
- assert references_verdict_directory(probe_command) is True
387
-
388
-
389
- def test_bytes_fromhex_path_write_is_flagged() -> None:
390
- probe_command = (
391
- "python -c \"import os; "
392
- "open(bytes.fromhex('2e636c61756465').decode(),'w')\""
393
- )
394
- assert references_verdict_directory(probe_command) is True
395
-
396
-
397
- def test_base64_decoded_path_write_is_flagged() -> None:
398
- probe_command = (
399
- "python -c \"import base64; "
400
- "open(base64.b64decode('LmNsYXVkZQ==').decode(),'w')\""
401
- )
402
- assert references_verdict_directory(probe_command) is True
403
-
404
-
405
- def test_int_list_bytes_path_write_is_flagged() -> None:
406
- claude_segment_codes = ",".join(str(each_byte) for each_byte in b".claude")
407
- probe_command = (
408
- f"python -c \"open(bytes([{claude_segment_codes}]).decode(),'w')\""
409
- )
410
- assert references_verdict_directory(probe_command) is True
411
-
412
-
413
- def test_int_list_bytearray_path_write_is_flagged() -> None:
414
- verification_segment_codes = ",".join(
415
- str(each_byte) for each_byte in b"verification"
416
- )
417
- probe_command = (
418
- f"python -c \"open(bytearray([{verification_segment_codes}]).decode(),'w')\""
419
- )
420
- assert references_verdict_directory(probe_command) is True
421
-
422
-
423
- def test_int_list_full_verdict_path_forge_is_flagged() -> None:
424
- verdict_path_codes = ",".join(
425
- str(each_byte)
426
- for each_byte in b"/home/u/.claude/verification/0123456789abcdef.json"
427
- )
428
- probe_command = (
429
- f'python3 -c "f=open(bytes([{verdict_path_codes}]).decode(),'
430
- "'w');f.write('{}')\""
431
- )
432
- assert references_verdict_directory(probe_command) is True
433
-
434
-
435
- def test_int_list_bytes_to_unrelated_file_is_not_flagged() -> None:
436
- unrelated_segment_codes = ",".join(str(each_byte) for each_byte in b"ab")
437
- probe_command = (
438
- f"python -c \"open(bytes([{unrelated_segment_codes}]).decode(),'w')\""
439
- )
440
- assert references_verdict_directory(probe_command) is False
441
-
442
-
443
- def test_chr_without_write_primitive_is_not_flagged() -> None:
444
- assert references_verdict_directory("python -c \"print(chr(126))\"") is False
445
-
446
-
447
- def test_chr_write_to_unrelated_path_is_not_flagged() -> None:
448
- assert (
449
- references_verdict_directory('python -c "print(chr(65))" > /tmp/out.txt')
450
- is False
451
- )
452
-
453
-
454
- def test_base64_decode_to_unrelated_file_is_not_flagged() -> None:
455
- assert (
456
- references_verdict_directory(
457
- 'python -c "import base64,sys; '
458
- "open('decoded.bin','wb').write(base64.b64decode(sys.argv[1]))\""
459
- )
460
- is False
461
- )
462
-
463
-
464
- def test_set_content_char_to_unrelated_file_is_not_flagged() -> None:
465
- assert references_verdict_directory("Set-Content out.txt ([char]65)") is False
466
-
467
-
468
- def test_bytes_fromhex_to_unrelated_file_is_not_flagged() -> None:
469
- assert (
470
- references_verdict_directory(
471
- "python -c \"open(bytes.fromhex('6162').decode(),'w')\""
472
- )
473
- is False
474
- )
475
-
476
-
477
- def test_obfuscated_write_to_unrelated_path_with_incidental_verification_word_is_not_flagged() -> None:
478
- assert (
479
- references_verdict_directory(
480
- "python -c \"open(chr(47)+chr(116)); print('verification done')\""
481
- )
482
- is False
483
- )
484
-
485
-
486
- def test_obfuscated_write_with_decoded_segment_after_separator_stays_flagged() -> None:
487
- assert (
488
- references_verdict_directory(
489
- "python -c \"open(bytes.fromhex('2e636c61756465').decode(),'w'); "
490
- "print('saved')\""
491
- )
492
- is True
493
- )
494
-
495
-
496
- def test_obfuscated_write_with_incidental_claude_word_after_separator_is_not_flagged() -> None:
497
- assert (
498
- references_verdict_directory(
499
- "python -c \"open(chr(47)+chr(116)); print('claude run complete')\""
500
- )
501
- is False
502
- )
503
-
504
-
505
- def test_base64_encoded_verification_segment_obfuscated_write_is_flagged() -> None:
506
- probe_command = (
507
- "python -c \"import base64; "
508
- "open(base64.b64decode('dmVyaWZpY2F0aW9u').decode(),'w')\""
509
- )
510
- assert references_verdict_directory(probe_command) is True
511
-
512
-
513
- def test_hex_encoded_verification_segment_obfuscated_write_is_flagged() -> None:
514
- probe_command = (
515
- "python -c \"open(bytes.fromhex('766572696669636174696f6e').decode(),'w')\""
516
- )
517
- assert references_verdict_directory(probe_command) is True
518
-
519
-
520
- def test_split_cd_into_verdict_directory_then_bare_write_is_flagged() -> None:
521
- assert (
522
- references_verdict_directory(
523
- "cd ~/.claude && cd verification && echo x > 0123456789abcdef.json"
524
- )
525
- is True
526
- )
527
-
528
-
529
- def test_split_cd_into_unrelated_subdirectory_then_write_is_not_flagged() -> None:
530
- assert (
531
- references_verdict_directory(
532
- "cd ~/.claude && cd hooks && echo x > note.txt"
533
- )
534
- is False
535
- )
536
-
537
-
538
- def test_split_cd_into_verdict_directory_then_copy_is_flagged() -> None:
539
- assert (
540
- references_verdict_directory(
541
- "cd ~/.claude && cd verification && cp /tmp/f.json out.json"
542
- )
543
- is True
544
- )
545
-
546
-
547
- def test_split_cd_into_verdict_directory_then_move_is_flagged() -> None:
548
- assert (
549
- references_verdict_directory(
550
- "cd ~/.claude && cd verification && mv /tmp/f.json out.json"
551
- )
552
- is True
553
- )
554
-
555
-
556
- def test_split_cd_into_unrelated_subdirectory_then_copy_is_not_flagged() -> None:
557
- assert (
558
- references_verdict_directory("cd ~/.claude && cd hooks && cp a b") is False
559
- )
560
-
561
-
562
- def test_concatenated_string_literal_verdict_path_write_is_flagged() -> None:
563
- assert (
564
- references_verdict_directory(
565
- "python -c \"open(str(pathlib.Path.home())+'/.claude'+'/verification'"
566
- "+'/a.json','w')\""
567
- )
568
- is True
569
- )
570
-
571
-
572
- def test_concatenated_absolute_verdict_path_write_is_flagged() -> None:
573
- assert (
574
- references_verdict_directory(
575
- "python -c \"open('/home/u/.claude'+'/verification/a.json','w')\""
576
- )
577
- is True
578
- )
579
-
580
-
581
- def test_shell_variable_home_then_verdict_path_write_is_flagged() -> None:
582
- assert (
583
- references_verdict_directory(
584
- "p=~/.claude; echo x > $p/verification/a.json"
585
- )
586
- is True
587
- )
588
-
589
-
590
- def test_shell_variable_verdict_directory_name_write_is_flagged() -> None:
591
- assert (
592
- references_verdict_directory(
593
- "VDIR=verification; cd ~/.claude && echo x > $VDIR/a.json"
594
- )
595
- is True
596
- )
597
-
598
-
599
- def test_write_without_obfuscation_primitive_is_not_flagged() -> None:
600
- assert (
601
- references_verdict_directory("echo hello > /tmp/notes.txt")
602
- is False
603
- )
604
-
605
-
606
- def test_unrelated_claude_path_with_no_obfuscation_stays_unflagged() -> None:
607
- assert (
608
- references_verdict_directory(
609
- "python $HOME/.claude/hooks/blocking/verified_commit_gate.py"
610
- )
611
- is False
612
- )
613
-
614
-
615
- def test_cd_outside_claude_then_relative_write_is_not_flagged() -> None:
616
- assert (
617
- references_verdict_directory("cd /tmp/work && echo x > verification/a.json")
618
- is False
619
- )
620
-
621
-
622
- def test_sibling_verification_directory_is_not_flagged() -> None:
623
- assert (
624
- references_verdict_directory("cat ~/.claude/verification-docs/readme.md")
625
- is False
626
- )
627
-
628
-
629
- def test_commit_message_naming_verdict_path_is_not_flagged() -> None:
630
- assert (
631
- references_verdict_directory('git commit -m "fix .claude/verification gate"')
632
- is False
633
- )
634
-
635
-
636
- def test_verdict_write_with_path_separator_stays_flagged() -> None:
637
- assert (
638
- references_verdict_directory("echo x > ~/.claude/verification/a.json")
639
- is True
640
- )
641
-
642
-
643
- def test_benign_redirect_mentioning_both_words_in_message_is_not_flagged() -> None:
644
- assert (
645
- references_verdict_directory(
646
- 'echo "note: updated .claude docs about the verification flow"'
647
- " > /tmp/notes.txt"
648
- )
649
- is False
650
- )
651
-
652
-
653
- def test_benign_single_quoted_message_mentioning_both_words_is_not_flagged() -> None:
654
- assert (
655
- references_verdict_directory(
656
- "echo 'see .claude and verification notes' > /tmp/out.txt"
657
- )
658
- is False
659
- )
660
-
661
-
662
- def test_commit_message_naming_verdict_path_with_redirect_is_not_flagged() -> None:
663
- assert (
664
- references_verdict_directory(
665
- 'git commit -m "fix .claude/verification gate" > /tmp/commit.log'
666
- )
667
- is False
668
- )
669
-
670
-
671
- def test_decision_for_bash_verdict_write_denies() -> None:
672
- payload = {
673
- "tool_name": "Bash",
674
- "tool_input": {"command": "echo x > ~/.claude/verification/a.json"},
675
- }
676
- decision = decision_for_payload(payload)
677
- assert decision is not None
678
- assert decision["hookSpecificOutput"]["permissionDecision"] == "deny"
679
-
680
-
681
- def test_decision_for_powershell_verdict_write_denies() -> None:
682
- payload = {
683
- "tool_name": "PowerShell",
684
- "tool_input": {
685
- "command": "'x' | Set-Content $HOME/.claude/verification/a.json"
686
- },
687
- }
688
- decision = decision_for_payload(payload)
689
- assert decision is not None
690
- assert decision["hookSpecificOutput"]["permissionDecision"] == "deny"
691
-
692
-
693
- def test_decision_for_unrelated_bash_command_is_none() -> None:
694
- payload = {"tool_name": "Bash", "tool_input": {"command": "git status"}}
695
- assert decision_for_payload(payload) is None
696
-
697
-
698
- def test_decision_for_non_shell_tool_is_none() -> None:
699
- payload = {
700
- "tool_name": "Read",
701
- "tool_input": {"command": "echo x > ~/.claude/verification/a.json"},
702
- }
703
- assert decision_for_payload(payload) is None
704
-
705
-
706
- def _hooks_manifest_path() -> pathlib.Path:
707
- return _HOOK_DIR.parent / "hooks.json"
708
-
709
-
710
- def _pretooluse_commands_for_matcher(matcher_substring: str) -> list[str]:
711
- manifest_record = json.loads(_hooks_manifest_path().read_text(encoding="utf-8"))
712
- matching_commands: list[str] = []
713
- for each_group in manifest_record["hooks"]["PreToolUse"]:
714
- if matcher_substring not in each_group.get("matcher", ""):
715
- continue
716
- for each_hook in each_group.get("hooks", []):
717
- matching_commands.append(each_hook.get("command", ""))
718
- return matching_commands
719
-
720
-
721
- _VERDICT_GUARD_SCRIPT_RELATIVE_PATH = "blocking/verdict_directory_write_blocker.py"
722
- _BASH_DISPATCHER_BASENAME = "bash_pre_tool_use_dispatcher.py"
723
-
724
-
725
- def _verdict_guard_hosted_entries() -> list:
726
- return [
727
- each_entry
728
- for each_entry in ALL_BASH_HOSTED_HOOK_ENTRIES
729
- if each_entry.script_relative_path == _VERDICT_GUARD_SCRIPT_RELATIVE_PATH
730
- ]
731
-
732
-
733
- def _assert_bash_dispatcher_registered_for_matcher(matcher_substring: str) -> None:
734
- matching_commands = _pretooluse_commands_for_matcher(matcher_substring)
735
- assert any(
736
- _BASH_DISPATCHER_BASENAME in each_command
737
- for each_command in matching_commands
738
- ), (
739
- f"bash_pre_tool_use_dispatcher.py must be registered on matcher containing "
740
- f"{matcher_substring!r}; commands={matching_commands!r}"
741
- )
742
-
743
-
744
- def test_guard_is_not_registered_on_bash() -> None:
745
- _assert_bash_dispatcher_registered_for_matcher("Bash")
746
- matching_entries = _verdict_guard_hosted_entries()
747
- assert matching_entries == []
748
-
749
-
750
- def test_guard_is_not_registered_on_powershell() -> None:
751
- _assert_bash_dispatcher_registered_for_matcher("PowerShell")
752
- matching_entries = _verdict_guard_hosted_entries()
753
- assert matching_entries == []
754
-
755
-
756
- def test_hook_subprocess_imports_real_config_when_parent_holds_shadowing_config(
757
- tmp_path: pathlib.Path,
758
- ) -> None:
759
- real_blocking_directory = pathlib.Path(__file__).resolve().parent
760
- real_hooks_directory = real_blocking_directory.parent
761
-
762
- staged_hooks_directory = tmp_path / "hooks"
763
- staged_blocking_directory = staged_hooks_directory / "blocking"
764
- staged_blocking_directory.mkdir(parents=True)
765
-
766
- shutil.copy(
767
- real_blocking_directory / "verdict_directory_write_blocker.py",
768
- staged_blocking_directory / "verdict_directory_write_blocker.py",
769
- )
770
- shutil.copy(
771
- real_blocking_directory / "verified_commit_config_bootstrap.py",
772
- staged_blocking_directory / "verified_commit_config_bootstrap.py",
773
- )
774
- shutil.copytree(
775
- real_blocking_directory / "config",
776
- staged_blocking_directory / "config",
777
- )
778
- shutil.copytree(
779
- real_hooks_directory / "hooks_constants",
780
- staged_hooks_directory / "hooks_constants",
781
- )
782
-
783
- shadowing_config_directory = staged_hooks_directory / "config"
784
- shadowing_config_directory.mkdir(parents=True, exist_ok=True)
785
- (shadowing_config_directory / "__init__.py").write_text("", encoding="utf-8")
786
- (shadowing_config_directory / "unrelated_constants.py").write_text(
787
- "UNRELATED_VALUE = 1\n", encoding="utf-8"
788
- )
789
-
790
- benign_payload = json.dumps(
791
- {"tool_name": "Bash", "tool_input": {"command": "echo hello"}}
792
- )
793
- completed = subprocess.run(
794
- [
795
- sys.executable,
796
- str(staged_blocking_directory / "verdict_directory_write_blocker.py"),
797
- ],
798
- input=benign_payload,
799
- capture_output=True,
800
- text=True,
801
- )
802
-
803
- assert "ModuleNotFoundError" not in completed.stderr
804
- assert completed.returncode == 0