claude-dev-env 2.4.0 → 2.7.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 (309) hide show
  1. package/CLAUDE.md +26 -59
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +6 -3
  4. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +12 -2
  5. package/_shared/pr-loop/scripts/code_rules_gate_parts/baseline_import_isolation.py +309 -0
  6. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +540 -0
  7. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +206 -70
  8. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/__init__.py +1 -0
  9. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/_repo_test_helpers.py +76 -0
  10. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_baseline_import_isolation.py +248 -0
  11. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_regression.py +309 -0
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +91 -58
  13. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
  14. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
  15. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +202 -0
  16. package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
  17. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
  18. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
  19. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
  20. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
  21. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
  22. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
  23. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
  24. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
  25. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
  26. package/_shared/pr-loop/worker-spawn.md +1 -1
  27. package/agents/CLAUDE.md +3 -2
  28. package/agents/caveman.md +0 -1
  29. package/agents/clasp-deployment-orchestrator.md +0 -1
  30. package/agents/clean-coder.md +0 -1
  31. package/agents/code-advisor.md +0 -1
  32. package/agents/code-quality-agent.md +1 -2
  33. package/agents/code-verifier.md +36 -8
  34. package/agents/deep-research.md +0 -1
  35. package/agents/docs-agent.md +0 -1
  36. package/agents/git-commit-crafter.md +0 -1
  37. package/agents/issue-tracker.md +42 -0
  38. package/agents/plan-packet-validator.md +0 -1
  39. package/agents/pr-description-writer.md +0 -1
  40. package/agents/test_agent_frontmatter.py +67 -18
  41. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
  42. package/bin/CLAUDE.md +68 -5
  43. package/bin/codex-compat.mjs +104 -0
  44. package/bin/codex-compat.test.mjs +51 -0
  45. package/bin/ever-shipped-skills.mjs +1 -0
  46. package/bin/install-constants.mjs +88 -0
  47. package/bin/install.mjs +1138 -114
  48. package/bin/install.prune.test.mjs +869 -19
  49. package/bin/install.test.mjs +906 -2
  50. package/codex-capability-map.json +13 -0
  51. package/commands/implement.md +1 -1
  52. package/commands/right-size.md +1 -1
  53. package/docs/CLAUDE.md +1 -0
  54. package/docs/CODE_RULES.md +2 -0
  55. package/docs/codex-compatibility.md +25 -0
  56. package/docs/host-pool-health-monitor.md +102 -0
  57. package/docs/nas-ssh-invocation.md +96 -12
  58. package/docs/references/CLAUDE.md +4 -2
  59. package/docs/references/advisor-tool.md +13 -0
  60. package/docs/references/code-review-enforcement.md +35 -0
  61. package/docs/references/team-advisor-skill.md +14 -0
  62. package/hooks/blocking/CLAUDE.md +4 -0
  63. package/hooks/blocking/code_review_pr_create_gate.py +7 -3
  64. package/hooks/blocking/code_review_push_gate.py +9 -4
  65. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
  66. package/hooks/blocking/config/__init__.py +5 -5
  67. package/hooks/blocking/config/code_review_enforcement_constants.py +40 -7
  68. package/hooks/blocking/config/test_code_review_enforcement_constants.py +58 -0
  69. package/hooks/blocking/config/verified_commit_constants.py +160 -159
  70. package/hooks/blocking/eli11_reply_enforcer.py +479 -0
  71. package/hooks/blocking/gh_body_arg_blocker.py +1 -1
  72. package/hooks/blocking/nas_ssh_binary_enforcer.py +8 -46
  73. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  74. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  75. package/hooks/blocking/shell_substitution_blocker.py +129 -0
  76. package/hooks/blocking/state_description_blocker.py +1 -1
  77. package/hooks/blocking/stop_dispatcher.py +1 -1
  78. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +2 -3
  79. package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
  80. package/hooks/blocking/test_code_review_push_gate.py +16 -0
  81. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
  82. package/hooks/blocking/test_eli11_reply_enforcer.py +457 -0
  83. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  84. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
  85. package/hooks/blocking/test_shell_substitution_blocker.py +124 -0
  86. package/hooks/blocking/test_stop_dispatcher.py +23 -0
  87. package/hooks/blocking/test_unscoped_search_blocker.py +102 -0
  88. package/hooks/blocking/test_verdict_directory_write_blocker.py +804 -808
  89. package/hooks/blocking/test_verification_verdict_store.py +54 -0
  90. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  91. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  92. package/hooks/blocking/unscoped_search_blocker.py +391 -0
  93. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  94. package/hooks/blocking/verification_verdict_store.py +1039 -1036
  95. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  96. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  97. package/hooks/git-hooks/CLAUDE.md +3 -0
  98. package/hooks/git-hooks/conftest.py +30 -0
  99. package/hooks/git-hooks/gate_utils.py +2 -2
  100. package/hooks/git-hooks/git_hooks_constants/__init__.py +41 -2
  101. package/hooks/git-hooks/pre_push.py +75 -4
  102. package/hooks/git-hooks/pre_push_base_reference.py +166 -0
  103. package/hooks/git-hooks/test_config.py +0 -15
  104. package/hooks/git-hooks/test_gate_utils.py +3 -15
  105. package/hooks/git-hooks/test_pre_commit.py +1 -15
  106. package/hooks/git-hooks/test_pre_push.py +257 -23
  107. package/hooks/git-hooks/test_pre_push_base_reference.py +339 -0
  108. package/hooks/hooks.json +10 -12
  109. package/hooks/hooks_constants/CLAUDE.md +7 -2
  110. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +4 -4
  111. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +101 -0
  112. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  113. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +2 -8
  114. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  115. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  116. package/hooks/hooks_constants/shell_command_segments.py +82 -0
  117. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +67 -0
  118. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -0
  119. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +5 -6
  120. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +1 -0
  121. package/hooks/hooks_constants/unscoped_search_blocker_constants.py +153 -0
  122. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  123. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  124. package/hooks/pyproject.toml +1 -0
  125. package/hooks/validators/CLAUDE.md +1 -0
  126. package/hooks/validators/config/__init__.py +0 -0
  127. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  128. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  129. package/hooks/validators/conftest.py +4 -0
  130. package/hooks/validators/ruff_integration.py +49 -5
  131. package/hooks/validators/run_all_validators.py +206 -9
  132. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  133. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  134. package/hooks/validators/test_ruff_integration.py +92 -1
  135. package/hooks/validators/test_run_all_validators.py +115 -68
  136. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  137. package/package.json +13 -3
  138. package/rules/CLAUDE.md +17 -22
  139. package/rules/agent-spawn-protocol.md +6 -6
  140. package/rules/anti-corollary-tests.md +1 -1
  141. package/rules/bdd.md +1 -1
  142. package/rules/cleanup-temp-files.md +10 -4
  143. package/rules/code-standards.md +7 -0
  144. package/rules/conservative-action.md +1 -5
  145. package/rules/context7.md +0 -4
  146. package/rules/destructive-commands.md +47 -0
  147. package/rules/doc-inventory-integrity.md +48 -0
  148. package/rules/doc-prose-cuts.md +58 -0
  149. package/rules/docstring-prose-matches-implementation.md +53 -44
  150. package/rules/durable-post-artifacts.md +0 -4
  151. package/rules/eli11-replies.md +31 -0
  152. package/rules/explore-thoroughly.md +4 -4
  153. package/rules/falsify-before-green.md +68 -0
  154. package/rules/file-global-constants.md +1 -1
  155. package/rules/filesystem-search.md +51 -0
  156. package/rules/gh-cli-conventions.md +27 -0
  157. package/rules/git-workflow.md +26 -0
  158. package/rules/hedging-claims.md +9 -0
  159. package/rules/long-horizon-autonomy.md +0 -4
  160. package/rules/measurement-denominators.md +48 -0
  161. package/rules/nas-ssh-invocation.md +23 -5
  162. package/rules/parallel-tools.md +2 -2
  163. package/rules/plain-illustrative-docstrings.md +3 -7
  164. package/rules/plain-language.md +2 -0
  165. package/rules/proof-of-work-pr-comments.md +0 -4
  166. package/rules/re-stage-before-commit.md +2 -0
  167. package/rules/research-mode.md +10 -0
  168. package/rules/shell-invocation.md +21 -0
  169. package/rules/testing.md +4 -0
  170. package/rules/verified-commit-gate-skip.md +3 -27
  171. package/rules/verify-before-asking.md +5 -0
  172. package/rules/windows-filesystem-safe.md +1 -1
  173. package/rules/workers-done-before-complete.md +4 -0
  174. package/scripts/CLAUDE.md +1 -0
  175. package/scripts/Capture-PoolHealth.ps1 +410 -0
  176. package/scripts/Migrate-ShellPolicy.ps1 +1 -1
  177. package/scripts/_code_review_test_support.py +404 -0
  178. package/scripts/claude_chain_runner.py +141 -1
  179. package/scripts/codex_capability_bridge.py +171 -0
  180. package/scripts/codex_compat_materializer.py +1087 -0
  181. package/scripts/codex_compat_watcher.py +502 -0
  182. package/scripts/conftest.py +16 -1
  183. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  184. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
  185. package/scripts/dev_env_scripts_constants/code_review_constants.py +37 -0
  186. package/scripts/invoke_code_review.py +11 -4
  187. package/scripts/resolve_worker_spawn.py +626 -626
  188. package/scripts/spawn_grok_batch.py +672 -672
  189. package/scripts/sync_to_cursor/rules.py +0 -10
  190. package/scripts/test_claude_chain_runner.py +131 -0
  191. package/scripts/test_invoke_code_review.py +85 -908
  192. package/scripts/test_invoke_code_review_chain.py +70 -0
  193. package/scripts/test_invoke_code_review_cli.py +192 -0
  194. package/scripts/test_invoke_code_review_contract.py +256 -0
  195. package/scripts/test_invoke_code_review_git.py +123 -0
  196. package/scripts/test_invoke_code_review_mode.py +99 -0
  197. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  198. package/scripts/tests/test_code_review_constants.py +80 -0
  199. package/scripts/tests/test_codex_capability_bridge.py +91 -0
  200. package/scripts/tests/test_codex_compat_materializer.py +632 -0
  201. package/scripts/tests/test_codex_compat_watcher.py +599 -0
  202. package/scripts/tests/test_sync_to_cursor.py +0 -1
  203. package/skills/CLAUDE.md +2 -0
  204. package/skills/auditing-claude-config/SKILL.md +114 -114
  205. package/skills/autoconverge/SKILL.md +427 -427
  206. package/skills/autoconverge/reference/convergence.md +24 -3
  207. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  208. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
  209. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
  210. package/skills/autoconverge/workflow/converge.mjs +168 -1
  211. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  212. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  213. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  214. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  215. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  216. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  217. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  218. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  219. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  220. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  221. package/skills/bugteam/reference/copilot-gap-analysis.md +1 -1
  222. package/skills/closeout/SKILL.md +33 -50
  223. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  224. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  225. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  226. package/skills/condensing-instructions/SKILL.md +72 -0
  227. package/skills/copilot-review/SKILL.md +119 -119
  228. package/skills/e-code-review/SKILL.md +52 -0
  229. package/skills/e-code-review/reference/fix.md +54 -0
  230. package/skills/e-code-review/reference/loop.md +43 -0
  231. package/skills/e-code-review/reference/low.md +57 -0
  232. package/skills/e-code-review/reference/medium.md +153 -0
  233. package/skills/e-code-review/reference/xhigh.md +182 -0
  234. package/skills/e-simplify/SKILL.md +97 -0
  235. package/skills/fresh-branch/CLAUDE.md +1 -1
  236. package/skills/fresh-branch/SKILL.md +5 -6
  237. package/skills/fresh-branch/scripts/create_fresh_branch.py +42 -24
  238. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -3
  239. package/skills/fresh-branch/scripts/test_create_fresh_branch.py +30 -126
  240. package/skills/issue-tracker/SKILL.md +92 -0
  241. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  242. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  243. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  244. package/skills/orchestrator/SKILL.md +177 -22
  245. package/skills/orchestrator/scripts/status_gate.py +625 -0
  246. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  247. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  248. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  249. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  250. package/skills/orchestrator-refresh/SKILL.md +129 -35
  251. package/skills/plan-to-pr/SKILL.md +155 -0
  252. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  253. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  254. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  255. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  256. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  257. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  258. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  259. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  260. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  261. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  262. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  263. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  264. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  265. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  266. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  267. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  268. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  269. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  270. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  271. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  272. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  273. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  274. package/skills/pr-converge/SKILL.md +472 -469
  275. package/skills/pr-converge/reference/examples.md +3 -3
  276. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  277. package/skills/pr-converge/reference/ground-rules.md +7 -4
  278. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  279. package/skills/pr-converge/reference/per-tick.md +5 -5
  280. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  281. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  282. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  283. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  284. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  285. package/skills/pr-fix-protocol/SKILL.md +1 -1
  286. package/skills/privacy-hygiene/SKILL.md +68 -68
  287. package/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
  288. package/skills/prototype/workflows/promotion.md +1 -1
  289. package/skills/release-notes-html/SKILL.md +164 -0
  290. package/skills/session-log/SKILL.md +1 -1
  291. package/skills/task-build/CLAUDE.md +8 -7
  292. package/skills/task-build/SKILL.md +16 -8
  293. package/skills/task-build/reference/tool-routing.md +19 -0
  294. package/rules/claude-md-orphan-file.md +0 -28
  295. package/rules/cleanup-command-forms.md +0 -23
  296. package/rules/code-reviews.md +0 -11
  297. package/rules/env-var-table-code-drift.md +0 -10
  298. package/rules/gh-body-file.md +0 -5
  299. package/rules/gh-paginate.md +0 -3
  300. package/rules/hook-prose-matches-detector.md +0 -15
  301. package/rules/no-historical-clutter.md +0 -26
  302. package/rules/no-inline-destructive-literals.md +0 -9
  303. package/rules/no-justification-noise.md +0 -61
  304. package/rules/package-inventory-stale-entry.md +0 -25
  305. package/rules/right-sized-engineering.md +0 -28
  306. package/rules/self-contained-docs.md +0 -17
  307. package/rules/shell-invocation-policy.md +0 -5
  308. package/rules/tdd.md +0 -7
  309. package/skills/closeout/reference/issue-body-templates.md +0 -108
@@ -9,6 +9,8 @@ import importlib.util
9
9
  import pathlib
10
10
  import re
11
11
 
12
+ import pytest
13
+
12
14
  _CONFIG_DIR = pathlib.Path(__file__).parent
13
15
 
14
16
  SAMPLE_ROOT_KEY_HEX_LENGTH = 16
@@ -23,6 +25,10 @@ _constants_module = importlib.util.module_from_spec(_constants_spec)
23
25
  _constants_spec.loader.exec_module(_constants_module)
24
26
 
25
27
  effort_meets_threshold = _constants_module.effort_meets_threshold
28
+ CODE_REVIEW_ENFORCEMENT_ENV_VAR = _constants_module.CODE_REVIEW_ENFORCEMENT_ENV_VAR
29
+ code_review_enforcement_enabled_in_environment = (
30
+ _constants_module.code_review_enforcement_enabled_in_environment
31
+ )
26
32
  PUSH_REQUIRED_EFFORT = _constants_module.PUSH_REQUIRED_EFFORT
27
33
  PR_CREATE_REQUIRED_EFFORT = _constants_module.PR_CREATE_REQUIRED_EFFORT
28
34
  GATED_PUSH_SUBCOMMANDS = _constants_module.GATED_PUSH_SUBCOMMANDS
@@ -106,3 +112,55 @@ def test_push_gate_message_names_the_push_action_and_low_effort() -> None:
106
112
 
107
113
  def test_guard_message_directs_users_to_the_sanctioned_minter_flag() -> None:
108
114
  assert SANCTIONED_STAMP_MINTER_FLAG in STAMP_DIRECTORY_GUARD_MESSAGE
115
+
116
+
117
+ def test_enforcement_defaults_to_off(monkeypatch: pytest.MonkeyPatch) -> None:
118
+ monkeypatch.delenv(CODE_REVIEW_ENFORCEMENT_ENV_VAR, raising=False)
119
+ assert code_review_enforcement_enabled_in_environment() is False
120
+
121
+
122
+ @pytest.mark.parametrize("enabling_value", ["1", "true", "TRUE", " yes ", "on"])
123
+ def test_enforcement_turns_on_for_each_enabling_environment_value(
124
+ monkeypatch: pytest.MonkeyPatch, enabling_value: str
125
+ ) -> None:
126
+ monkeypatch.setenv(CODE_REVIEW_ENFORCEMENT_ENV_VAR, enabling_value)
127
+ assert code_review_enforcement_enabled_in_environment() is True
128
+
129
+
130
+ @pytest.mark.parametrize("non_enabling_value", ["0", "false", "off", "", "maybe"])
131
+ def test_enforcement_stays_off_for_a_non_enabling_environment_value(
132
+ monkeypatch: pytest.MonkeyPatch, non_enabling_value: str
133
+ ) -> None:
134
+ monkeypatch.setenv(CODE_REVIEW_ENFORCEMENT_ENV_VAR, non_enabling_value)
135
+ assert code_review_enforcement_enabled_in_environment() is False
136
+
137
+
138
+ def _module_flag_after_a_fresh_import(
139
+ monkeypatch: pytest.MonkeyPatch, environment_value: str | None
140
+ ) -> bool:
141
+ if environment_value is None:
142
+ monkeypatch.delenv(CODE_REVIEW_ENFORCEMENT_ENV_VAR, raising=False)
143
+ else:
144
+ monkeypatch.setenv(CODE_REVIEW_ENFORCEMENT_ENV_VAR, environment_value)
145
+ fresh_spec = importlib.util.spec_from_file_location(
146
+ "code_review_enforcement_constants_fresh_import",
147
+ _CONFIG_DIR / "code_review_enforcement_constants.py",
148
+ )
149
+ assert fresh_spec is not None
150
+ assert fresh_spec.loader is not None
151
+ fresh_module = importlib.util.module_from_spec(fresh_spec)
152
+ fresh_spec.loader.exec_module(fresh_module)
153
+ flag_value: bool = fresh_module.CODE_REVIEW_ENFORCEMENT_ENABLED
154
+ return flag_value
155
+
156
+
157
+ def test_module_flag_turns_on_when_the_environment_asks_for_enforcement(
158
+ monkeypatch: pytest.MonkeyPatch,
159
+ ) -> None:
160
+ assert _module_flag_after_a_fresh_import(monkeypatch, "true") is True
161
+
162
+
163
+ def test_module_flag_stays_off_when_the_environment_is_unset(
164
+ monkeypatch: pytest.MonkeyPatch,
165
+ ) -> None:
166
+ assert _module_flag_after_a_fresh_import(monkeypatch, None) is False
@@ -1,159 +1,160 @@
1
- """Constants for the verified-commit gate hook family.
2
-
3
- Shared by ``verification_verdict_store.py``, ``verified_commit_gate.py``,
4
- and ``verifier_verdict_minter.py`` so every tunable lives in one place.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- GIT_TIMEOUT_SECONDS = 30
10
- ROOT_KEY_HEX_LENGTH = 16
11
- VERDICT_JSON_INDENT = 2
12
- CLAUDE_HOME_DIRECTORY_NAME = ".claude"
13
- VERDICT_DIRECTORY_NAME = "verification"
14
- VERDICT_DIRECTORY_NAME_SEPARATOR_PATTERN = r"['\"\\/,\s]+"
15
- VERDICT_DIRECTORY_PATH_BOUNDARY_PATTERN = r"(?=['\"]*[\\/,])"
16
- RELATIVE_VERDICT_DIRECTORY_PATTERN = r"(?:^|(?<=[\s;&|(='\"]))verification[\\/]"
17
- VERDICT_PATH_GLUE_PATTERN = r"['\"+\\/\s]*[\\/]['\"+\\/\s]*"
18
- VERDICT_DIRECTORY_CHANGE_TARGET_PATTERN = r"[ \t]+['\"]?verification[\\/]?['\"]?(?=[\s;&|]|$)"
19
- VERDICT_PATH_JOINED_VARIABLE_PATTERN = r"\$\{?(\w+)\}?[\\/]|[\\/]\$\{?(\w+)\}?"
20
- VERDICT_PATH_VARIABLE_ASSIGNMENT_PATTERN = r"(?:^|(?<=[\s;&|(]))%s=(\S+)"
21
- VERDICT_FILE_RELATIVE_REFERENCE_PATTERN = (
22
- rf"(?:^|(?<=[\s;&|(='\"\\/]))verification[\\/][0-9a-f]{{{ROOT_KEY_HEX_LENGTH}}}\.json"
23
- )
24
- PATH_OBFUSCATION_PRIMITIVE_PATTERN = (
25
- r"chr\s*\(|bytes\.fromhex\s*\(|b64decode\s*\(|codecs\.decode\s*\("
26
- r"|(?:bytes|bytearray)\s*\(\s*\[|\[char\[?\]?\]"
27
- )
28
- ALL_VERDICT_PATH_SEGMENT_NAMES = (".claude", "verification")
29
- ALL_VERDICT_PATH_SEGMENT_BODIES = ("claude", "verification")
30
- HEX_TOKEN_PATTERN = r"(?<![0-9a-fx])([0-9a-f]{6,})(?![0-9a-f])"
31
- BASE64_TOKEN_PATTERN = r"[A-Za-z0-9+/]{8,}={0,2}"
32
- CHARACTER_CODE_SEQUENCE_PATTERN = r"\d{1,3}(?:\s*,\s*\d{1,3})+"
33
- CHR_CALL_CHAIN_PATTERN = r"chr\(\s*\d{1,3}\s*\)(?:\s*\+\s*chr\(\s*\d{1,3}\s*\))+"
34
- CHR_CALL_CODE_PATTERN = r"chr\(\s*(\d{1,3})\s*\)"
35
- HEX_DIGITS_PER_BYTE = 2
36
- FILE_WRITE_PRIMITIVE_PATTERN = (
37
- r"\bopen\s*\(|\.write_text\s*\(|\.write_bytes\s*\("
38
- r"|Out-File|Set-Content|Add-Content|\btee\b|>"
39
- )
40
- NON_REDIRECT_FILE_WRITE_PRIMITIVE_PATTERN = (
41
- r"\bopen\s*\(|\.write_text\s*\(|\.write_bytes\s*\("
42
- r"|Out-File|Set-Content|Add-Content|\btee\b"
43
- )
44
- WRITE_CALL_REGION_PATTERN = (
45
- r"(?:\bopen\s*\(|\.write_text\s*\(|\.write_bytes\s*\("
46
- r"|Out-File|Set-Content|Add-Content|\btee\b)[^;&|\n]*"
47
- )
48
- VERDICT_KEY_ALL_PASS = "all_pass"
49
- VERDICT_KEY_MANIFEST_SHA256 = "manifest_sha256"
50
- VERDICT_KEY_FINDINGS = "findings"
51
- VERDICT_FINDING_CHECK_KEY = "check"
52
- LOGS_DIRECTORY_NAME = "logs"
53
- VERIFIER_VERDICTS_JSONL_FILENAME = "verifier-verdicts.jsonl"
54
- LEDGER_KEY_TIMESTAMP = "timestamp"
55
- LEDGER_KEY_REPO_ROOT = "repo_root"
56
- LEDGER_KEY_BRANCH = "branch"
57
- LEDGER_KEY_MANIFEST_HASH = "manifest_hash"
58
- LEDGER_KEY_ALL_PASS = "all_pass"
59
- LEDGER_KEY_FINDING_COUNT = "finding_count"
60
- LEDGER_KEY_FINDING_CHECK_NAMES = "finding_check_names"
61
- LEDGER_KEY_MINTED_FROM_AGENT_ID = "minted_from_agent_id"
62
- VERDICT_FILE_GLOB = "*.json"
63
- SUBAGENTS_DIRECTORY_NAME = "subagents"
64
- AGENT_TRANSCRIPT_GLOB = "agent-*.jsonl"
65
- AGENT_META_SIDECAR_SUFFIX = ".meta.json"
66
- AGENT_META_TYPE_KEY = "agentType"
67
- TRANSCRIPT_ENTRY_TYPE_KEY = "type"
68
- TRANSCRIPT_ASSISTANT_ENTRY_TYPE = "assistant"
69
- TRANSCRIPT_MESSAGE_KEY = "message"
70
- TRANSCRIPT_CONTENT_KEY = "content"
71
- TRANSCRIPT_CONTENT_TYPE_KEY = "type"
72
- TRANSCRIPT_TEXT_CONTENT_TYPE = "text"
73
- TRANSCRIPT_TEXT_KEY = "text"
74
- VERDICT_FENCE_PATTERN = r"```verdict\s*\n(.*?)```"
75
- MANIFEST_HASH_CLI_FLAG = "--manifest-hash"
76
- MANIFEST_HASH_FOR_BRANCH_CLI_FLAG = "--manifest-hash-for-branch"
77
- WORKTREE_LIST_PATH_PREFIX = "worktree "
78
- WORKTREE_LIST_BRANCH_PREFIX = "branch "
79
- BRANCH_REFERENCE_PREFIX = "refs/heads/"
80
- EMPTY_SURFACE_GUARD_MESSAGE = (
81
- "ERROR: The work tree at {repo_root} has no changed or untracked files "
82
- "versus origin/main (empty change surface). This work tree holds nothing "
83
- "to verify — you are pointed at the wrong work tree. Run `git worktree list` "
84
- "to see all checked-out work trees and target the one on the branch under review."
85
- )
86
- BRANCH_WORKTREE_ABSENT_MESSAGE = (
87
- "ERROR: No work tree has branch '{branch}' checked out. "
88
- "Check it out in a work tree first: "
89
- "`git worktree add <path> <branch>` or `git checkout <branch>`."
90
- )
91
- DOCS_ONLY_EXTENSIONS = frozenset(
92
- {".md", ".txt", ".rst", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".ico"}
93
- )
94
- PYTHON_EXTENSION = ".py"
95
- TEST_FILE_PREFIX = "test_"
96
- TEST_FILE_SUFFIX = "_test.py"
97
- CONFTEST_FILE_NAME = "conftest.py"
98
- MINIMUM_STATUS_FIELD_COUNT = 2
99
- ALL_FALLBACK_BASE_REFERENCES = ("origin/main", "origin/master")
100
- DETACHED_HEAD_LABEL = "HEAD"
101
- BRANCH_REMOTE_CONFIG_KEY_TEMPLATE = "branch.{branch_name}.remote"
102
- ALL_TOOLING_STATE_PREFIXES = (
103
- ".claude/verification/",
104
- ".claude/worktrees/",
105
- ".claude/daemon/",
106
- ".claude/teams/",
107
- ".claude/sessions/",
108
- ".cursor/worktrees/",
109
- )
110
- GATED_GIT_SUBCOMMANDS = frozenset({"commit", "push"})
111
- ALL_GIT_BINARY_NAMES = frozenset({"git", "git.exe"})
112
- VALUE_TAKING_GIT_OPTIONS = frozenset({"-C", "-c", "--git-dir", "--work-tree", "--namespace"})
113
- REPO_DIRECTORY_OPTION = "-C"
114
- WORK_TREE_OPTION = "--work-tree"
115
- DIRECTORY_CHANGE_VERBS = frozenset({"cd", "pushd", "set-location", "sl"})
116
- DIRECTORY_CHANGE_PATH_OPTIONS = frozenset({"-path", "-literalpath"})
117
- DIRECTORY_CHANGE_OPTION_TERMINATOR = "--"
118
- DIRECTORY_CHANGE_PATTERN_PREFIX = r"(?:^|(?<=[\s;&|(]))(?:"
119
- DIRECTORY_CHANGE_PATTERN_SUFFIX = r")(?=\s|$)"
120
- DIRECTORY_CHANGE_OPTION_PREFIX_PATTERN = r"(?:[ \t]+(?:%s)(?=\s|$))*"
121
- DIRECTORY_CHANGE_TARGET_PATTERN = r"[ \t]+['\"]?\S*"
122
- CLAUDE_HOME_TARGET_BOUNDARY_PATTERN = r"[\\/]?['\"]?(?=[\s;&|]|$)"
123
- VERDICT_DIRECTORY_TARGET_BOUNDARY_PATTERN = r"[\\/]?['\"]?(?=[\s;&|]|$)"
124
- COMMAND_AFTER_DIRECTORY_CHANGE_PATTERN = r"[;&|\n][\s]*\S"
125
- OPTION_WITH_VALUE_STEP = 2
126
- ALL_GATED_TOOL_NAMES = ("Bash", "PowerShell")
127
- HASH_PREVIEW_LENGTH = 16
128
- VERIFICATION_BYPASS_MARKER = "# verify-skip"
129
- MINTING_AGENT_TYPE = "code-verifier"
130
- VERDICT_DIRECTORY_GUARD_MESSAGE = (
131
- "BLOCKED: [VERDICT_DIRECTORY_GUARD] Shell access to the verification "
132
- "verdict directory (~/.claude/verification/) is denied. Only the "
133
- "verifier_verdict_minter.py SubagentStop hook writes verdict files; a "
134
- "shell write here would forge a passing verdict and defeat the "
135
- "verified-commit gate. Spawn the code-verifier agent to earn a verdict "
136
- "instead of writing one."
137
- )
138
- CORRECTIVE_MESSAGE = (
139
- "BLOCKED: [VERIFIED_COMMIT_GATE] This branch surface has no passing "
140
- "verification verdict. Clear commit-committability gates first "
141
- "(CODE_RULES on changed lines and merge conflicts vs base — the "
142
- "code_verifier_spawn_preflight_gate enforces these on the spawn). If a "
143
- "prior commit failed on CODE_RULES or staging, fix those before "
144
- "re-verifying. Then spawn the code-verifier agent (Agent or Task tool, "
145
- "subagent_type 'code-verifier') with the task texts, the diff scope, "
146
- "and recorded baselines; when it finishes with a clean verdict the "
147
- "SubagentStop hook mints the verdict and this command will pass. Any "
148
- "file change after verification invalidates the verdict, so verify "
149
- "last only after commit gates are green. The verdict binds to the exact "
150
- "surface content (every changed and untracked file's bytes), not to a "
151
- "work-tree path, so a commit from a different work tree passes only "
152
- "when its surface is byte-identical; a work tree carrying any extra or "
153
- "later edit has a surface no verdict covers. To land this change, run "
154
- "this command from the work tree that holds the verified surface, or "
155
- "verify this surface here. Exempt automatically: docs/image files, "
156
- "pytest test files, and "
157
- "Python files whose docstring- and comment-stripped AST is unchanged "
158
- "(comment-only edits in non-Python files are not exempt)."
159
- )
1
+ """Constants for the verified-commit gate hook family.
2
+
3
+ Shared by ``verification_verdict_store.py``, ``verified_commit_gate.py``,
4
+ and ``verifier_verdict_minter.py`` so every tunable lives in one place.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ GIT_TIMEOUT_SECONDS = 30
10
+ ROOT_KEY_HEX_LENGTH = 16
11
+ VERDICT_JSON_INDENT = 2
12
+ CLAUDE_HOME_DIRECTORY_NAME = ".claude"
13
+ VERDICT_DIRECTORY_NAME = "verification"
14
+ VERDICT_DIRECTORY_NAME_SEPARATOR_PATTERN = r"['\"\\/,\s]+"
15
+ VERDICT_DIRECTORY_PATH_BOUNDARY_PATTERN = r"(?=['\"]*[\\/,])"
16
+ RELATIVE_VERDICT_DIRECTORY_PATTERN = r"(?:^|(?<=[\s;&|(='\"]))verification[\\/]"
17
+ VERDICT_PATH_GLUE_PATTERN = r"['\"+\\/\s]*[\\/]['\"+\\/\s]*"
18
+ VERDICT_DIRECTORY_CHANGE_TARGET_PATTERN = r"[ \t]+['\"]?verification[\\/]?['\"]?(?=[\s;&|]|$)"
19
+ VERDICT_PATH_JOINED_VARIABLE_PATTERN = r"\$\{?(\w+)\}?[\\/]|[\\/]\$\{?(\w+)\}?"
20
+ VERDICT_PATH_VARIABLE_ASSIGNMENT_PATTERN = r"(?:^|(?<=[\s;&|(]))%s=(\S+)"
21
+ VERDICT_FILE_RELATIVE_REFERENCE_PATTERN = (
22
+ rf"(?:^|(?<=[\s;&|(='\"\\/]))verification[\\/][0-9a-f]{{{ROOT_KEY_HEX_LENGTH}}}\.json"
23
+ )
24
+ PATH_OBFUSCATION_PRIMITIVE_PATTERN = (
25
+ r"chr\s*\(|bytes\.fromhex\s*\(|b64decode\s*\(|codecs\.decode\s*\("
26
+ r"|(?:bytes|bytearray)\s*\(\s*\[|\[char\[?\]?\]"
27
+ )
28
+ ALL_VERDICT_PATH_SEGMENT_NAMES = (".claude", "verification")
29
+ ALL_VERDICT_PATH_SEGMENT_BODIES = ("claude", "verification")
30
+ HEX_TOKEN_PATTERN = r"(?<![0-9a-fx])([0-9a-f]{6,})(?![0-9a-f])"
31
+ BASE64_TOKEN_PATTERN = r"[A-Za-z0-9+/]{8,}={0,2}"
32
+ CHARACTER_CODE_SEQUENCE_PATTERN = r"\d{1,3}(?:\s*,\s*\d{1,3})+"
33
+ CHR_CALL_CHAIN_PATTERN = r"chr\(\s*\d{1,3}\s*\)(?:\s*\+\s*chr\(\s*\d{1,3}\s*\))+"
34
+ CHR_CALL_CODE_PATTERN = r"chr\(\s*(\d{1,3})\s*\)"
35
+ HEX_DIGITS_PER_BYTE = 2
36
+ FILE_WRITE_PRIMITIVE_PATTERN = (
37
+ r"\bopen\s*\(|\.write_text\s*\(|\.write_bytes\s*\("
38
+ r"|Out-File|Set-Content|Add-Content|\btee\b|>"
39
+ )
40
+ NON_REDIRECT_FILE_WRITE_PRIMITIVE_PATTERN = (
41
+ r"\bopen\s*\(|\.write_text\s*\(|\.write_bytes\s*\("
42
+ r"|Out-File|Set-Content|Add-Content|\btee\b"
43
+ )
44
+ WRITE_CALL_REGION_PATTERN = (
45
+ r"(?:\bopen\s*\(|\.write_text\s*\(|\.write_bytes\s*\("
46
+ r"|Out-File|Set-Content|Add-Content|\btee\b)[^;&|\n]*"
47
+ )
48
+ VERDICT_KEY_ALL_PASS = "all_pass"
49
+ VERDICT_KEY_MANIFEST_SHA256 = "manifest_sha256"
50
+ VERDICT_KEY_FINDINGS = "findings"
51
+ VERDICT_FINDING_CHECK_KEY = "check"
52
+ LOGS_DIRECTORY_NAME = "logs"
53
+ VERIFIER_VERDICTS_JSONL_FILENAME = "verifier-verdicts.jsonl"
54
+ LEDGER_KEY_TIMESTAMP = "timestamp"
55
+ LEDGER_KEY_REPO_ROOT = "repo_root"
56
+ LEDGER_KEY_BRANCH = "branch"
57
+ LEDGER_KEY_MANIFEST_HASH = "manifest_hash"
58
+ LEDGER_KEY_ALL_PASS = "all_pass"
59
+ LEDGER_KEY_FINDING_COUNT = "finding_count"
60
+ LEDGER_KEY_FINDING_CHECK_NAMES = "finding_check_names"
61
+ LEDGER_KEY_MINTED_FROM_AGENT_ID = "minted_from_agent_id"
62
+ VERDICT_FILE_GLOB = "*.json"
63
+ SUBAGENTS_DIRECTORY_NAME = "subagents"
64
+ AGENT_TRANSCRIPT_GLOB = "agent-*.jsonl"
65
+ AGENT_META_SIDECAR_SUFFIX = ".meta.json"
66
+ AGENT_META_TYPE_KEY = "agentType"
67
+ TRANSCRIPT_ENTRY_TYPE_KEY = "type"
68
+ TRANSCRIPT_ASSISTANT_ENTRY_TYPE = "assistant"
69
+ TRANSCRIPT_MESSAGE_KEY = "message"
70
+ TRANSCRIPT_CONTENT_KEY = "content"
71
+ TRANSCRIPT_CONTENT_TYPE_KEY = "type"
72
+ TRANSCRIPT_TEXT_CONTENT_TYPE = "text"
73
+ TRANSCRIPT_TEXT_KEY = "text"
74
+ VERDICT_FENCE_PATTERN = r"```verdict\s*\n(.*?)```"
75
+ MANIFEST_HASH_CLI_FLAG = "--manifest-hash"
76
+ MANIFEST_HASH_FOR_BRANCH_CLI_FLAG = "--manifest-hash-for-branch"
77
+ WORKTREE_LIST_PATH_PREFIX = "worktree "
78
+ WORKTREE_LIST_BRANCH_PREFIX = "branch "
79
+ BRANCH_REFERENCE_PREFIX = "refs/heads/"
80
+ EMPTY_SURFACE_GUARD_MESSAGE = (
81
+ "ERROR: The work tree at {repo_root} has no changed or untracked files "
82
+ "versus origin/main (empty change surface). This work tree holds nothing "
83
+ "to verify — you are pointed at the wrong work tree. Run `git worktree list` "
84
+ "to see all checked-out work trees and target the one on the branch under review."
85
+ )
86
+ BRANCH_WORKTREE_ABSENT_MESSAGE = (
87
+ "ERROR: No work tree has branch '{branch}' checked out. "
88
+ "Check it out in a work tree first: "
89
+ "`git worktree add <path> <branch>` or `git checkout <branch>`."
90
+ )
91
+ DOCS_ONLY_EXTENSIONS = frozenset(
92
+ {".md", ".txt", ".rst", ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".ico"}
93
+ )
94
+ PYTHON_EXTENSION = ".py"
95
+ TEST_FILE_PREFIX = "test_"
96
+ TEST_FILE_SUFFIX = "_test.py"
97
+ CONFTEST_FILE_NAME = "conftest.py"
98
+ MINIMUM_STATUS_FIELD_COUNT = 2
99
+ ALL_FALLBACK_BASE_REFERENCES = ("origin/main", "origin/master")
100
+ DETACHED_HEAD_LABEL = "HEAD"
101
+ BRANCH_REMOTE_CONFIG_KEY_TEMPLATE = "branch.{branch_name}.remote"
102
+ ALL_TOOLING_STATE_PREFIXES = (
103
+ ".claude/verification/",
104
+ ".claude/worktrees/",
105
+ ".claude/daemon/",
106
+ ".claude/teams/",
107
+ ".claude/sessions/",
108
+ ".cursor/worktrees/",
109
+ ".claude-plugin-data/",
110
+ )
111
+ GATED_GIT_SUBCOMMANDS = frozenset({"commit", "push"})
112
+ ALL_GIT_BINARY_NAMES = frozenset({"git", "git.exe"})
113
+ VALUE_TAKING_GIT_OPTIONS = frozenset({"-C", "-c", "--git-dir", "--work-tree", "--namespace"})
114
+ REPO_DIRECTORY_OPTION = "-C"
115
+ WORK_TREE_OPTION = "--work-tree"
116
+ DIRECTORY_CHANGE_VERBS = frozenset({"cd", "pushd", "set-location", "sl"})
117
+ DIRECTORY_CHANGE_PATH_OPTIONS = frozenset({"-path", "-literalpath"})
118
+ DIRECTORY_CHANGE_OPTION_TERMINATOR = "--"
119
+ DIRECTORY_CHANGE_PATTERN_PREFIX = r"(?:^|(?<=[\s;&|(]))(?:"
120
+ DIRECTORY_CHANGE_PATTERN_SUFFIX = r")(?=\s|$)"
121
+ DIRECTORY_CHANGE_OPTION_PREFIX_PATTERN = r"(?:[ \t]+(?:%s)(?=\s|$))*"
122
+ DIRECTORY_CHANGE_TARGET_PATTERN = r"[ \t]+['\"]?\S*"
123
+ CLAUDE_HOME_TARGET_BOUNDARY_PATTERN = r"[\\/]?['\"]?(?=[\s;&|]|$)"
124
+ VERDICT_DIRECTORY_TARGET_BOUNDARY_PATTERN = r"[\\/]?['\"]?(?=[\s;&|]|$)"
125
+ COMMAND_AFTER_DIRECTORY_CHANGE_PATTERN = r"[;&|\n][\s]*\S"
126
+ OPTION_WITH_VALUE_STEP = 2
127
+ ALL_GATED_TOOL_NAMES = ("Bash", "PowerShell")
128
+ HASH_PREVIEW_LENGTH = 16
129
+ VERIFICATION_BYPASS_MARKER = "# verify-skip"
130
+ MINTING_AGENT_TYPE = "code-verifier"
131
+ VERDICT_DIRECTORY_GUARD_MESSAGE = (
132
+ "BLOCKED: [VERDICT_DIRECTORY_GUARD] Shell access to the verification "
133
+ "verdict directory (~/.claude/verification/) is denied. Only the "
134
+ "verifier_verdict_minter.py SubagentStop hook writes verdict files; a "
135
+ "shell write here would forge a passing verdict and defeat the "
136
+ "verified-commit gate. Spawn the code-verifier agent to earn a verdict "
137
+ "instead of writing one."
138
+ )
139
+ CORRECTIVE_MESSAGE = (
140
+ "BLOCKED: [VERIFIED_COMMIT_GATE] This branch surface has no passing "
141
+ "verification verdict. Clear commit-committability gates first "
142
+ "(CODE_RULES on changed lines and merge conflicts vs base — the "
143
+ "code_verifier_spawn_preflight_gate enforces these on the spawn). If a "
144
+ "prior commit failed on CODE_RULES or staging, fix those before "
145
+ "re-verifying. Then spawn the code-verifier agent (Agent or Task tool, "
146
+ "subagent_type 'code-verifier') with the task texts, the diff scope, "
147
+ "and recorded baselines; when it finishes with a clean verdict the "
148
+ "SubagentStop hook mints the verdict and this command will pass. Any "
149
+ "file change after verification invalidates the verdict, so verify "
150
+ "last only after commit gates are green. The verdict binds to the exact "
151
+ "surface content (every changed and untracked file's bytes), not to a "
152
+ "work-tree path, so a commit from a different work tree passes only "
153
+ "when its surface is byte-identical; a work tree carrying any extra or "
154
+ "later edit has a surface no verdict covers. To land this change, run "
155
+ "this command from the work tree that holds the verified surface, or "
156
+ "verify this surface here. Exempt automatically: docs/image files, "
157
+ "pytest test files, and "
158
+ "Python files whose docstring- and comment-stripped AST is unchanged "
159
+ "(comment-only edits in non-Python files are not exempt)."
160
+ )