claude-dev-env 2.12.0 → 2.13.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 (193) hide show
  1. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  2. package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
  3. package/agents/CLAUDE.md +1 -2
  4. package/agents/pr-description-writer.md +11 -81
  5. package/bin/install.mjs +48 -9
  6. package/bin/install.prune.test.mjs +26 -0
  7. package/bin/install.settings-defaults.test.mjs +60 -33
  8. package/bin/install.test.mjs +2 -4
  9. package/commands/sr-loop.md +9 -5
  10. package/docs/CLAUDE.md +0 -1
  11. package/docs/references/CLAUDE.md +0 -1
  12. package/hooks/CLAUDE.md +1 -1
  13. package/hooks/blocking/CLAUDE.md +2 -27
  14. package/hooks/blocking/config/CLAUDE.md +1 -13
  15. package/hooks/blocking/conventional_pr_title_gate.py +1 -2
  16. package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
  17. package/hooks/blocking/stop_dispatcher.py +4 -4
  18. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
  19. package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
  20. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
  21. package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
  22. package/hooks/diagnostic/CLAUDE.md +3 -3
  23. package/hooks/diagnostic/hook_log_extractor.py +2 -36
  24. package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
  25. package/hooks/diagnostic/test_hook_log_extractor.py +8 -21
  26. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
  27. package/hooks/git-hooks/git_hooks_constants/__init__.py +0 -6
  28. package/hooks/git-hooks/pre_push.py +3 -85
  29. package/hooks/git-hooks/test_pre_push.py +0 -130
  30. package/hooks/hooks.json +0 -35
  31. package/hooks/hooks_constants/CLAUDE.md +0 -9
  32. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
  33. package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
  34. package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
  35. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
  36. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
  37. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  38. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
  39. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
  40. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  41. package/hooks/lifecycle/CLAUDE.md +1 -3
  42. package/package.json +1 -1
  43. package/rules/CLAUDE.md +0 -2
  44. package/rules/gh-cli-conventions.md +0 -1
  45. package/rules/git-workflow.md +1 -3
  46. package/scripts/CLAUDE.md +0 -1
  47. package/scripts/_code_review_test_support.py +0 -95
  48. package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
  49. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
  50. package/scripts/invoke_code_review.py +8 -451
  51. package/scripts/test_invoke_code_review.py +5 -61
  52. package/scripts/test_invoke_code_review_cli.py +1 -45
  53. package/scripts/test_invoke_code_review_contract.py +1 -118
  54. package/settings.json +1 -10
  55. package/skills/CLAUDE.md +8 -0
  56. package/skills/autoconverge/reference/CLAUDE.md +1 -1
  57. package/skills/autoconverge/reference/convergence.md +7 -7
  58. package/skills/autoconverge/reference/gotchas.md +3 -10
  59. package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
  60. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
  61. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
  62. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
  63. package/skills/autoconverge/workflow/converge.mjs +201 -238
  64. package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
  65. package/skills/bugteam/CONSTRAINTS.md +1 -1
  66. package/skills/bugteam/SKILL.md +9 -0
  67. package/skills/bugteam/reference/audit-and-teammates.md +10 -6
  68. package/skills/bugteam/test_skill_additions.py +9 -0
  69. package/skills/comments/SKILL.md +65 -0
  70. package/skills/descriptions/SKILL.md +51 -0
  71. package/skills/emergencies/SKILL.md +42 -0
  72. package/skills/imagegen/SKILL.md +21 -0
  73. package/skills/imagegen/scripts/config/__init__.py +1 -0
  74. package/skills/imagegen/scripts/config/constants.py +28 -0
  75. package/skills/imagegen/scripts/imagegen.py +45 -0
  76. package/skills/imagegen/scripts/imagegen_core.py +528 -0
  77. package/skills/imagegen/scripts/test_imagegen.py +204 -0
  78. package/skills/orchestrator/SKILL.md +2 -4
  79. package/skills/plan-to-pr/SKILL.md +6 -7
  80. package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
  81. package/skills/plan-to-pr/reference/process-inventory.md +2 -2
  82. package/skills/plan-to-pr/reference/review-loop.md +2 -2
  83. package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
  84. package/skills/plan-to-pr/reference/task-seeds.md +1 -1
  85. package/skills/plan-to-pr/reference/task-ticket.md +3 -4
  86. package/skills/plan-to-pr/scripts/config/constants.py +1 -2
  87. package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
  88. package/skills/plan-to-pr/test_skill_contract.py +2 -2
  89. package/skills/pr-converge/SKILL.md +13 -16
  90. package/skills/pr-converge/reference/progress-checklist.md +6 -5
  91. package/skills/pr-converge/reference/state-schema.md +1 -27
  92. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  93. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
  94. package/skills/pr-loop-lifecycle/SKILL.md +1 -1
  95. package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
  96. package/skills/prototype/SKILL.md +4 -4
  97. package/skills/prototype/reference/honest-limitations.md +4 -4
  98. package/skills/prototype/reference/promotion-tasks.md +2 -2
  99. package/skills/prototype/workflows/promotion.md +4 -6
  100. package/skills/reviews/SKILL.md +85 -0
  101. package/skills/small-cl/SKILL.md +52 -0
  102. package/agents/code-verifier.md +0 -74
  103. package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
  104. package/docs/references/code-review-enforcement.md +0 -132
  105. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
  106. package/hooks/blocking/code_review_gate_deny.py +0 -74
  107. package/hooks/blocking/code_review_pr_create_gate.py +0 -198
  108. package/hooks/blocking/code_review_push_gate.py +0 -145
  109. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
  110. package/hooks/blocking/code_review_stamp_store.py +0 -233
  111. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
  112. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
  113. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
  114. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
  115. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
  116. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
  117. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
  118. package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
  119. package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
  120. package/hooks/blocking/config/verified_commit_constants.py +0 -160
  121. package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
  122. package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
  123. package/hooks/blocking/conftest.py +0 -123
  124. package/hooks/blocking/convergence_gate_blocker.py +0 -310
  125. package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
  126. package/hooks/blocking/pr_description_body_audit.py +0 -148
  127. package/hooks/blocking/pr_description_command_parser.py +0 -237
  128. package/hooks/blocking/pr_description_enforcer.py +0 -227
  129. package/hooks/blocking/pr_description_pr_number.py +0 -155
  130. package/hooks/blocking/pr_description_proof_of_work.py +0 -385
  131. package/hooks/blocking/pr_description_readability.py +0 -364
  132. package/hooks/blocking/reviewer_spawn_gate.py +0 -182
  133. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
  134. package/hooks/blocking/test_code_review_gate_deny.py +0 -54
  135. package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
  136. package/hooks/blocking/test_code_review_push_gate.py +0 -205
  137. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
  138. package/hooks/blocking/test_code_review_stamp_store.py +0 -205
  139. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
  140. package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
  141. package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
  142. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
  143. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
  144. package/hooks/blocking/test_pr_description_enforcer.py +0 -221
  145. package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
  146. package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
  147. package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
  148. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
  149. package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
  150. package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
  151. package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
  152. package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
  153. package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
  154. package/hooks/blocking/test_verification_verdict_store.py +0 -974
  155. package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
  156. package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
  157. package/hooks/blocking/test_verified_commit_gate.py +0 -581
  158. package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
  159. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
  160. package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
  161. package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
  162. package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
  163. package/hooks/blocking/verification_verdict_store.py +0 -1039
  164. package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
  165. package/hooks/blocking/verified_commit_gate.py +0 -113
  166. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
  167. package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
  168. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
  169. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
  170. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
  171. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
  172. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
  173. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
  174. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
  175. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
  176. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
  177. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
  178. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
  179. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
  180. package/hooks/blocking/verifier_verdict_minter.py +0 -280
  181. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
  182. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
  183. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
  184. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
  185. package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
  186. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
  187. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
  188. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
  189. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
  190. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
  191. package/rules/proof-of-work-pr-comments.md +0 -22
  192. package/rules/verified-commit-gate-skip.md +0 -4
  193. package/scripts/Show-Asset.ps1 +0 -106
@@ -6,10 +6,7 @@ PreToolUse hooks that deny (block) tool calls when a rule is violated. The main
6
6
 
7
7
  | Directory | Role |
8
8
  |---|---|
9
- | `config/` | Shared constants for the verified-commit gate family (`verified_commit_constants.py`) |
10
9
  | `tdd_enforcer_parts/` | Concern modules the `tdd_enforcer.py` entry hook wires together: path classification, content analysis, candidate-path resolution, freshness, git-tracking restore detection, decisions, and constants |
11
- | `verified_commit_gate_parts/` | Concern modules the `verified_commit_gate.py` entry hook wires together: command tokenization, directory-change resolution, gated git-invocation resolution, deny-reason resolution, and deny-payload assembly |
12
- | `code_review_stamp_write_blocker_parts/` | Concern modules the `code_review_stamp_directory_write_blocker.py` entry hook wires together: the split directory-change-into-stamp matcher and the obfuscated-stamp-path-write matcher |
13
10
  | `claude_md_orphan_file_blocker_parts/` | Concern modules the `claude_md_orphan_file_blocker.py` entry hook wires together: reference extraction, subtree scan, scan plan, decision, and constants |
14
11
  | `package_inventory_stale_blocker_parts/` | Concern modules the `package_inventory_stale_blocker.py` entry hook wires together: inventory detection, decision, and constants |
15
12
  | `inventory_intent_records/` | The shared per-session pending-intent store both inventory blockers read to break the file/row add-order deadlock |
@@ -73,11 +70,6 @@ The check modules it calls are the `code_rules_<concern>.py` files below.
73
70
  | `block_main_commit.py` | PreToolUse (Bash) | `git commit`/`git push` directly to `main` |
74
71
  | `bot_mention_comment_blocker.py` | PreToolUse (Write/Edit) | PR review comments that @-mention a bot |
75
72
  | `claude_md_orphan_file_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | Per-directory `CLAUDE.md` table cells naming a bare filename absent from the directory subtree |
76
- | `code_review_pr_create_gate.py` | PreToolUse (Bash/MCP GitHub) | `gh pr create` or the MCP `create_pull_request` tool without a clean `xhigh` code-review stamp covering the branch surface |
77
- | `code_review_push_gate.py` | PreToolUse (Bash/PowerShell) | `git push` without a clean `low` code-review stamp covering the branch surface |
78
- | `code_review_stamp_directory_write_blocker.py` | PreToolUse (Bash/PowerShell/Write/Edit/MultiEdit) | Shell or file-tool writes into `~/.claude/code-review-stamps/`, and shell references to the stamp store module or its mint call, outside the sanctioned invoker |
79
- | `code_verifier_spawn_preflight_gate.py` | PreToolUse (Agent) | Spawning the `code-verifier` subagent when the branch has a merge conflict vs its base or a CODE_RULES violation on a working-tree-added line, or the CODE_RULES engine fails to load |
80
- | `convergence_gate_blocker.py` | PreToolUse (Bash) | Convergence workflow actions on a conflicting PR |
81
73
  | `conventional_pr_title_gate.py` | PreToolUse (Bash) | `gh pr create`/`gh pr edit` with a `--title` that is not a Conventional Commit, in a repo whose CI runs a semantic-pull-request title check |
82
74
  | `destructive_command_blocker.py` | PreToolUse (Bash/PowerShell) | Shell commands with destructive literals (`rm -rf`, `git reset --hard`, etc.) |
83
75
  | `docstring_rule_gate_count_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | A stale spelled-out gate-validator count in `docstring-prose-matches-implementation.md` — the "N more gate validators" / "M gated slices" count drifting from the `check_docstring_*` validators the prose names |
@@ -101,13 +93,10 @@ The check modules it calls are the `code_rules_<concern>.py` files below.
101
93
  | `pii_scanner.py` | library | Pure text scanners shared by `pii_prevention_blocker.py` |
102
94
  | `piped_pytest_blocker.py` | PreToolUse (Bash) | A pytest run whose output feeds a pipe, where the pipeline reports the exit code of the command on the right |
103
95
  | `plain_language_blocker.py` | PreToolUse (Write/Edit/AskUserQuestion) | Heavy or jargon words in user-facing prose when `CLAUDE_PROSE_STYLE_ENFORCEMENT` is on (default off); AskUserQuestion lean-block structure stays always on |
104
- | `pr_converge_bugteam_enforcer.py` | PreToolUse | Enforces that bugteam runs in parallel with bugbot in pr-converge loops |
105
- | `pr_description_enforcer.py` | PreToolUse (Bash) | `gh pr create`/`edit`/`comment` bodies that fail the Anthropic claude-code style audit, proof-shaped `gh pr comment` bodies missing proof-of-work parts, and `gh pr ready` while the PR carries no passing proof comment |
106
96
  | `precommit_code_rules_gate.py` | PreToolUse (Bash) | Staged changes that fail the CODE_RULES gate at commit time |
107
97
  | `pytest_testpaths_orphan_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | New `test_*.py` files created under a directory absent from a package's explicit pytest `testpaths` allowlist |
108
98
  | `question_to_user_enforcer.py` | Stop | User-directed questions not routed through `AskUserQuestion` |
109
- | `reviewer_spawn_gate.py` | PreToolUse (Bash) | A sentinel-marked autoconverge reviewer-spawn command (Copilot review request, Bugbot rerun comment) run while `reviewer_availability.py` reports that reviewer down or out of quota |
110
- | `send_user_file_open_locally_blocker.py` | PreToolUse (SendUserFile) | A desk-side file attach (`SendUserFile` with `status` not `proactive`); points to opening the file locally via `Show-Asset.ps1` |
99
+ | `send_user_file_open_locally_blocker.py` | PreToolUse (SendUserFile) | A desk-side file attach (`SendUserFile` with `status` not `proactive`); points to opening the file with its native Windows app |
111
100
  | `sensitive_file_protector.py` | PreToolUse (Write/Edit) | Writes to sensitive credential or config files |
112
101
  | `session_edit_stage_gate.py` | PreToolUse (Bash) | A `git commit` that would drop files edited this session because they are tracked but left unstaged |
113
102
  | `session_handoff_blocker.py` | Stop | Responses suggesting a new session mid-task |
@@ -117,10 +106,6 @@ The check modules it calls are the `code_rules_<concern>.py` files below.
117
106
  | `subprocess_budget_completeness.py` | PreToolUse | Subprocess calls missing required budget arguments |
118
107
  | `tdd_enforcer.py` | PreToolUse (Write/Edit) | Production code written without a matching failing test |
119
108
  | `unscoped_search_blocker.py` | PreToolUse (Bash/PowerShell) | A `find` or recursive listing that walks from the filesystem root, a drive root, bare home, or a network share root |
120
- | `verdict_directory_write_blocker.py` | PreToolUse (Bash/PowerShell) | Shell writes into `~/.claude/verification/` |
121
- | `verified_commit_gate.py` | PreToolUse (Bash/PowerShell) | `git commit`/`git push` without a passing verifier verdict |
122
- | `verified_commit_message_accuracy_blocker.py` | PreToolUse | Commit messages that misstate what the diff has |
123
- | `verifier_verdict_minter.py` | SubagentStop | Mints a passing verdict file when a `code-verifier` agent finishes cleanly |
124
109
  | `volatile_path_in_post_blocker.py` | PreToolUse (Bash/MCP GitHub) | `gh` post commands and GitHub MCP post tools whose body references a volatile path (job scratch dir, worktree, or system temp) that outlives the durable post |
125
110
  | `windows_rmtree_blocker.py` | PreToolUse (Write/Edit) | `shutil.rmtree` with `ignore_errors=True` on Windows |
126
111
  | `workflow_substitution_slot_blocker.py` | PreToolUse (Write/Edit) | Workflow templates with bare per-iteration tokens missing angle-bracket slots |
@@ -131,18 +116,8 @@ The check modules it calls are the `code_rules_<concern>.py` files below.
131
116
  | File | Role |
132
117
  |---|---|
133
118
  | `_gh_body_arg_utils.py` | Parsing helpers for `gh_body_arg_blocker.py` |
134
- | `code_review_enforcement_config_bootstrap.py` | Binds `config.code_review_enforcement_constants` to the sibling `config/` file by explicit location, so the code-review gate family resolves its constants regardless of a foreign `config` package's `sys.path` order |
135
- | `code_review_gate_deny.py` | Shared deny scaffold for the push and PR-create code-review gates: the `hookSpecificOutput` deny-payload builder and the log-and-emit helper, so both gates share one deny shape |
136
- | `code_review_stamp_store.py` | Reads and writes the per-work-tree code-review stamp files under `~/.claude/code-review-stamps/`, and decides whether a clean stamp at the needed effort covers the live branch surface |
137
- | `pr_description_body_audit.py` | Body audit logic for `pr_description_enforcer.py` |
138
- | `pr_description_command_parser.py` | `gh` command parsing for `pr_description_enforcer.py` |
139
- | `pr_description_pr_number.py` | PR number extraction logic |
140
- | `pr_description_proof_of_work.py` | Proof-of-work comment audit and `gh pr ready` gate logic for `pr_description_enforcer.py` |
141
- | `pr_description_readability.py` | Readability checks on PR description bodies |
142
- | `verification_verdict_store.py` | Reads and writes verdict files under `~/.claude/verification/` |
143
- | `verified_commit_config_bootstrap.py` | Binds `config.verified_commit_constants` to the sibling `config/` file by explicit location, so the gate family resolves its constants regardless of a foreign `config` package's `sys.path` order |
144
119
 
145
120
  ## Conventions
146
121
 
147
122
  - Tests live beside each hook as `test_<hookname>.py` or `test_<hookname>_<suffix>.py`. Run with `python -m pytest <test_file>`.
148
- - Tunable constants live in `hooks_constants/<hook_name>_constants.py`; the verified-commit family uses `blocking/config/verified_commit_constants.py`.
123
+ - Tunable constants live in `hooks_constants/<hook_name>_constants.py`.
@@ -1,22 +1,10 @@
1
1
  # hooks/blocking/config
2
2
 
3
- A Python package that holds shared constants for blocking hooks. Verified-commit gate modules and opinionated prose-style gates import from here.
3
+ A Python package that holds shared constants for blocking hooks. Opinionated prose-style gates import from here.
4
4
 
5
5
  ## Key files
6
6
 
7
7
  | File | Contents |
8
8
  |---|---|
9
9
  | `__init__.py` | Declares this as a regular package (not a namespace package) so it resolves first on `sys.path` |
10
- | `code_review_enforcement_constants.py` | Stamp and effort tunables for the code-review enforcement gates |
11
10
  | `prose_style_enforcement_constants.py` | `CLAUDE_PROSE_STYLE_ENFORCEMENT` opt-in (default off) for opinionated prose gates |
12
- | `verified_commit_constants.py` | All tunables for the gate: directory names, regex patterns for detecting verdict paths and obfuscation attempts, timeout values, git subcommand sets, bypass marker, and corrective messages |
13
- | `verified_commit_context_constants.py` | `VERIFY_SKIP_ADDITIONAL_CONTEXT`, the ``# verify-skip`` usage rule the gate attaches to a deny payload's `additionalContext` |
14
- | `verified_commit_gate_output_constants.py` | `PRE_TOOL_USE_HOOK_EVENT_NAME` and `DENY_PERMISSION_DECISION` (the deny payload's event name and decision string), `GATE_HOOK_MODULE_NAME` (the gate's own module name for block logging), and `REGEX_ALTERNATION_SEPARATOR` (the `\|` join for the directory-change verb alternation in `gated_invocations.py`) |
15
-
16
- ## Key constants in `verified_commit_constants.py`
17
-
18
- - `VERIFICATION_BYPASS_MARKER` — the `# verify-skip` comment that exempts a single commit/push from the gate
19
- - `MINTING_AGENT_TYPE` — `"code-verifier"`, the agent type whose SubagentStop hook mints verdicts
20
- - `VERDICT_DIRECTORY_NAME` — `"verification"`, the directory under `~/.claude/` that holds verdict JSON files
21
- - `DOCS_ONLY_EXTENSIONS` — extensions (`.md`, `.txt`, images) whose changes are mechanically exempt from the gate
22
- - `CORRECTIVE_MESSAGE` / `VERDICT_DIRECTORY_GUARD_MESSAGE` — user-facing block messages
@@ -36,8 +36,6 @@ _hooks_dir = str(Path(__file__).resolve().parent.parent)
36
36
  if _hooks_dir not in sys.path:
37
37
  sys.path.insert(0, _hooks_dir)
38
38
 
39
- from verification_verdict_store import resolve_repo_root # noqa: E402
40
-
41
39
  from blocking._gh_body_arg_utils import ( # noqa: E402
42
40
  all_value_flags,
43
41
  count_extra_tokens_to_skip_for_split_quoted_value,
@@ -68,6 +66,7 @@ from hooks_constants.conventional_pr_title_gate_constants import ( # noqa: E402
68
66
  YAML_LIST_ITEM_PREFIX,
69
67
  )
70
68
  from hooks_constants.hook_block_logger import log_hook_block # noqa: E402
69
+ from hooks_constants.project_paths_reader import find_git_root as resolve_repo_root # noqa: E402
71
70
 
72
71
 
73
72
  def _matches_gh_pr_title_subcommand(command: str) -> bool:
@@ -3,7 +3,7 @@
3
3
 
4
4
  SendUserFile attaches a file to the session. While the user is at the terminal
5
5
  (status "normal" or unset) an attach does not let them see the file — it must
6
- open on screen in its own viewer via Show-Asset.ps1. The one attach allowed
6
+ open on screen in its native Windows app. The one attach allowed
7
7
  through is an away-from-desk phone push (status "proactive").
8
8
  """
9
9
 
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env python3
2
- """Stop-hook dispatcher that hosts the six Stop-chain hooks in one process.
2
+ """Stop-hook dispatcher that hosts the Stop-chain blockers in one process.
3
3
 
4
4
  Reads the Stop payload from stdin once, runs each hosted hook in registration
5
5
  order via the shared hosted-hook runner, and emits the first block decision the
6
- chain produced. Later hooks still run so side-effect hooks (the log extractor
7
- wrapper) fire even when an earlier hook blocked. A single hosted hook crash
8
- fails open and does not suppress the remaining hooks.
6
+ chain produced. Later hooks still run after an earlier block so every hosted
7
+ hook gets a chance to run. A single hosted hook crash fails open and does not
8
+ suppress the remaining hooks.
9
9
  """
10
10
 
11
11
  from __future__ import annotations
@@ -221,9 +221,6 @@ def test_powershell_selects_the_shared_hooks_in_registration_order() -> None:
221
221
  assert powershell_paths == [
222
222
  "blocking/unscoped_search_blocker.py",
223
223
  "blocking/pii_prevention_blocker.py",
224
- "blocking/code_review_push_gate.py",
225
- "blocking/code_review_pr_create_gate.py",
226
- "blocking/code_review_stamp_directory_write_blocker.py",
227
224
  ]
228
225
 
229
226
 
@@ -715,8 +715,8 @@ def test_dispatcher_write_applies_both_groups() -> None:
715
715
  assert "blocking/plain_language_blocker.py" in all_write_script_paths, (
716
716
  "plain_language_blocker (Group B) must be in Write applicable set"
717
717
  )
718
- assert len(all_write_entries) == 21, (
719
- f"Write tool must apply to all 21 hosted hooks, got {len(all_write_entries)}"
718
+ assert len(all_write_entries) == 19, (
719
+ f"Write tool must apply to all 19 hosted hooks, got {len(all_write_entries)}"
720
720
  )
721
721
 
722
722
 
@@ -732,16 +732,16 @@ def test_dispatcher_edit_applies_both_groups() -> None:
732
732
  assert "advisory/refactor_guard.py" in all_edit_script_paths, (
733
733
  "refactor_guard is Edit-scoped and hosted, so it belongs in the Edit applicable set"
734
734
  )
735
- assert len(all_edit_entries) == 24, (
736
- f"expected 24 Edit entries, got {len(all_edit_entries)}"
735
+ assert len(all_edit_entries) == 22, (
736
+ f"expected 22 Edit entries, got {len(all_edit_entries)}"
737
737
  )
738
738
 
739
739
 
740
740
  def test_dispatcher_multi_edit_applies_only_group_b() -> None:
741
741
  """MultiEdit tool triggers only Group B (10 hooks), not Group A."""
742
742
  all_multi_edit_entries = _applicable_entries_for_tool(MULTI_EDIT_TOOL_NAME)
743
- assert len(all_multi_edit_entries) == 10, (
744
- f"MultiEdit tool must apply to exactly 10 Group-B hooks, got {len(all_multi_edit_entries)}"
743
+ assert len(all_multi_edit_entries) == 9, (
744
+ f"MultiEdit tool must apply to exactly 9 Group-B hooks, got {len(all_multi_edit_entries)}"
745
745
  )
746
746
 
747
747
 
@@ -969,7 +969,6 @@ def test_hosted_hook_set_covers_all_write_edit_blocking_hooks() -> None:
969
969
  "blocking/state_description_blocker.py",
970
970
  "blocking/subprocess_budget_completeness.py",
971
971
  "blocking/hook_prose_detector_consistency.py",
972
- "blocking/verified_commit_message_accuracy_blocker.py",
973
972
  "blocking/workflow_substitution_slot_blocker.py",
974
973
  "blocking/claude_md_orphan_file_blocker.py",
975
974
  "blocking/pytest_testpaths_orphan_blocker.py",
@@ -45,8 +45,9 @@ def test_allows_proactive_status() -> None:
45
45
  assert _should_block(PROACTIVE_STATUS) is False
46
46
 
47
47
 
48
- def test_corrective_message_points_to_show_asset() -> None:
49
- assert "Show-Asset.ps1" in CORRECTIVE_MESSAGE
48
+ def test_corrective_message_points_to_native_windows_app() -> None:
49
+ assert "native Windows app" in CORRECTIVE_MESSAGE
50
+ assert "Show-Asset.ps1" not in CORRECTIVE_MESSAGE
50
51
 
51
52
 
52
53
  def test_corrective_message_names_proactive_escape_hatch() -> None:
@@ -71,7 +72,7 @@ def test_main_blocks_normal_attach() -> None:
71
72
  output_text = _run_main_with_io(json.dumps(hook_input))
72
73
  output = json.loads(output_text)
73
74
  assert output["hookSpecificOutput"]["permissionDecision"] == "deny"
74
- assert "Show-Asset.ps1" in output["hookSpecificOutput"]["permissionDecisionReason"]
75
+ assert "native Windows app" in output["hookSpecificOutput"]["permissionDecisionReason"]
75
76
 
76
77
 
77
78
  def test_main_allows_proactive_attach() -> None:
@@ -30,8 +30,6 @@ _BLOCKING_DIRECTORY = Path(__file__).resolve().parent
30
30
  ALL_CONVERTED_HOOK_FILENAMES = (
31
31
  "open_questions_in_plans_blocker.py",
32
32
  "claude_md_orphan_file_blocker.py",
33
- "pr_converge_bugteam_enforcer.py",
34
- "verdict_directory_write_blocker.py",
35
33
  "package_inventory_stale_blocker.py",
36
34
  )
37
35
 
@@ -14,8 +14,8 @@ Hooks and scripts that collect, store, and query hook-firing records. The pipeli
14
14
  | File | What it does |
15
15
  |---|---|
16
16
  | `hook_log_init.py` | One-time setup: creates the Neon schema (runs `schema.sql`), then verifies read-write parity with a sentinel round-trip |
17
- | `hook_log_extractor.py` | Stop hook reads per-session JSONL transcripts and ingests new `hook_*` attachment records into the `hook_events` table; idempotent via a UNIQUE constraint on `(source_jsonl_path, source_line_number)` |
18
- | `hook_log_stop_wrapper.py` | Thin wrapper that invokes `hook_log_extractor.py` from the Stop lifecycle event |
17
+ | `hook_log_extractor.py` | Disabled CLI: exits 0 without reading transcripts or rewriting offset state; body remains for a re-enable path that ingests `hook_*` attachments into Neon |
18
+ | `hook_log_stop_wrapper.py` | Disabled Stop wrapper stub: exits 0; not on the Stop dispatcher roster |
19
19
  | `schema.sql` | DDL for the `hook_events` table, `blocked_commands` view, and supporting indexes |
20
20
  | `requirements-hook-logs.txt` | Runtime dependencies (`psycopg`) for the extractor |
21
21
  | `requirements-hook-logs-dev.txt` | Dev/test dependencies |
@@ -38,6 +38,6 @@ The `blocked_commands` view filters to `outcome = 'blocked'`.
38
38
 
39
39
  ## Conventions
40
40
 
41
- - The extractor exits 0 even when Neon is unreachable (offline-graceful); it logs to `OFFLINE_WARNING_LOG` and does not block session end.
41
+ - Extractor and Stop wrapper mains are disabled and exit 0 with no work.
42
42
  - Constants for the extractor (table name, offset state file, timeout) live in `hooks_constants/hook_log_extractor_constants.py`.
43
43
  - Tests run with `python -m pytest diagnostic/test_hook_log_*.py`.
@@ -67,15 +67,10 @@ from hooks_constants.hook_log_extractor_constants import (
67
67
  CATEGORY_PATH_MINIMUM_PARTS,
68
68
  COMMAND_EXCERPT_MAX_CHARACTERS,
69
69
  CONNECT_TIMEOUT_SECONDS,
70
- DEFAULT_QUERY_FOR_SUMMARY,
71
70
  EMPTY_STRING,
72
71
  EXIT_CODE_EXTRACTOR_ENVIRONMENT_MISSING,
73
72
  EXIT_CODE_SUCCESS,
74
73
  EXIT_CODE_UNKNOWN_QUERY,
75
- FLAG_FULL_REBUILD,
76
- FLAG_INCREMENTAL,
77
- FLAG_QUERY,
78
- FLAG_SUMMARY,
79
74
  HOOK_CATEGORY_UNCATEGORIZED,
80
75
  HOOK_EVENTS_INSERT_SQL,
81
76
  HOOK_EVENTS_TRUNCATE_SQL,
@@ -94,10 +89,8 @@ from hooks_constants.hook_log_extractor_constants import (
94
89
  NEON_DATABASE_URL_ENVIRONMENT_VARIABLE,
95
90
  NEWLINE_JOINER,
96
91
  OFFLINE_WARNING_LOG,
97
- OFFSET_STATE_FILE,
98
92
  OFFSETS_JSON_INDENT,
99
93
  OUTCOME_BY_ATTACHMENT_TYPE,
100
- PROJECTS_TRANSCRIPT_ROOT,
101
94
  QUERIES_DIRECTORY_NAME,
102
95
  QUERY_NAME_PATTERN,
103
96
  QUERY_NO_ROWS_RETURNED_MESSAGE,
@@ -874,35 +867,8 @@ def run_query(named_query: str) -> int:
874
867
 
875
868
 
876
869
  def main() -> int:
877
- """Entry point for the hook-log extractor CLI.
878
-
879
- Supported flags:
880
-
881
- * ``--summary`` prints the top blockers of the last twenty-four hours.
882
- * ``--query <name>`` runs a pre-baked SQL file under ``queries/``.
883
- * ``--full-rebuild`` truncates ``hook_events`` and re-reads every
884
- JSONL from byte zero.
885
- * ``--incremental`` is a documented no-op; it selects the default
886
- byte-offset resumption path that the Stop hook also uses when no
887
- flags are passed.
888
- """
889
- all_cli_arguments = list(sys.argv[1:])
890
- if FLAG_SUMMARY in all_cli_arguments:
891
- return run_summary()
892
- if FLAG_QUERY in all_cli_arguments:
893
- flag_index = all_cli_arguments.index(FLAG_QUERY)
894
- if flag_index + 1 >= len(all_cli_arguments):
895
- return run_query(DEFAULT_QUERY_FOR_SUMMARY)
896
- return run_query(all_cli_arguments[flag_index + 1])
897
- is_full_rebuild_requested = FLAG_FULL_REBUILD in all_cli_arguments
898
- is_incremental_requested = FLAG_INCREMENTAL in all_cli_arguments
899
- if is_incremental_requested and is_full_rebuild_requested:
900
- is_full_rebuild_requested = False
901
- return run_full_extraction(
902
- transcripts_root=PROJECTS_TRANSCRIPT_ROOT,
903
- state_file_path=OFFSET_STATE_FILE,
904
- full_rebuild=is_full_rebuild_requested,
905
- )
870
+ """Disabled extractor entry point: exit success with no work."""
871
+ return EXIT_CODE_SUCCESS
906
872
 
907
873
 
908
874
  if __name__ == "__main__":
@@ -1,173 +1,24 @@
1
1
  #!/usr/bin/env python3
2
- """Stop-hook wrapper for hook_log_extractor: debounced, fire-and-forget.
2
+ """Disabled Stop-hook wrapper for the hook-log extractor.
3
3
 
4
- Runs after every assistant turn (Stop hook), so per-turn latency must
5
- stay near zero. The wrapper:
6
-
7
- 1. Reads the last-spawn timestamp; if it falls within the debounce
8
- window, exits 0 immediately without spawning anything (typical fast
9
- path: a small file read, well under 10ms).
10
- 2. Otherwise records the current timestamp, then launches the extractor
11
- as a fully detached background process (no stdio, separate process
12
- group on POSIX or CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP on
13
- Windows) and returns without waiting for it. CREATE_NO_WINDOW
14
- prevents a console flash on Windows even when the wrapper goes
15
- through ``bws run`` first; DETACHED_PROCESS would let the
16
- grandchild python.exe allocate a fresh console.
17
-
18
- Bitwarden injection: when both ``bws`` is on PATH and
19
- ``BWS_ACCESS_TOKEN`` is set, the extractor is launched via
20
- ``bws run --`` so the Neon URL never hits disk; otherwise it is
21
- launched directly. The extractor itself exits 0 when
22
- ``NEON_HOOK_LOGS_DATABASE_URL`` is unset, so missing dependencies
23
- cannot block session shutdown.
24
-
25
- This wrapper always exits 0 so the Stop hook never surfaces a failure.
4
+ The wrapper is off the Stop dispatcher roster and exits 0 without spawning
5
+ the extractor or writing debounce state. Git history holds the prior
6
+ debounce-and-spawn implementation if Neon-backed extraction returns.
26
7
  """
27
8
 
28
9
  from __future__ import annotations
29
10
 
30
- import os
31
- import shutil
32
- import subprocess
33
11
  import sys
34
- import time
35
12
  from pathlib import Path
36
13
 
37
14
  if str(Path(__file__).resolve().parent.parent) not in sys.path:
38
15
  sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
39
16
 
40
- from hooks_constants.hook_log_extractor_constants import (
41
- BWS_ACCESS_TOKEN_ENV_VAR,
42
- BWS_EXECUTABLE_NAME,
43
- BWS_RUN_SEPARATOR,
44
- BWS_RUN_SUBCOMMAND,
45
- EXIT_CODE_SUCCESS,
46
- FLAG_INCREMENTAL,
47
- STOP_WRAPPER_DEBOUNCE_SECONDS,
48
- STOP_WRAPPER_EXTRACTOR_SCRIPT_NAME,
49
- STOP_WRAPPER_LAST_RUN_TIMESTAMP_FILE,
50
- WINDOWS_CREATE_NEW_PROCESS_GROUP_FLAG,
51
- WINDOWS_CREATE_NO_WINDOW_FLAG,
52
- WINDOWS_OS_NAME,
53
- )
54
-
55
-
56
- def _extractor_script_path() -> str:
57
- return str(
58
- Path(__file__).resolve().parent / STOP_WRAPPER_EXTRACTOR_SCRIPT_NAME
59
- )
60
-
61
-
62
- def _last_run_timestamp_path() -> Path:
63
- return Path(STOP_WRAPPER_LAST_RUN_TIMESTAMP_FILE)
64
-
65
-
66
- def _is_within_debounce_window() -> bool:
67
- timestamp_path = _last_run_timestamp_path()
68
- if not timestamp_path.exists():
69
- return False
70
- try:
71
- previous_timestamp = float(timestamp_path.read_text().strip())
72
- except (OSError, ValueError):
73
- return False
74
- seconds_since_previous_spawn = time.time() - previous_timestamp
75
- if seconds_since_previous_spawn < 0:
76
- return False
77
- return seconds_since_previous_spawn < STOP_WRAPPER_DEBOUNCE_SECONDS
78
-
79
-
80
- def _record_current_timestamp() -> None:
81
- timestamp_path = _last_run_timestamp_path()
82
- timestamp_path.parent.mkdir(parents=True, exist_ok=True)
83
- timestamp_path.write_text(str(time.time()))
84
-
85
-
86
- def _clear_recorded_timestamp() -> None:
87
- """Remove the debounce timestamp regardless of which process wrote it.
88
-
89
- The unlink is unconditional: if process A writes a timestamp, process
90
- B observes it and debounces, then A's spawn fails, A's rollback here
91
- deletes the timestamp B already saw. The next Stop hook then spawns
92
- a fresh extractor instead of debouncing the remainder of the window.
93
- Consequence is benign because the extractor's own offset-file lock
94
- (LOCK_MAXIMUM_RETRY_COUNT in hook_log_extractor_constants) serializes
95
- concurrent extractor runs, so at most one extra extractor briefly
96
- waits on the lock. Scoping the rollback to A's own write would
97
- require a per-process sentinel and tighter atomicity than the
98
- benefit warrants for this Stop-hook fast path.
99
- """
100
- timestamp_path = _last_run_timestamp_path()
101
- timestamp_path.unlink(missing_ok=True)
102
-
103
-
104
- def _can_use_bws() -> bool:
105
- if not os.environ.get(BWS_ACCESS_TOKEN_ENV_VAR):
106
- return False
107
- return shutil.which(BWS_EXECUTABLE_NAME) is not None
108
-
109
-
110
- def _detached_spawn_keyword_arguments() -> dict[str, object]:
111
- spawn_arguments: dict[str, object] = {
112
- "stdin": subprocess.DEVNULL,
113
- "stdout": subprocess.DEVNULL,
114
- "stderr": subprocess.DEVNULL,
115
- "close_fds": True,
116
- }
117
- if os.name == WINDOWS_OS_NAME:
118
- spawn_arguments["creationflags"] = (
119
- WINDOWS_CREATE_NO_WINDOW_FLAG
120
- | WINDOWS_CREATE_NEW_PROCESS_GROUP_FLAG
121
- )
122
- startup_info = subprocess.STARTUPINFO()
123
- startup_info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
124
- startup_info.wShowWindow = subprocess.SW_HIDE
125
- spawn_arguments["startupinfo"] = startup_info
126
- else:
127
- spawn_arguments["start_new_session"] = True
128
- return spawn_arguments
129
-
130
-
131
- def _spawn_with_bws() -> None:
132
- subprocess.Popen(
133
- [
134
- BWS_EXECUTABLE_NAME,
135
- BWS_RUN_SUBCOMMAND,
136
- BWS_RUN_SEPARATOR,
137
- sys.executable,
138
- _extractor_script_path(),
139
- FLAG_INCREMENTAL,
140
- ],
141
- **_detached_spawn_keyword_arguments(),
142
- )
143
-
144
-
145
- def _spawn_without_bws() -> None:
146
- subprocess.Popen(
147
- [
148
- sys.executable,
149
- _extractor_script_path(),
150
- FLAG_INCREMENTAL,
151
- ],
152
- **_detached_spawn_keyword_arguments(),
153
- )
17
+ from hooks_constants.hook_log_extractor_constants import EXIT_CODE_SUCCESS
154
18
 
155
19
 
156
20
  def main() -> int:
157
- """Debounce, then fire-and-forget the extractor; always exit 0."""
158
- try:
159
- if _is_within_debounce_window():
160
- return EXIT_CODE_SUCCESS
161
- _record_current_timestamp()
162
- try:
163
- if _can_use_bws():
164
- _spawn_with_bws()
165
- else:
166
- _spawn_without_bws()
167
- except Exception:
168
- _clear_recorded_timestamp()
169
- except Exception:
170
- pass
21
+ """Exit success without spawning the extractor."""
171
22
  return EXIT_CODE_SUCCESS
172
23
 
173
24
 
@@ -805,32 +805,19 @@ def test_offline_fallback_still_exits_zero_when_warning_log_write_raises(
805
805
  assert exit_code == 0
806
806
 
807
807
 
808
- def test_main_accepts_incremental_flag_as_noop(
809
- tmp_path: Path,
808
+ def test_main_is_disabled_and_skips_extraction(
810
809
  monkeypatch: pytest.MonkeyPatch,
811
810
  ) -> None:
812
- """C8: ``--incremental`` must be recognized and route to default extraction."""
813
- captured_arguments: dict[str, object] = {}
814
-
815
- def _fake_run_full_extraction(
816
- transcripts_root: str,
817
- state_file_path: str,
818
- full_rebuild: bool,
819
- ) -> int:
820
- captured_arguments["transcripts_root"] = transcripts_root
821
- captured_arguments["state_file_path"] = state_file_path
822
- captured_arguments["full_rebuild"] = full_rebuild
823
- return 0
824
-
825
- monkeypatch.setattr(sys, "argv", ["hook_log_extractor.py", "--incremental"])
811
+ """Disabled main exits success without running extraction."""
812
+
813
+ def _fail_if_called(*_args: object, **_kwargs: object) -> int:
814
+ raise AssertionError("run_full_extraction must not run while disabled")
815
+
826
816
  monkeypatch.setattr(
827
- hook_log_extractor, "run_full_extraction", _fake_run_full_extraction
817
+ hook_log_extractor, "run_full_extraction", _fail_if_called
828
818
  )
829
819
 
830
- exit_code = hook_log_extractor.main()
831
-
832
- assert exit_code == 0
833
- assert captured_arguments["full_rebuild"] is False
820
+ assert hook_log_extractor.main() == 0
834
821
 
835
822
 
836
823
  def test_run_query_returns_nonzero_for_unknown_query(