claude-dev-env 2.11.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 (203) hide show
  1. package/_shared/advisor/reference/sol-rung.md +5 -2
  2. package/_shared/advisor/reference/warm-up.md +1 -0
  3. package/_shared/advisor/scripts/codex_sol_advisor.py +96 -31
  4. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +5 -0
  5. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +173 -11
  6. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  7. package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
  8. package/agents/CLAUDE.md +1 -2
  9. package/agents/pr-description-writer.md +11 -81
  10. package/bin/install.mjs +48 -9
  11. package/bin/install.prune.test.mjs +26 -0
  12. package/bin/install.settings-defaults.test.mjs +60 -33
  13. package/bin/install.test.mjs +2 -4
  14. package/commands/CLAUDE.md +1 -0
  15. package/commands/sr-loop.md +52 -0
  16. package/docs/CLAUDE.md +0 -1
  17. package/docs/references/CLAUDE.md +0 -1
  18. package/hooks/CLAUDE.md +1 -1
  19. package/hooks/blocking/CLAUDE.md +2 -27
  20. package/hooks/blocking/config/CLAUDE.md +1 -13
  21. package/hooks/blocking/conventional_pr_title_gate.py +1 -2
  22. package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
  23. package/hooks/blocking/stop_dispatcher.py +4 -4
  24. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
  25. package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
  26. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
  27. package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
  28. package/hooks/diagnostic/CLAUDE.md +3 -3
  29. package/hooks/diagnostic/hook_log_extractor.py +2 -36
  30. package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
  31. package/hooks/diagnostic/test_hook_log_extractor.py +8 -21
  32. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
  33. package/hooks/git-hooks/git_hooks_constants/__init__.py +0 -6
  34. package/hooks/git-hooks/pre_push.py +3 -85
  35. package/hooks/git-hooks/test_pre_push.py +0 -130
  36. package/hooks/hooks.json +0 -35
  37. package/hooks/hooks_constants/CLAUDE.md +0 -9
  38. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
  39. package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
  40. package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
  41. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
  42. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
  43. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  44. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
  45. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
  46. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  47. package/hooks/lifecycle/CLAUDE.md +1 -3
  48. package/package.json +1 -1
  49. package/rules/CLAUDE.md +0 -2
  50. package/rules/gh-cli-conventions.md +0 -1
  51. package/rules/git-workflow.md +1 -3
  52. package/scripts/CLAUDE.md +0 -1
  53. package/scripts/_code_review_test_support.py +0 -95
  54. package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
  55. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
  56. package/scripts/invoke_code_review.py +8 -451
  57. package/scripts/test_invoke_code_review.py +5 -61
  58. package/scripts/test_invoke_code_review_cli.py +1 -45
  59. package/scripts/test_invoke_code_review_contract.py +1 -118
  60. package/settings.json +1 -10
  61. package/skills/CLAUDE.md +8 -0
  62. package/skills/autoconverge/reference/CLAUDE.md +1 -1
  63. package/skills/autoconverge/reference/convergence.md +7 -7
  64. package/skills/autoconverge/reference/gotchas.md +3 -10
  65. package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
  66. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
  67. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
  68. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
  69. package/skills/autoconverge/workflow/converge.mjs +201 -238
  70. package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
  71. package/skills/bugteam/CONSTRAINTS.md +1 -1
  72. package/skills/bugteam/SKILL.md +9 -0
  73. package/skills/bugteam/reference/audit-and-teammates.md +10 -6
  74. package/skills/bugteam/test_skill_additions.py +9 -0
  75. package/skills/comments/SKILL.md +65 -0
  76. package/skills/descriptions/SKILL.md +51 -0
  77. package/skills/emergencies/SKILL.md +42 -0
  78. package/skills/imagegen/SKILL.md +21 -0
  79. package/skills/imagegen/scripts/config/__init__.py +1 -0
  80. package/skills/imagegen/scripts/config/constants.py +28 -0
  81. package/skills/imagegen/scripts/imagegen.py +45 -0
  82. package/skills/imagegen/scripts/imagegen_core.py +528 -0
  83. package/skills/imagegen/scripts/test_imagegen.py +204 -0
  84. package/skills/orchestrator/SKILL.md +2 -4
  85. package/skills/plan-to-pr/SKILL.md +6 -7
  86. package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
  87. package/skills/plan-to-pr/reference/process-inventory.md +2 -2
  88. package/skills/plan-to-pr/reference/review-loop.md +2 -2
  89. package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
  90. package/skills/plan-to-pr/reference/task-seeds.md +1 -1
  91. package/skills/plan-to-pr/reference/task-ticket.md +3 -4
  92. package/skills/plan-to-pr/scripts/config/constants.py +1 -2
  93. package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
  94. package/skills/plan-to-pr/test_skill_contract.py +2 -2
  95. package/skills/pr-converge/SKILL.md +13 -16
  96. package/skills/pr-converge/reference/progress-checklist.md +6 -5
  97. package/skills/pr-converge/reference/state-schema.md +1 -27
  98. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  99. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
  100. package/skills/pr-loop-lifecycle/SKILL.md +1 -1
  101. package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
  102. package/skills/prototype/SKILL.md +4 -4
  103. package/skills/prototype/reference/honest-limitations.md +4 -4
  104. package/skills/prototype/reference/promotion-tasks.md +2 -2
  105. package/skills/prototype/workflows/promotion.md +4 -6
  106. package/skills/reviews/SKILL.md +85 -0
  107. package/skills/small-cl/SKILL.md +52 -0
  108. package/skills/usage-pause/SKILL.md +1 -1
  109. package/skills/usage-pause/scripts/resolve_usage_window.py +32 -4
  110. package/skills/usage-pause/scripts/test_resolve_usage_window.py +26 -0
  111. package/skills/usage-pause/scripts/usage_pause_constants/resolve_usage_window_constants.py +3 -1
  112. package/agents/code-verifier.md +0 -74
  113. package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
  114. package/docs/references/code-review-enforcement.md +0 -132
  115. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
  116. package/hooks/blocking/code_review_gate_deny.py +0 -74
  117. package/hooks/blocking/code_review_pr_create_gate.py +0 -198
  118. package/hooks/blocking/code_review_push_gate.py +0 -145
  119. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
  120. package/hooks/blocking/code_review_stamp_store.py +0 -233
  121. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
  122. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
  123. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
  124. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
  125. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
  126. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
  127. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
  128. package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
  129. package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
  130. package/hooks/blocking/config/verified_commit_constants.py +0 -160
  131. package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
  132. package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
  133. package/hooks/blocking/conftest.py +0 -123
  134. package/hooks/blocking/convergence_gate_blocker.py +0 -310
  135. package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
  136. package/hooks/blocking/pr_description_body_audit.py +0 -148
  137. package/hooks/blocking/pr_description_command_parser.py +0 -237
  138. package/hooks/blocking/pr_description_enforcer.py +0 -227
  139. package/hooks/blocking/pr_description_pr_number.py +0 -155
  140. package/hooks/blocking/pr_description_proof_of_work.py +0 -385
  141. package/hooks/blocking/pr_description_readability.py +0 -364
  142. package/hooks/blocking/reviewer_spawn_gate.py +0 -182
  143. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
  144. package/hooks/blocking/test_code_review_gate_deny.py +0 -54
  145. package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
  146. package/hooks/blocking/test_code_review_push_gate.py +0 -205
  147. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
  148. package/hooks/blocking/test_code_review_stamp_store.py +0 -205
  149. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
  150. package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
  151. package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
  152. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
  153. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
  154. package/hooks/blocking/test_pr_description_enforcer.py +0 -221
  155. package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
  156. package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
  157. package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
  158. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
  159. package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
  160. package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
  161. package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
  162. package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
  163. package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
  164. package/hooks/blocking/test_verification_verdict_store.py +0 -974
  165. package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
  166. package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
  167. package/hooks/blocking/test_verified_commit_gate.py +0 -581
  168. package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
  169. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
  170. package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
  171. package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
  172. package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
  173. package/hooks/blocking/verification_verdict_store.py +0 -1039
  174. package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
  175. package/hooks/blocking/verified_commit_gate.py +0 -113
  176. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
  177. package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
  178. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
  179. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
  180. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
  181. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
  182. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
  183. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
  184. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
  185. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
  186. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
  187. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
  188. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
  189. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
  190. package/hooks/blocking/verifier_verdict_minter.py +0 -280
  191. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
  192. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
  193. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
  194. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
  195. package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
  196. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
  197. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
  198. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
  199. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
  200. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
  201. package/rules/proof-of-work-pr-comments.md +0 -22
  202. package/rules/verified-commit-gate-skip.md +0 -4
  203. package/scripts/Show-Asset.ps1 +0 -106
@@ -1,94 +1,24 @@
1
1
  ---
2
2
  name: pr-description-writer
3
- description: "Optional agent for PR descriptions and PR comments. Authors bodies in one of three Anthropic-derived shapes (Trivial / Standard / Heavy) so PRs match merged-PR style in `anthropics/claude-code`, `anthropics/claude-code-action`, and `anthropics/claude-code-sdk-python`, and pass the `pr_description_enforcer` PreToolUse hook's body audit on `gh pr create`, `gh pr edit`, and `gh pr comment`."
3
+ description: "Optional agent that drafts factual pull request descriptions and comments from the current diff and validates each statement against the master review guides."
4
4
  tools: Read,Grep,Glob,Bash
5
5
  ---
6
6
 
7
7
  # PR Description Writer
8
8
 
9
- You write PR bodies and PR comments. Your output follows Anthropic Claude Code house style. The style derives from a 120-PR sample across `anthropics/claude-code`, `anthropics/claude-code-action`, and `anthropics/claude-code-sdk-python`. Pick a shape that matches the diff size. Open with an imperative verb. Explain WHY the change exists in prose a reviewer can scan. The companion reference `packages/claude-dev-env/docs/PR_DESCRIPTION_GUIDE.md` carries the full header catalog and the readability escape-hatch flow.
9
+ Write the body from the current pull request diff and task. Use the [description guide](../skills/descriptions/SKILL.md#required-content) for required content and [description shape](../skills/descriptions/SKILL.md#description-shape). Use the [comment guide](../skills/comments/SKILL.md#writing-useful-review-comments) for a review comment.
10
10
 
11
- ## The three shapes
11
+ ## Draft the body
12
12
 
13
- Choose one shape per body. Mixing shapes signals confusion to the reviewer.
13
+ 1. Inspect the cumulative diff, the current pull request body, and the validation results.
14
+ 2. State the behavior change, its scope, and the validation that ran.
15
+ 3. Preserve accurate issue links and curated sections from the current body.
16
+ 4. Use headings when they help a reviewer scan the change.
14
17
 
15
- ### Trivial diff ≤ 10 lines
18
+ ## Publish through GitHub CLI
16
19
 
17
- One to three sentences of prose. Zero headers. No `## Summary`, no `## Test plan`. The opening verb describes the change directly.
20
+ Place markdown in a BOM-free temporary file and pass its path with `--body-file`. Follow [gh CLI conventions](../rules/gh-cli-conventions.md#body-content-goes-in-a-file).
18
21
 
19
- ```markdown
20
- Bump bun to 1.3.14. Picks up the bugfix for the runtime panic on empty stdin.
21
- ```
22
+ ## Check the draft
22
23
 
23
- ### Standard diff 11–500 lines
24
-
25
- Opens with an imperative-verb paragraph. Optional headers drawn from the Anthropic set: `## Summary`, `## Problem`, `## Fix`, `## Changes`, `## Test plan`, `## Tests`, `## Testing`, `## Approach`, `## Root cause`. Most Standard PRs use one or two — pick the ones the change earns.
26
-
27
- ```markdown
28
- Adds a syllable-counted Flesch reading score to the PR description enforcer. Bodies above the readability ceiling surface a targeted block message before the strike counter increments.
29
-
30
- ## Test plan
31
-
32
- - [ ] `pytest packages/claude-dev-env/hooks/blocking/test_pr_description_enforcer.py`
33
- - [ ] Open a draft PR with a 45-word sentence and confirm the metric block fires
34
- ```
35
-
36
- ### Heavy — diff > 500 lines, or a cross-cutting bug fix
37
-
38
- Two required header categories. Body MUST contain at least one of `## Problem` or `## Summary`. Body MUST contain at least one of `## Test plan`, `## Testing`, `## Tests`, `## Verification`, or `## Validation`. Add `## Fix`, `## Changes`, `## Root cause`, or `## Approach` when the change earns them.
39
-
40
- ```markdown
41
- ## Problem
42
-
43
- Long-running `gh api` review fetches drop pages silently when the reviewer count crosses 30. Bugbot findings on PRs past the first review cycle stay hidden.
44
-
45
- ## Fix
46
-
47
- Routes every `gh api .../reviews` and `.../comments` call through `--paginate --slurp | jq` so the cross-page filter sees the full set.
48
-
49
- ## Test plan
50
-
51
- - [ ] Mock paginated API and assert `jq` filter operates on the merged stream
52
- - [ ] Replay PR #467 review history and confirm the late bugbot comment surfaces
53
- ```
54
-
55
- ## Style rules
56
-
57
- - Open with an imperative verb: `Adds`, `Fixes`, `Updates`, `Removes`, `Ports`, `Tightens`. 51% of prose-opening Anthropic PRs follow this pattern.
58
- - Do not open with `This PR`. The phrase appears in 1 of 120 corpus PRs. The enforcer hook treats `This PR adds/fixes/updates…` as a hard block.
59
- - Backticked identifiers (`function_name`, `--flag`, `CONST`) belong in intro prose. Anthropic PRs use them routinely.
60
- - Em-dashes — like these — work as parenthetical separators. 48% of corpus PRs use them.
61
- - Focus on WHY. The diff shows WHAT.
62
- - No code snippets in the description body. Reviewers read the diff for code.
63
- - No implementation-detail dumping. `Add a 5-second timeout` beats ``Add `pullStartedAt: Date.now()` parameter to the `runPull()` callback``.
64
- - No filler. Strike `In this PR I have made the following changes:` and start with the action.
65
-
66
- ## Gotchas
67
-
68
- - **Self-closing reference.** Do not write `Fixes #<the PR's own number>` in a `gh pr edit` or `gh pr comment` body. The enforcer hook reads the positional after `pr edit` or `pr comment` and blocks the body when the self-reference matches.
69
- - **Trivial shape with `## Summary` header.** A four-line body wrapped in `## Summary` trips the ceremony-on-Trivial check. Keep tiny bodies as plain prose.
70
- - **Heavy shape missing a testing header.** A 600-line PR body with `## Problem` and `## Fix` but no `## Test plan` / `## Testing` / `## Tests` block trips the Heavy required-headers check.
71
- - **`This PR` opening on any shape.** Hard block regardless of size.
72
- - **Dual-mode dispatch (unique pattern in `hooks/blocking/`).** The `pr_description_enforcer.py` hook reads `sys.argv` for `--readability-*` CLI flags as well as its stdin-JSON hook input. This dual-mode pattern is the only one of its kind across the blocking hooks directory. When extending the hook, preserve the precedence: CLI flags handled first and exited; stdin-JSON path falls through.
73
-
74
- ## Readability targets
75
-
76
- The enforcer hook computes a Flesch Reading Ease score plus sentence-length metrics on the intro paragraph and the first body section combined.
77
-
78
- | Metric | Target |
79
- |---|---|
80
- | Longest sentence | ≤ 28 words |
81
- | Average sentence | ≤ 18 words |
82
- | Flesch Reading Ease | ≥ 50 |
83
-
84
- The hook tracks a per-user strike counter at `~/.claude/state/pr_description_readability_strikes.json`. The first two readability failures emit a metric-specific block (e.g., `Readability: longest sentence is 32 words (maximum 28); split or rewrite the longest sentence`). The third triggering failure fires an escape-hatch message listing four recovery actions. The full action list lives in `PR_DESCRIPTION_GUIDE.md` under "Escape hatch".
85
-
86
- Hit the targets on first attempt by writing short sentences in common Anglo-Saxon words. The average tracks the corpus median of 14.5.
87
-
88
- ## Refusals
89
-
90
- Decline to write a body when:
91
-
92
- - The PR has zero diff lines (genuinely empty).
93
- - The user describes intent that contradicts the diff (e.g., "describe this as a bug fix" when the diff adds a feature).
94
- - The user requests a shape that violates the hook (e.g., `## Summary` on a one-line bump). Offer the correct shape.
24
+ Confirm every statement matches the diff and current pull request state. Return the final markdown and the file path ready for publication.
package/bin/install.mjs CHANGED
@@ -115,8 +115,10 @@ export const CORE_INCLUDE_DIRECTORIES = [
115
115
  ];
116
116
 
117
117
  export const CORE_SKILLS = [
118
+ 'imagegen',
118
119
  'orchestrator', 'orchestrator-refresh', 'team-advisor', 'grokify',
119
120
  'grok-spawn',
121
+ 'small-cl', 'comments', 'reviews', 'descriptions', 'emergencies',
120
122
  'anthropic-plan', 'everything-search',
121
123
  'privacy-hygiene',
122
124
  'issue-tracker',
@@ -928,7 +930,6 @@ export const FOLDED_HOOK_RELATIVE_PATHS = new Set([
928
930
  'blocking/state_description_blocker.py',
929
931
  'blocking/subprocess_budget_completeness.py',
930
932
  'blocking/hook_prose_detector_consistency.py',
931
- 'blocking/verified_commit_message_accuracy_blocker.py',
932
933
  'blocking/workflow_substitution_slot_blocker.py',
933
934
  'blocking/claude_md_orphan_file_blocker.py',
934
935
  'blocking/env_var_table_code_drift_blocker.py',
@@ -1491,23 +1492,58 @@ function loadPackageManagedDenyEntries() {
1491
1492
  }
1492
1493
 
1493
1494
  /**
1494
- * Merge package-owned permission defaults into ~/.claude/settings.json.
1495
+ * Load deny entries an earlier manifest records as package-managed.
1495
1496
  *
1496
- * @returns {{addedCount: number, alreadyPresentCount: number, managedDenyEntries: string[]}}
1497
+ * @returns {string[]}
1498
+ */
1499
+ function loadPriorManagedDenyEntries() {
1500
+ if (!existsSync(MANIFEST_FILE)) return [];
1501
+ try {
1502
+ const priorManifest = JSON.parse(readFileSync(MANIFEST_FILE, 'utf8'));
1503
+ return managedDenyEntriesFromPackageSettings({
1504
+ permissions: priorManifest?.[MANIFEST_MANAGED_PERMISSIONS_KEY],
1505
+ });
1506
+ } catch {
1507
+ return [];
1508
+ }
1509
+ }
1510
+
1511
+ /**
1512
+ * Reconcile package-managed permission defaults with ~/.claude/settings.json.
1513
+ *
1514
+ * @returns {{addedCount: number, alreadyPresentCount: number, removedCount: number, managedDenyEntries: string[]}}
1497
1515
  */
1498
1516
  function mergeManagedPermissions() {
1499
- const managedDenyEntries = loadPackageManagedDenyEntries();
1500
- if (managedDenyEntries.length === 0) {
1501
- return { addedCount: 0, alreadyPresentCount: 0, managedDenyEntries: [] };
1517
+ const allCurrentManagedDenyEntries = loadPackageManagedDenyEntries();
1518
+ const allPriorManagedDenyEntries = loadPriorManagedDenyEntries();
1519
+ const allCurrentManagedDenyEntrySet = new Set(allCurrentManagedDenyEntries);
1520
+ const allRetiredManagedDenyEntries = allPriorManagedDenyEntries.filter(
1521
+ eachEntry => !allCurrentManagedDenyEntrySet.has(eachEntry),
1522
+ );
1523
+ const hasCurrentManagedDenyEntries = allCurrentManagedDenyEntries.length > 0;
1524
+ const hasRetiredManagedDenyEntries = allRetiredManagedDenyEntries.length > 0;
1525
+ if (!hasCurrentManagedDenyEntries && !hasRetiredManagedDenyEntries) {
1526
+ return {
1527
+ addedCount: 0,
1528
+ alreadyPresentCount: 0,
1529
+ removedCount: 0,
1530
+ managedDenyEntries: [],
1531
+ };
1502
1532
  }
1503
1533
  const settingsPath = join(CLAUDE_HOME, SETTINGS_FILE_NAME);
1504
1534
  const settingsExisted = existsSync(settingsPath);
1505
1535
  const settings = loadClaudeSettingsObjectOrExit(settingsPath);
1506
- const mergeOutcome = mergeManagedPermissionsIntoSettings(settings, managedDenyEntries);
1507
- if (mergeOutcome.addedCount > 0 || !settingsExisted) {
1536
+ const pruneOutcome = pruneManagedPermissionsFromSettings(settings, allRetiredManagedDenyEntries);
1537
+ const mergeOutcome = hasCurrentManagedDenyEntries
1538
+ ? mergeManagedPermissionsIntoSettings(settings, allCurrentManagedDenyEntries)
1539
+ : { addedCount: 0, alreadyPresentCount: 0, managedDenyEntries: [] };
1540
+ const shouldWriteSettings = pruneOutcome.removedCount > 0
1541
+ || mergeOutcome.addedCount > 0
1542
+ || (!settingsExisted && hasCurrentManagedDenyEntries);
1543
+ if (shouldWriteSettings) {
1508
1544
  writeFileSync(settingsPath, JSON.stringify(settings, null, 4) + '\n');
1509
1545
  }
1510
- return mergeOutcome;
1546
+ return { ...mergeOutcome, removedCount: pruneOutcome.removedCount };
1511
1547
  }
1512
1548
 
1513
1549
  /**
@@ -2010,6 +2046,9 @@ function executeInstallPlanMutations(plan, transactionHelpers) {
2010
2046
 
2011
2047
  const permissionMerge = mergeManagedPermissions();
2012
2048
  summary.managedPermissions = permissionMerge;
2049
+ if (permissionMerge.removedCount > 0) {
2050
+ console.log(` Permissions: ${permissionMerge.removedCount} retired managed deny(s) removed`);
2051
+ }
2013
2052
  if (permissionMerge.managedDenyEntries.length > 0) {
2014
2053
  console.log(
2015
2054
  ` Permissions: ${permissionMerge.addedCount} managed deny(s) added, `
@@ -37,6 +37,13 @@ const RUNTIME_ARTIFACT_RELATIVE_SEGMENTS = ['scripts', '__pycache__', 'helper.cp
37
37
  const DEPENDENCY_STUB_PACKAGE_SEGMENTS = ['@jl-cmd', 'prompt-generator'];
38
38
  const DEPENDENCY_SUPPLIED_SKILL_DIRECTORY = 'dependency-supplied-skill';
39
39
  const SCOPED_GROUP_SKILL_DIRECTORY = 'orchestrator';
40
+ const CORE_REVIEW_GUIDE_SKILL_DIRECTORIES = [
41
+ 'small-cl',
42
+ 'comments',
43
+ 'reviews',
44
+ 'descriptions',
45
+ 'emergencies',
46
+ ];
40
47
  const PRIOR_RUN_BACKUP_DIRECTORY_NAMES = [
41
48
  '2020-01-01T00-00-00-000Z',
42
49
  '2021-06-15T12-30-45-123Z',
@@ -829,6 +836,25 @@ test('a scoped --only install records the skill names it installed', () => {
829
836
  }
830
837
  });
831
838
 
839
+ test('a scoped core install ships the canonical review guide skills', () => {
840
+ const sandbox = createSandbox();
841
+ try {
842
+ runInstaller(sandbox.homeDirectory, ['--only', 'core']);
843
+
844
+ const missingGuideSkillNames = CORE_REVIEW_GUIDE_SKILL_DIRECTORIES.filter(
845
+ eachSkillName => !existsSync(join(sandbox.skillsDirectory, eachSkillName, 'SKILL.md')),
846
+ );
847
+ assert.deepEqual(missingGuideSkillNames, []);
848
+
849
+ const recordedSkillNames = new Set(readManifest(sandbox.manifestPath).skills);
850
+ for (const eachSkillName of CORE_REVIEW_GUIDE_SKILL_DIRECTORIES) {
851
+ assert.equal(recordedSkillNames.has(eachSkillName), true);
852
+ }
853
+ } finally {
854
+ rmSync(sandbox.homeDirectory, { recursive: true, force: true });
855
+ }
856
+ });
857
+
832
858
  test('the prune skip is scoped: once the dependency resolves a later full install prunes normally', () => {
833
859
  const sandbox = createSandbox();
834
860
  try {
@@ -26,12 +26,15 @@ const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
26
26
  const PACKAGE_SETTINGS_PATH = join(PACKAGE_ROOT, 'settings.json');
27
27
  const INSTALL_ENTRY = join(PACKAGE_ROOT, 'bin', 'install.mjs');
28
28
 
29
- const EXPECTED_DENY_ENTRIES = [
29
+ const EXPECTED_DENY_ENTRIES = [];
30
+ const SAMPLE_MANAGED_DENY_ENTRIES = ['Edit($HOME/.claude/managed-test/**)'];
31
+ const LEGACY_MANAGED_DENY_ENTRIES = [
30
32
  'Edit($HOME/.claude/verification/**)',
31
33
  'Write($HOME/.claude/code-review-stamps/**)',
32
34
  'Edit($HOME/.claude/code-review-stamps/**)',
33
35
  'MultiEdit($HOME/.claude/code-review-stamps/**)',
34
36
  ];
37
+ const USER_OWNED_DENY_ENTRY = 'Bash(rm -rf /)';
35
38
 
36
39
  function packageDenyEntries() {
37
40
  const packageSettings = JSON.parse(readFileSync(PACKAGE_SETTINGS_PATH, 'utf8'));
@@ -53,19 +56,18 @@ function runInstallerInSandbox(sandboxHome, installerArguments = []) {
53
56
  });
54
57
  }
55
58
 
56
- test('package settings.json publishes exactly the four managed deny entries', () => {
59
+ test('package settings.json publishes no managed deny entries', () => {
57
60
  const denyEntries = packageDenyEntries();
58
- assert.equal(denyEntries.length, 4);
59
61
  assert.deepEqual(denyEntries, EXPECTED_DENY_ENTRIES);
60
62
  });
61
63
 
62
64
  test('an array-valued permissions field is replaced so managed denies survive a round trip', () => {
63
65
  const targetSettings = { permissions: [] };
64
- const result = mergeManagedPermissionsIntoSettings(targetSettings, EXPECTED_DENY_ENTRIES);
65
- assert.equal(result.addedCount, EXPECTED_DENY_ENTRIES.length);
66
+ const result = mergeManagedPermissionsIntoSettings(targetSettings, SAMPLE_MANAGED_DENY_ENTRIES);
67
+ assert.equal(result.addedCount, SAMPLE_MANAGED_DENY_ENTRIES.length);
66
68
  assert.equal(Array.isArray(targetSettings.permissions), false);
67
69
  const roundTripped = JSON.parse(JSON.stringify(targetSettings));
68
- assert.deepEqual(roundTripped.permissions.deny, EXPECTED_DENY_ENTRIES);
70
+ assert.deepEqual(roundTripped.permissions.deny, SAMPLE_MANAGED_DENY_ENTRIES);
69
71
  });
70
72
 
71
73
  test('npm pack includes package settings.json in the published artifact', () => {
@@ -88,25 +90,28 @@ test('npm pack includes package settings.json in the published artifact', () =>
88
90
  );
89
91
  });
90
92
 
91
- test('mergeManagedPermissionsIntoSettings adds each package deny exactly once', () => {
93
+ test('mergeManagedPermissionsIntoSettings adds each managed deny exactly once', () => {
92
94
  const target = {
93
95
  permissions: {
94
96
  allow: ['Bash(git status)'],
95
97
  deny: ['Bash(rm -rf /)'],
96
98
  },
97
99
  };
98
- const first = mergeManagedPermissionsIntoSettings(target, EXPECTED_DENY_ENTRIES);
99
- assert.equal(first.addedCount, 4);
100
+ const first = mergeManagedPermissionsIntoSettings(target, SAMPLE_MANAGED_DENY_ENTRIES);
101
+ assert.equal(first.addedCount, SAMPLE_MANAGED_DENY_ENTRIES.length);
100
102
  assert.equal(first.alreadyPresentCount, 0);
101
- assert.equal(target.permissions.deny.filter((each) => EXPECTED_DENY_ENTRIES.includes(each)).length, 4);
103
+ const managedDenyCount = target.permissions.deny.filter((each) => (
104
+ SAMPLE_MANAGED_DENY_ENTRIES.includes(each)
105
+ )).length;
106
+ assert.equal(managedDenyCount, SAMPLE_MANAGED_DENY_ENTRIES.length);
102
107
  assert.ok(target.permissions.deny.includes('Bash(rm -rf /)'));
103
108
  assert.deepEqual(target.permissions.allow, ['Bash(git status)']);
104
109
 
105
- const second = mergeManagedPermissionsIntoSettings(target, EXPECTED_DENY_ENTRIES);
110
+ const second = mergeManagedPermissionsIntoSettings(target, SAMPLE_MANAGED_DENY_ENTRIES);
106
111
  assert.equal(second.addedCount, 0);
107
- assert.equal(second.alreadyPresentCount, 4);
112
+ assert.equal(second.alreadyPresentCount, SAMPLE_MANAGED_DENY_ENTRIES.length);
108
113
  assert.equal(
109
- target.permissions.deny.filter((each) => each === EXPECTED_DENY_ENTRIES[0]).length,
114
+ target.permissions.deny.filter((each) => each === SAMPLE_MANAGED_DENY_ENTRIES[0]).length,
110
115
  1,
111
116
  );
112
117
  });
@@ -118,18 +123,18 @@ test('pruneManagedPermissionsFromSettings removes only package-owned deny entrie
118
123
  ask: ['Edit(./**)'],
119
124
  deny: [
120
125
  'Bash(rm -rf /)',
121
- ...EXPECTED_DENY_ENTRIES,
126
+ ...SAMPLE_MANAGED_DENY_ENTRIES,
122
127
  ],
123
128
  },
124
129
  };
125
- const outcome = pruneManagedPermissionsFromSettings(target, EXPECTED_DENY_ENTRIES);
126
- assert.equal(outcome.removedCount, 4);
130
+ const outcome = pruneManagedPermissionsFromSettings(target, SAMPLE_MANAGED_DENY_ENTRIES);
131
+ assert.equal(outcome.removedCount, SAMPLE_MANAGED_DENY_ENTRIES.length);
127
132
  assert.deepEqual(target.permissions.deny, ['Bash(rm -rf /)']);
128
133
  assert.deepEqual(target.permissions.allow, ['Bash(git status)']);
129
134
  assert.deepEqual(target.permissions.ask, ['Edit(./**)']);
130
135
  });
131
136
 
132
- test('sandbox install adds all four deny entries once and is idempotent on repeat', () => {
137
+ test('sandbox install keeps default managed denies empty on repeat', () => {
133
138
  const sandboxHome = mkdtempSync(join(tmpdir(), 'cde-settings-defaults-'));
134
139
  try {
135
140
  const firstInstall = runInstallerInSandbox(sandboxHome, []);
@@ -139,30 +144,52 @@ test('sandbox install adds all four deny entries once and is idempotent on repea
139
144
  assert.ok(existsSync(settingsPath), 'settings.json written');
140
145
  const firstSettings = JSON.parse(readFileSync(settingsPath, 'utf8'));
141
146
  const firstDeny = firstSettings.permissions?.deny ?? [];
142
- for (const eachEntry of EXPECTED_DENY_ENTRIES) {
143
- assert.ok(firstDeny.includes(eachEntry), `missing deny: ${eachEntry}`);
144
- assert.equal(
145
- firstDeny.filter((candidate) => candidate === eachEntry).length,
146
- 1,
147
- `deny not unique: ${eachEntry}`,
148
- );
149
- }
147
+ assert.deepEqual(firstDeny, EXPECTED_DENY_ENTRIES);
150
148
 
151
149
  const manifestPath = join(sandboxHome, '.claude', '.claude-dev-env-manifest.json');
152
150
  const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
153
- assert.deepEqual(manifest.managedPermissions?.deny, EXPECTED_DENY_ENTRIES);
151
+ assert.deepEqual(manifest.managedPermissions?.deny ?? [], EXPECTED_DENY_ENTRIES);
154
152
 
155
153
  const secondInstall = runInstallerInSandbox(sandboxHome, []);
156
154
  assert.equal(secondInstall.status, 0, secondInstall.stderr || secondInstall.stdout);
157
155
  const secondSettings = JSON.parse(readFileSync(settingsPath, 'utf8'));
158
156
  const secondDeny = secondSettings.permissions?.deny ?? [];
159
- for (const eachEntry of EXPECTED_DENY_ENTRIES) {
160
- assert.equal(
161
- secondDeny.filter((candidate) => candidate === eachEntry).length,
162
- 1,
163
- `repeat install duplicated deny: ${eachEntry}`,
164
- );
165
- }
157
+ assert.deepEqual(secondDeny, EXPECTED_DENY_ENTRIES);
158
+ } finally {
159
+ rmSync(sandboxHome, { recursive: true, force: true });
160
+ }
161
+ });
162
+
163
+ test('a normal upgrade retires manifest-owned denies and preserves user entries', () => {
164
+ const sandboxHome = mkdtempSync(join(tmpdir(), 'cde-settings-upgrade-'));
165
+ try {
166
+ const initialInstall = runInstallerInSandbox(sandboxHome, []);
167
+ assert.equal(initialInstall.status, 0, initialInstall.stderr || initialInstall.stdout);
168
+
169
+ const settingsPath = join(sandboxHome, '.claude', 'settings.json');
170
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
171
+ settings.permissions = {
172
+ allow: ['Bash(git status)'],
173
+ ask: ['Edit(./**)'],
174
+ deny: [USER_OWNED_DENY_ENTRY, ...LEGACY_MANAGED_DENY_ENTRIES],
175
+ };
176
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 4) + '\n');
177
+
178
+ const manifestPath = join(sandboxHome, '.claude', '.claude-dev-env-manifest.json');
179
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
180
+ manifest.managedPermissions = { deny: LEGACY_MANAGED_DENY_ENTRIES };
181
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
182
+
183
+ const upgradedInstall = runInstallerInSandbox(sandboxHome, []);
184
+ assert.equal(upgradedInstall.status, 0, upgradedInstall.stderr || upgradedInstall.stdout);
185
+
186
+ const upgradedSettings = JSON.parse(readFileSync(settingsPath, 'utf8'));
187
+ assert.deepEqual(upgradedSettings.permissions?.deny, [USER_OWNED_DENY_ENTRY]);
188
+ assert.deepEqual(upgradedSettings.permissions?.allow, ['Bash(git status)']);
189
+ assert.deepEqual(upgradedSettings.permissions?.ask, ['Edit(./**)']);
190
+
191
+ const upgradedManifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
192
+ assert.equal(Object.hasOwn(upgradedManifest, 'managedPermissions'), false);
166
193
  } finally {
167
194
  rmSync(sandboxHome, { recursive: true, force: true });
168
195
  }
@@ -953,7 +953,6 @@ const OLD_FOLDED_HOOKS_SETTINGS = {
953
953
  { type: 'command', command: 'py -3 C:/Users/x/.claude/hooks/blocking/state_description_blocker.py', timeout: 10 },
954
954
  { type: 'command', command: 'py -3 C:/Users/x/.claude/hooks/blocking/subprocess_budget_completeness.py', timeout: 10 },
955
955
  { type: 'command', command: 'py -3 C:/Users/x/.claude/hooks/blocking/hook_prose_detector_consistency.py', timeout: 10 },
956
- { type: 'command', command: 'py -3 C:/Users/x/.claude/hooks/blocking/verified_commit_message_accuracy_blocker.py', timeout: 10 },
957
956
  ],
958
957
  },
959
958
  {
@@ -971,8 +970,8 @@ const OLD_FOLDED_HOOKS_SETTINGS = {
971
970
  };
972
971
 
973
972
 
974
- test('FOLDED_HOOK_RELATIVE_PATHS contains all 16 hooks removed from hooks.json plus the retired md_to_html_blocker', () => {
975
- assert.equal(FOLDED_HOOK_RELATIVE_PATHS.size, 17);
973
+ test('FOLDED_HOOK_RELATIVE_PATHS contains all 15 hooks removed from hooks.json plus the retired md_to_html_blocker', () => {
974
+ assert.equal(FOLDED_HOOK_RELATIVE_PATHS.size, 16);
976
975
  assert.ok(FOLDED_HOOK_RELATIVE_PATHS.has('blocking/write_existing_file_blocker.py'));
977
976
  assert.ok(FOLDED_HOOK_RELATIVE_PATHS.has('blocking/plain_language_blocker.py'));
978
977
  assert.ok(FOLDED_HOOK_RELATIVE_PATHS.has('blocking/code_rules_enforcer.py'));
@@ -992,7 +991,6 @@ test('FOLDED_HOOK_RELATIVE_PATHS lists every hook the PreToolUse dispatcher host
992
991
  'blocking/state_description_blocker.py',
993
992
  'blocking/subprocess_budget_completeness.py',
994
993
  'blocking/hook_prose_detector_consistency.py',
995
- 'blocking/verified_commit_message_accuracy_blocker.py',
996
994
  'blocking/workflow_substitution_slot_blocker.py',
997
995
  'blocking/claude_md_orphan_file_blocker.py',
998
996
  'blocking/env_var_table_code_drift_blocker.py',
@@ -15,6 +15,7 @@ Slash-command definitions installed into `~/.claude/commands/` by `bin/install.m
15
15
  | `pr-comments.md` | `/pr-comments` | Fetches and formats PR review comments for response |
16
16
  | `review-plan.md` | `/review-plan` | Reviews the current plan packet against code standards |
17
17
  | `right-size.md` | `/right-size` | Checks an implementation against the Right-Sized Engineering rules |
18
+ | `sr-loop.md` | `/sr-loop` | Runs the converging cleanup loop: /simplify passes until clean, then a code-review fix pass |
18
19
  | `sum.md` | `/sum` | Generates a formatted session summary for quick pickup in a new session |
19
20
 
20
21
  ## Format
@@ -0,0 +1,52 @@
1
+ ---
2
+ description: Converging cleanup loop - repeat /simplify until clean, then repeat /code-review --fix until clean
3
+ argument-hint: [PR URL, branch, or blank for the current diff]
4
+ ---
5
+
6
+ Run the converging cleanup loop on the target: `$ARGUMENTS` (blank means the
7
+ current branch's diff). Each phase invokes an existing skill with the Skill
8
+ tool and repeats it until a pass returns zero new findings.
9
+
10
+ ## Phase A — loop /simplify
11
+
12
+ 1. Invoke the `simplify` skill on the target (the same review the user gets
13
+ from `/simplify <target>`). Let it run its 4-lens fan-out and apply its
14
+ fixes.
15
+ 2. After each pass: run the scoped test suite (test files beside the touched
16
+ code, not the full repo suite), commit once (`git commit -F <file>`, body
17
+ written with the Write tool, Co-Authored-By line, 10-minute timeout — the
18
+ pre-commit gate runs its own tests), and push to the PR head branch. The PR
19
+ stays draft.
20
+ 3. Repeat the invocation. **Skips are sticky:** carry every adjudicated skip
21
+ forward into the next pass as "already adjudicated — do not re-report: ..."
22
+ context, or the loop never converges.
23
+ 4. Phase A converges when a full pass returns zero new findings, or when a
24
+ pass's new findings are all nits — doc phrasing, naming, comment altitude,
25
+ or any fix below a behavior change. On a nit-only pass: apply the fixes,
26
+ test, commit, push, and stop with no confirming pass. When the previous
27
+ pass changed only a line or two, a single combined-lens confirmation agent
28
+ may serve as the final pass.
29
+
30
+ ## Phase B — loop /code-review low --fix
31
+
32
+ 1. Invoke the `code-review` skill with arguments `low --fix` on the same
33
+ target. Let it report findings and apply its fixes.
34
+ 2. After any pass that changed files: test, commit, push as in Phase A.
35
+ 3. Repeat until a pass reports zero findings, or until a pass reports only
36
+ nits — then fix, test, commit, push, and stop with no confirming pass.
37
+ Phase B usually converges in one pass when Phase A ran first.
38
+
39
+ ## Finish
40
+
41
+ Report: passes run per phase, commits pushed with hashes, fixes applied, and
42
+ the standing skip list with reasons.
43
+
44
+ ## Constraints (observed under headless runs)
45
+
46
+ - Run this loop in the main session. A wrapper subagent cannot see the
47
+ `code-review` skill, and a nested agent's own subagent reports route to the
48
+ main session instead of back to it.
49
+ - Scope test runs to the touched files; a full-suite run can outlive one
50
+ foreground tool call.
51
+ - A hung or newly slow test suite is a finding to investigate, not an
52
+ inconvenience.
package/docs/CLAUDE.md CHANGED
@@ -11,7 +11,6 @@ Reference documentation installed into `~/.claude/docs/` by `bin/install.mjs`. T
11
11
  | `BDD_DISCOVERY_PROTOCOL.md` | Example Mapping algorithm for discovery before implementation; based on Smart & Molak *BDD in Action* §6.4 |
12
12
  | `BDD_SCENARIO_QUALITY.md` | Seven scenario quality patterns (§7.6-style catalog) |
13
13
  | `BDD_TEST_LAYOUT.md` | `describe/when/should` test layout and soap-opera personas |
14
- | `PR_DESCRIPTION_GUIDE.md` | Authoritative reference for the `pr-description-writer` agent; PR body shapes derived from a 120-PR Anthropic corpus |
15
14
  | `DJANGO_PATTERNS.md` | Django-specific coding patterns |
16
15
  | `REACT_PATTERNS.md` | React-specific coding patterns |
17
16
  | `agent-spawn-protocol.md` | Full agent-spawn protocol behind the `rules/agent-spawn-protocol.md` kernel: context-sufficiency check, `/prompt-generator` prompt crafting, and the spawn step |
@@ -7,7 +7,6 @@ Pointer documents to external sources, standard terminology, and internal tool o
7
7
  | File | Purpose |
8
8
  |---|---|
9
9
  | `dead-code-elimination.md` | External sources and standard terms behind CODE_RULES §9.8 (remove code you orphan): DCE, tree shaking, reachability analysis, and the Lava Flow anti-pattern |
10
- | `code-review-enforcement.md` | How the code-review gates work: the two required efforts (push at low, PR creation at xhigh), the stamp bound to the branch-surface hash, the single sanctioned minter, the two-layer stamp-directory guard, and the bypass surfaces the gates leave open |
11
10
  | `prose-style-enforcement.md` | How `CLAUDE_PROSE_STYLE_ENFORCEMENT` arms opinionated prose gates (default off) while AskUserQuestion lean-block stays always on |
12
11
  | `advisor-tool.md` | Canonical consult bones for any stronger reviewer: when to call, hard rule before first write, how to treat advice; maps to the Anthropic advisor tool |
13
12
  | `team-advisor-skill.md` | `/team-advisor` map: sole-consumer warm bind, ref index, and advisor selection |
package/hooks/CLAUDE.md CHANGED
@@ -8,7 +8,7 @@ Python hook scripts wired into Claude Code's lifecycle via `settings.json`. Each
8
8
  |---|---|
9
9
  | `advisory/` | Hooks that warn but do not block (`permissionDecision: "ask"`) |
10
10
  | `blocking/` | Hooks that deny tool calls when a rule is violated |
11
- | `blocking/config/` | Shared constants for the verified-commit gate family |
11
+ | `blocking/config/` | Shared constants for blocking hooks |
12
12
  | `diagnostic/` | Hooks that record and extract hook-firing records into Neon |
13
13
  | `diagnostic/migrations/` | SQL migrations for the `hook_events` Neon schema |
14
14
  | `diagnostic/queries/` | Parameterized SQL queries for inspecting blocked commands |
@@ -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