claude-dev-env 1.41.0 → 1.43.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.
- package/CLAUDE.md +8 -0
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +232 -8
- package/_shared/pr-loop/scripts/code_rules_gate.py +293 -8
- package/_shared/pr-loop/scripts/fix_hookspath.py +96 -5
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +124 -20
- package/_shared/pr-loop/scripts/post_audit_thread.py +4 -4
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +90 -0
- package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/claude_settings_keys_constants.py +2 -0
- package/_shared/pr-loop/scripts/preflight.py +13 -31
- package/_shared/pr-loop/scripts/reviews_disabled.py +2 -16
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +76 -33
- package/_shared/pr-loop/scripts/tests/conftest.py +1 -51
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +385 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +37 -2
- package/_shared/pr-loop/scripts/tests/test_claude_settings_keys_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath_constants.py +6 -2
- package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +2 -2
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1 -2
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +4 -2
- package/_shared/pr-loop/scripts/tests/test_preflight.py +17 -52
- package/_shared/pr-loop/scripts/tests/test_preflight_constants.py +6 -2
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +5 -3
- package/agents/pr-description-writer.md +50 -140
- package/docs/PR_DESCRIPTION_GUIDE.md +101 -102
- package/hooks/_gh_pr_author_swap_utils.py +1 -1
- package/hooks/blocking/bot_mention_comment_blocker.py +4 -10
- package/hooks/blocking/code_rules_enforcer.py +217 -99
- package/hooks/blocking/code_rules_path_utils.py +8 -1
- package/hooks/blocking/destructive_command_blocker.py +1 -1
- package/hooks/blocking/es_exe_path_rewriter.py +7 -13
- package/hooks/blocking/gh_body_arg_blocker.py +6 -1
- package/hooks/blocking/gh_pr_author_enforcer.py +5 -5
- package/hooks/blocking/gh_pr_author_restore.py +5 -5
- package/hooks/blocking/hedging_language_blocker.py +4 -10
- package/hooks/blocking/md_path_exemptions.py +205 -0
- package/hooks/blocking/md_to_html_blocker.py +48 -20
- package/hooks/blocking/pr_converge_bugteam_enforcer.py +5 -11
- package/hooks/blocking/pr_description_enforcer.py +626 -41
- package/hooks/blocking/question_to_user_enforcer.py +4 -10
- package/hooks/blocking/state_description_blocker.py +6 -12
- package/hooks/blocking/tdd_enforcer.py +1 -1
- package/hooks/blocking/test_bot_mention_comment_blocker.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer.py +3 -3
- package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +1 -1
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -2
- package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +184 -0
- package/hooks/blocking/test_code_rules_enforcer_type_checking_scope.py +82 -0
- package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +29 -29
- package/hooks/blocking/test_gh_body_arg_blocker.py +7 -8
- package/hooks/blocking/test_gh_pr_author_enforcer.py +1 -1
- package/hooks/blocking/test_gh_pr_author_restore.py +1 -1
- package/hooks/blocking/test_hedging_language_blocker.py +2 -2
- package/hooks/blocking/test_md_to_html_blocker.py +463 -8
- package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +1 -1
- package/hooks/blocking/test_pr_description_enforcer.py +1210 -13
- package/hooks/blocking/test_question_to_user_enforcer.py +1 -1
- package/hooks/blocking/windows_rmtree_blocker.py +5 -11
- package/hooks/diagnostic/hook_log_extractor.py +1 -1
- package/hooks/diagnostic/hook_log_init.py +1 -1
- package/hooks/diagnostic/hook_log_stop_wrapper.py +1 -1
- package/hooks/diagnostic/test_hook_log_extractor.py +1 -1
- package/hooks/diagnostic/test_hook_log_init.py +2 -2
- package/hooks/diagnostic/test_hook_log_stop_wrapper.py +1 -1
- package/hooks/git-hooks/gate_utils.py +1 -1
- package/hooks/git-hooks/pre_commit.py +1 -1
- package/hooks/git-hooks/pre_push.py +1 -1
- package/hooks/git-hooks/test_config.py +5 -5
- package/hooks/git-hooks/test_pre_push.py +6 -6
- package/hooks/{config → hooks_constants}/code_rules_enforcer_constants.py +37 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +28 -0
- package/hooks/hooks_constants/md_to_html_blocker_constants.py +82 -0
- package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_state.py +1 -1
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +154 -0
- package/hooks/{config → hooks_constants}/pre_tool_use_stdin.py +1 -1
- package/hooks/{config → hooks_constants}/project_paths_reader.py +2 -2
- package/hooks/{config → hooks_constants}/test_banned_identifiers_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_dynamic_stderr_handler.py +1 -1
- package/hooks/{config → hooks_constants}/test_hardcoded_user_path_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_hook_log_extractor_constants.py +2 -2
- package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +110 -0
- package/hooks/{config → hooks_constants}/test_messages.py +2 -6
- package/hooks/{config → hooks_constants}/test_path_rewriter_constants.py +1 -1
- package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +292 -0
- package/hooks/{config → hooks_constants}/test_pre_tool_use_stdin.py +2 -2
- package/hooks/{config → hooks_constants}/test_project_paths_reader.py +3 -3
- package/hooks/{config → hooks_constants}/test_session_env_cleanup_constants.py +1 -1
- package/hooks/{config → hooks_constants}/test_setup_project_paths_constants.py +2 -2
- package/hooks/{config → hooks_constants}/test_unused_module_import_constants.py +1 -1
- package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +5 -11
- package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +1 -1
- package/hooks/session/gh_pr_author_session_cleanup.py +5 -6
- package/hooks/session/session_env_cleanup.py +4 -10
- package/hooks/session/test_gh_pr_author_session_cleanup.py +1 -1
- package/hooks/session/test_untracked_repo_detector.py +2 -2
- package/hooks/session/untracked_repo_detector.py +6 -12
- package/hooks/test__gh_pr_author_swap_utils.py +1 -1
- package/hooks/validators/run_all_validators.py +16 -5
- package/hooks/validators/test_output_formatter.py +46 -0
- package/hooks/workflow/doc_gist_auto_publish.py +1 -1
- package/hooks/workflow/md_to_html_companion.py +8 -15
- package/hooks/workflow/test_md_to_html_companion.py +184 -23
- package/package.json +1 -1
- package/rules/ask-user-question-required.md +1 -1
- package/rules/vault-context.md +1 -1
- package/scripts/{config → dev_env_scripts_constants}/timing.py +1 -1
- package/scripts/setup_project_paths.py +49 -11
- package/scripts/sweep_empty_dirs.py +10 -1
- package/scripts/test_setup_project_paths.py +2 -2
- package/scripts/test_sweep_empty_dirs.py +2 -6
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +1 -1
- package/skills/_shared/pr-loop/scripts/init_loop_state.py +1 -1
- package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +1 -1
- package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +2 -2
- package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +2 -2
- package/skills/bugteam/PROMPTS.md +1 -1
- package/skills/bugteam/SKILL.md +1 -1
- package/skills/bugteam/reference/github-pr-reviews.md +1 -1
- package/skills/bugteam/scripts/{_claude_permissions_common.py → _bugteam_permissions_common.py} +110 -13
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +1 -13
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +1 -16
- package/skills/bugteam/scripts/bugteam_preflight.py +1 -13
- package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +69 -0
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +117 -12
- package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +1 -1
- package/skills/bugteam/scripts/reflow_skill_md.py +1 -1
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +71 -25
- package/skills/bugteam/scripts/{test__claude_permissions_common.py → test__bugteam_permissions_common.py} +4 -4
- package/skills/bugteam/scripts/test_agent_config_carveout.py +356 -0
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -26
- package/skills/bugteam/scripts/{test_claude_permissions_common.py → test_bugteam_permissions_common.py} +3 -66
- package/skills/bugteam/scripts/test_bugteam_preflight.py +2 -27
- package/skills/bugteam/scripts/windows_safe_rmtree.py +1 -1
- package/skills/doc-gist/SKILL.md +1 -1
- package/skills/doc-gist/scripts/gist_upload.py +1 -1
- package/skills/implement/SKILL.md +66 -0
- package/skills/implement/scripts/append_note.py +133 -0
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +12 -0
- package/skills/implement/scripts/test_append_note.py +191 -0
- package/skills/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
- package/skills/pr-converge/{config → pr_converge_skill_constants}/constants.py +6 -1
- package/skills/pr-converge/scripts/check_bugbot_ci.py +2 -2
- package/skills/pr-converge/scripts/check_convergence.py +175 -29
- package/skills/pr-converge/scripts/check_pending_reviews.py +2 -2
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +2 -2
- package/skills/pr-converge/scripts/post_fix_reply.py +2 -2
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
- package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/pr_converge_constants.py +1 -1
- package/skills/pr-converge/scripts/reflow_skill_md.py +90 -16
- package/skills/pr-converge/scripts/test_check_bugbot_ci.py +1 -1
- package/skills/pr-converge/scripts/test_check_convergence.py +324 -0
- package/skills/pr-converge/scripts/test_reflow_skill_md.py +0 -31
- package/skills/refine/SKILL.md +257 -0
- package/skills/refine/templates/implementation-notes-template.html +56 -0
- package/skills/refine/templates/plan-template.md +60 -0
- package/skills/session-log/SKILL.md +98 -233
- package/_shared/pr-loop/scripts/config/claude_permissions_constants.py +0 -36
- package/hooks/config/pr_description_enforcer_constants.py +0 -19
- package/hooks/config/test_pr_description_enforcer_constants.py +0 -82
- package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +0 -20
- package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +0 -55
- package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +0 -55
- package/skills/pr-converge/scripts/evict_cached_config_modules.py +0 -20
- package/skills/pr-converge/scripts/test_evict_cached_config_modules.py +0 -22
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/__init__.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/code_rules_gate_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/fix_hookspath_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/post_audit_thread_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/preflight_constants.py +0 -0
- /package/_shared/pr-loop/scripts/{config → pr_loop_shared_constants}/reviews_disabled_constants.py +0 -0
- /package/hooks/git-hooks/{config.py → git_hooks_constants/__init__.py} +0 -0
- /package/hooks/{config → hooks_constants}/__init__.py +0 -0
- /package/hooks/{config → hooks_constants}/any_type_config.py +0 -0
- /package/hooks/{config → hooks_constants}/banned_identifiers_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/blocking_check_limits.py +0 -0
- /package/hooks/{config → hooks_constants}/bot_mention_comment_blocker_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/convergence_branch_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/doc_gist_auto_publish_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/dynamic_stderr_handler.py +0 -0
- /package/hooks/{config → hooks_constants}/gh_pr_author_swap_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/hardcoded_user_path_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/hook_log_extractor_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/html_companion_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/inline_tuple_string_magic_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/messages.py +0 -0
- /package/hooks/{config → hooks_constants}/path_rewriter_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/pr_converge_bugteam_enforcer_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/session_env_cleanup_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/setup_project_paths_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/state_description_blocker_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/stuttering_check_config.py +0 -0
- /package/hooks/{config → hooks_constants}/stuttering_import_binding_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/sys_path_insert_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/unused_module_import_constants.py +0 -0
- /package/hooks/{config → hooks_constants}/windows_rmtree_blocker_constants.py +0 -0
- /package/{skills/_shared/pr-loop/scripts/config → hooks/lifecycle}/__init__.py +0 -0
- /package/{skills/bugteam/scripts/config → hooks/session}/__init__.py +0 -0
- /package/scripts/{config → dev_env_scripts_constants}/__init__.py +0 -0
- /package/skills/{doc-gist/scripts/config → _shared/pr-loop/scripts/skills_pr_loop_constants}/__init__.py +0 -0
- /package/skills/_shared/pr-loop/scripts/{config → skills_pr_loop_constants}/path_resolver_constants.py +0 -0
- /package/skills/{pr-converge/config → bugteam/scripts/bugteam_scripts_constants}/__init__.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_code_rules_gate_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_fix_hookspath_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/bugteam_preflight_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/probe_code_rules_enforcer_check_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/reflow_skill_md_constants.py +0 -0
- /package/skills/bugteam/scripts/{config → bugteam_scripts_constants}/windows_safe_rmtree_constants.py +0 -0
- /package/skills/{pr-converge/scripts/config → doc-gist/scripts/doc_gist_scripts_constants}/__init__.py +0 -0
- /package/skills/doc-gist/scripts/{config → doc_gist_scripts_constants}/gist_upload_constants.py +0 -0
- /package/skills/pr-converge/scripts/{config → pr_converge_scripts_constants}/reflow_skill_md_constants.py +0 -0
|
@@ -6,9 +6,13 @@ from types import ModuleType
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def _load_constants_module() -> ModuleType:
|
|
9
|
-
module_path =
|
|
9
|
+
module_path = (
|
|
10
|
+
Path(__file__).parent.parent
|
|
11
|
+
/ "pr_loop_shared_constants"
|
|
12
|
+
/ "preflight_constants.py"
|
|
13
|
+
)
|
|
10
14
|
specification = importlib.util.spec_from_file_location(
|
|
11
|
-
"
|
|
15
|
+
"pr_loop_shared_constants.preflight_constants", module_path
|
|
12
16
|
)
|
|
13
17
|
assert specification is not None
|
|
14
18
|
assert specification.loader is not None
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Smoke tests for revoke_project_claude_permissions wiring.
|
|
2
2
|
|
|
3
3
|
Confirms the module imports cleanly with the constants now sourced from
|
|
4
|
-
|
|
4
|
+
pr_loop_shared_constants/claude_permissions_constants.py and
|
|
5
|
+
pr_loop_shared_constants/claude_settings_keys_constants.py.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
from __future__ import annotations
|
|
@@ -17,7 +18,6 @@ def _load_revoke_module() -> ModuleType:
|
|
|
17
18
|
parent_directory = str(scripts_directory.resolve())
|
|
18
19
|
if parent_directory not in sys.path:
|
|
19
20
|
sys.path.insert(0, parent_directory)
|
|
20
|
-
sys.modules.pop("config", None)
|
|
21
21
|
module_path = scripts_directory / "revoke_project_claude_permissions.py"
|
|
22
22
|
specification = importlib.util.spec_from_file_location(
|
|
23
23
|
"revoke_project_claude_permissions", module_path
|
|
@@ -32,7 +32,9 @@ def _load_revoke_module() -> ModuleType:
|
|
|
32
32
|
def test_module_imports_constants_from_config_modules() -> None:
|
|
33
33
|
revoke_module = _load_revoke_module()
|
|
34
34
|
assert revoke_module.ALL_PERMISSION_ALLOW_TOOLS == ("Edit", "Write", "Read")
|
|
35
|
-
assert
|
|
35
|
+
assert revoke_module.AUTO_MODE_ENVIRONMENT_ENTRY_PREFIX == (
|
|
36
|
+
"Trusted local workspace:"
|
|
37
|
+
)
|
|
36
38
|
assert revoke_module.CLAUDE_SETTINGS_PERMISSIONS_KEY == "permissions"
|
|
37
39
|
|
|
38
40
|
|
|
@@ -1,185 +1,95 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pr-description-writer
|
|
3
|
-
description: "MANDATORY agent for
|
|
3
|
+
description: "MANDATORY agent for PR descriptions and PR comments. Enforced by the `pr_description_enforcer` PreToolUse hook on `gh pr create`, `gh pr edit`, and `gh pr comment`. 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`."
|
|
4
4
|
tools: Read,Grep,Glob,Bash
|
|
5
5
|
model: haiku
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# PR Description Writer
|
|
9
9
|
|
|
10
|
-
You
|
|
10
|
+
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.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## The three shapes
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
- Sizing (Trivial / Standard / Heavy)
|
|
16
|
-
- Shape 1: Trivial (sectionless one-liner)
|
|
17
|
-
- Shape 2: Standard (intro + Changes + Test plan)
|
|
18
|
-
- Shape 3: Heavy (intro + Problem + Fix + Verification + optional)
|
|
19
|
-
- File reference style
|
|
20
|
-
- Cross-references
|
|
21
|
-
- Markers and footers
|
|
22
|
-
- Commit messages
|
|
23
|
-
- Gotchas
|
|
24
|
-
- Refusals
|
|
14
|
+
Choose one shape per body. Mixing shapes signals confusion to the reviewer.
|
|
25
15
|
|
|
26
|
-
|
|
16
|
+
### Trivial — diff ≤ 10 lines
|
|
27
17
|
|
|
28
|
-
|
|
18
|
+
One to three sentences of prose. Zero headers. No `## Summary`, no `## Test plan`. The opening verb describes the change directly.
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- [ ] Inspect the diff: `git diff <base>...HEAD --stat`, then `git diff <base>...HEAD` for any file whose purpose isn't obvious from the path.
|
|
33
|
-
- [ ] If the branch name or any commit mentions an issue (`fix-1311`, `Fixes #1311`), read it: `gh issue view 1311`.
|
|
34
|
-
- [ ] Pick the shape from the Sizing table.
|
|
35
|
-
- [ ] Write the body in that shape. Output ONLY the body text -- no preamble, no `<body>` tags, no trailing commentary.
|
|
36
|
-
|
|
37
|
-
## Sizing
|
|
38
|
-
|
|
39
|
-
| Signal | Shape |
|
|
40
|
-
|---|---|
|
|
41
|
-
| 1-3 files, mechanical change (pin bump, link fix, typo, single-line config), no behavior change | **Trivial** |
|
|
42
|
-
| Behavior change, bug fix, small feature; under ~15 files | **Standard** |
|
|
43
|
-
| New subsystem, refactor across many files, schema or contract change, anything with a caveat | **Heavy** |
|
|
44
|
-
|
|
45
|
-
Prefer the smaller shape when borderline. Anthropic authors prefer the smaller shape.
|
|
46
|
-
|
|
47
|
-
## Shape 1: Trivial
|
|
48
|
-
|
|
49
|
-
One declarative sentence. No Markdown headers. Optional `Fixes #N` line.
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
Pin third-party GitHub Actions references to immutable commit SHAs.
|
|
20
|
+
```markdown
|
|
21
|
+
Bump bun to 1.3.14. Picks up the bugfix for the runtime panic on empty stdin.
|
|
53
22
|
```
|
|
54
23
|
|
|
55
|
-
|
|
56
|
-
Bump pinned Bun from 1.3.6 to 1.3.14.
|
|
57
|
-
|
|
58
|
-
Fixes #1311.
|
|
59
|
-
```
|
|
24
|
+
### Standard — diff 11–500 lines
|
|
60
25
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
<One short intro paragraph stating the change and why it matters.
|
|
65
|
-
Reference the failure mode or user-visible symptom when there is one.>
|
|
26
|
+
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.
|
|
66
27
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
## Changes
|
|
70
|
-
|
|
71
|
-
- `path/to/file.ext`: short clause describing the change
|
|
72
|
-
- `path/to/other.ext`: short clause
|
|
73
|
-
- `tests/foo.test.ts`: 2 new cases for X
|
|
28
|
+
```markdown
|
|
29
|
+
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.
|
|
74
30
|
|
|
75
31
|
## Test plan
|
|
76
32
|
|
|
77
|
-
- `
|
|
78
|
-
-
|
|
79
|
-
- Manual: reproduce on a branch named `feature/a,b`; confirm no rejection
|
|
33
|
+
- [ ] `pytest packages/claude-dev-env/hooks/blocking/test_pr_description_enforcer.py`
|
|
34
|
+
- [ ] Open a draft PR with a 45-word sentence and confirm the metric block fires
|
|
80
35
|
```
|
|
81
36
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
<Two- to four-sentence intro: scope, motivation, user-visible effect.
|
|
86
|
-
Link to the prior PR or issue that motivates this one if applicable.>
|
|
37
|
+
### Heavy — diff > 500 lines, or a cross-cutting bug fix
|
|
87
38
|
|
|
88
|
-
|
|
39
|
+
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.
|
|
89
40
|
|
|
41
|
+
```markdown
|
|
90
42
|
## Problem
|
|
91
43
|
|
|
92
|
-
|
|
93
|
-
error text or a reproduction in a fenced code block when it helps.>
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
<error or reproduction>
|
|
97
|
-
```
|
|
44
|
+
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.
|
|
98
45
|
|
|
99
46
|
## Fix
|
|
100
47
|
|
|
101
|
-
|
|
102
|
-
Reference the file or function by path. Don't restate the diff
|
|
103
|
-
line-by-line.>
|
|
104
|
-
|
|
105
|
-
- `src/path/file.ts`: brief description
|
|
106
|
-
- `src/path/other.ts`: brief description
|
|
107
|
-
|
|
108
|
-
## Verification
|
|
48
|
+
Routes every `gh api .../reviews` and `.../comments` call through `--paginate --slurp | jq` so the cross-page filter sees the full set.
|
|
109
49
|
|
|
110
|
-
|
|
111
|
-
- Command 2 (with output count when useful: "666 pass, 0 fail")
|
|
112
|
-
- Manual scenarios walked through
|
|
113
|
-
|
|
114
|
-
## Caveat
|
|
50
|
+
## Test plan
|
|
115
51
|
|
|
116
|
-
|
|
117
|
-
|
|
52
|
+
- [ ] Mock paginated API and assert `jq` filter operates on the merged stream
|
|
53
|
+
- [ ] Replay PR #467 review history and confirm the late bugbot comment surfaces
|
|
118
54
|
```
|
|
119
55
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
## File reference style
|
|
123
|
-
|
|
124
|
-
- Backtick file paths: `` `src/github/operations/branch.ts` ``.
|
|
125
|
-
- Use the full path from repo root unless the basename is unambiguous within the PR.
|
|
126
|
-
- Per-file change bullets lead with the backticked path and a colon:
|
|
127
|
-
- `` `src/foo.ts`: whitelists `,` in branch names ``
|
|
128
|
-
- When one file is the centerpiece, bold the backticked filename: `` **`branch.ts`**: ... ``.
|
|
56
|
+
## Style rules
|
|
129
57
|
|
|
130
|
-
|
|
58
|
+
- Open with an imperative verb: `Adds`, `Fixes`, `Updates`, `Removes`, `Ports`, `Tightens`. 51% of prose-opening Anthropic PRs follow this pattern.
|
|
59
|
+
- 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.
|
|
60
|
+
- Backticked identifiers (`function_name`, `--flag`, `CONST`) belong in intro prose. Anthropic PRs use them routinely.
|
|
61
|
+
- Em-dashes — like these — work as parenthetical separators. 48% of corpus PRs use them.
|
|
62
|
+
- Focus on WHY. The diff shows WHAT.
|
|
63
|
+
- No code snippets in the description body. Reviewers read the diff for code.
|
|
64
|
+
- No implementation-detail dumping. `Add a 5-second timeout` beats ``Add `pullStartedAt: Date.now()` parameter to the `runPull()` callback``.
|
|
65
|
+
- No filler. Strike `In this PR I have made the following changes:` and start with the action.
|
|
131
66
|
|
|
132
|
-
|
|
133
|
-
- `Fixes #N` and `Closes #N` close the issue on merge -- pick one and use it deliberately.
|
|
134
|
-
- `Linear: CC-1723` on its own line.
|
|
135
|
-
- "Follow-up to #<n>" / "Same change as <repo>#<n>" -- short orientation one-liners are welcome.
|
|
136
|
-
|
|
137
|
-
## Markers and footers
|
|
138
|
-
|
|
139
|
-
- `<!-- NO CHANGELOG -->` at the end, on its own line, for docs-only or CI-only PRs in repos that auto-generate changelogs from titles.
|
|
140
|
-
- No "Generated with Claude Code" footer. Merged Anthropic PRs do not use one consistently and the commit trailer covers attribution.
|
|
141
|
-
|
|
142
|
-
## Commit messages
|
|
67
|
+
## Gotchas
|
|
143
68
|
|
|
144
|
-
|
|
69
|
+
- **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.
|
|
70
|
+
- **Trivial shape with `## Summary` header.** A four-line body wrapped in `## Summary` trips the ceremony-on-Trivial check. Keep tiny bodies as plain prose.
|
|
71
|
+
- **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.
|
|
72
|
+
- **`This PR` opening on any shape.** Hard block regardless of size.
|
|
73
|
+
- **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.
|
|
145
74
|
|
|
146
|
-
|
|
147
|
-
- Blank line.
|
|
148
|
-
- Body: one short paragraph stating the Why and the Fix together. Reference the issue when relevant.
|
|
149
|
-
- Skip the body entirely for trivial commits whose first line says everything.
|
|
75
|
+
## Readability targets
|
|
150
76
|
|
|
151
|
-
|
|
152
|
-
fix: allow , in branch names
|
|
77
|
+
The enforcer hook computes a Flesch Reading Ease score plus sentence-length metrics on the intro paragraph and the first body section combined.
|
|
153
78
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
Fixes #1300.
|
|
161
|
-
```
|
|
79
|
+
| Metric | Target |
|
|
80
|
+
|---|---|
|
|
81
|
+
| Longest sentence | ≤ 28 words |
|
|
82
|
+
| Average sentence | ≤ 18 words |
|
|
83
|
+
| Flesch Reading Ease | ≥ 50 |
|
|
162
84
|
|
|
163
|
-
|
|
85
|
+
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".
|
|
164
86
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
- **Don't restate the PR title as the body's first line.** The title is already displayed. Start with the *consequence* the reader cares about.
|
|
168
|
-
- **Don't add `## Why` over a single-paragraph intro.** A header on one paragraph reads as ceremony. The unmarked intro paragraph IS the Why.
|
|
169
|
-
- **Don't add a "Generated with Claude Code" footer.** Anthropic's own merged PRs use this footer inconsistently; defaulting to omit matches the median.
|
|
170
|
-
- **Don't write second-person commentary to the reviewer.** "Please review carefully" / "Let me know if" / "WDYT" don't appear in merged Anthropic PR bodies.
|
|
171
|
-
- **Don't restate the diff in `## Changes`.** Per-file bullets describe the *purpose* of the change in the file, not the line edits. The reviewer reads the diff.
|
|
172
|
-
- **Don't put verification commands in `## Changes`.** They go in `## Test plan` / `## Verification`. Mixing them obscures both.
|
|
173
|
-
- **Don't bold a filename without backticks.** Filenames are code; the canonical form is `` **`branch.ts`**: ... ``, never `**branch.ts**:`.
|
|
174
|
-
- **Don't mix `Fixes #N` and `Closes #N` within one body.** Both close the linked issue on merge -- pick one verb per PR.
|
|
175
|
-
- **Don't add empty section headers.** If `## Caveat` would be empty, drop it. Headers exist to organize content, not to satisfy a template.
|
|
176
|
-
- **Don't hedge.** "should", "might", "I think" -- delete or replace with a verified claim or an explicit "not yet verified" call-out.
|
|
177
|
-
- **Trivial PRs need no sections.** Resist the urge to add `## Summary` over a one-sentence body. The hook does not require headers; the style does not invite them.
|
|
178
|
-
- **The hook permits sectionless bodies.** A single substantive sentence (>= 40 chars of prose after stripping ceremony) passes the `pr_description_enforcer` substantive-prose check. Don't add headers to placate the hook -- the hook isn't asking for them.
|
|
87
|
+
Hit the targets on first attempt by writing short sentences in common Anglo-Saxon words. The average tracks the corpus median of 14.5.
|
|
179
88
|
|
|
180
89
|
## Refusals
|
|
181
90
|
|
|
182
|
-
|
|
91
|
+
Decline to write a body when:
|
|
183
92
|
|
|
184
|
-
-
|
|
185
|
-
-
|
|
93
|
+
- The PR has zero diff lines (genuinely empty).
|
|
94
|
+
- The user describes intent that contradicts the diff (e.g., "describe this as a bug fix" when the diff adds a feature).
|
|
95
|
+
- The user requests a shape that violates the hook (e.g., `## Summary` on a one-line bump). Offer the correct shape.
|
|
@@ -1,158 +1,157 @@
|
|
|
1
1
|
# PR Description Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Authoritative reference for the `pr-description-writer` agent and the `pr_description_enforcer` PreToolUse hook. PR bodies that match this guide pass the enforcer on first attempt.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Anthropic style basis
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The shape rules and header vocabulary derive from a 120-PR sample. Sources: `anthropics/claude-code` (40 PRs), `anthropics/claude-code-action` (40 PRs), and `anthropics/claude-code-sdk-python` (40 PRs). The corpus was sampled from merged PRs.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|---|---|
|
|
11
|
-
| 1-3 files, mechanical change (pin bump, link fix, typo, single-line config), no behavior change | **Trivial** -- one declarative sentence, no headers |
|
|
12
|
-
| Behavior change, bug fix, small feature; under ~15 files | **Standard** -- intro paragraph + `## Changes` + `## Test plan` (or `## Validation`) |
|
|
13
|
-
| New subsystem, refactor across many files, schema or contract change, anything with a caveat | **Heavy** -- intro + `## Problem` + `## Fix` (or `## Changes`) + `## Verification` + extra sections as needed |
|
|
9
|
+
Key signals from the corpus:
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
- **Shape distribution.** Trivial (≤ 10 lines): 32.5% — median body 288 chars. Small (11–100 lines): 41.7% — median 1,105 chars. Medium (101–500 lines): 20.0% — median 940 chars. Large (> 500 lines): 5.8% — median 2,441 chars.
|
|
12
|
+
- **Modal headers.** `## Summary` 43, `## Problem` 20, `## Test plan` 20, `## Fix` 18, `## Changes` 14, `## Tests` 11, `## Testing` 10, `## Root cause` 5, `## Approach` 2.
|
|
13
|
+
- **Opening style.** 46.7% open with a header, 53.3% with an unmarked paragraph. 51% of prose-opening Small/Medium PRs open with an imperative verb. `This PR` appears in 1 of 120 PRs.
|
|
14
|
+
- **Issue references.** 27.5% of PRs use `Fixes #N`, `Closes #N`, or `Resolves #N`.
|
|
15
|
+
- **Sentence length.** First-paragraph mean 15.2 words; median 14.5. Sentences over 28 words are uncommon.
|
|
16
|
+
- **Em-dashes.** Appear in 48% of bodies as parenthetical separators.
|
|
17
|
+
- **Backtick identifiers in intros.** Routine — filenames, function names, env vars, and CLI flags appear in opening paragraphs.
|
|
16
18
|
|
|
17
|
-
##
|
|
19
|
+
## The three shapes
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
### Trivial
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
- **Guidance.** Diff ≤ 10 lines changed (the agent picks shape by diff size; the hook cannot see the diff).
|
|
24
|
+
- **Hook enforcement.** Substantive prose under `TRIVIAL_BODY_CHAR_THRESHOLD` (200 chars). The hook blocks any ATX heading at any depth (`#`, `##`, `###`, ...) in a Trivial-sized body — the ceremony-on-Trivial check uses `HEADING_LINE_PATTERN`, not just `##`.
|
|
25
|
+
- **Body.** 1–3 sentences of prose. Zero headings of any level.
|
|
26
|
+
- **Forbidden.** Any heading (`# Anything`, `## Summary`, `### Detail`, ...). Triggers the hook's ceremony-on-Trivial check.
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
Bump pinned Bun from 1.3.6 to 1.3.14.
|
|
28
|
+
Example:
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
```markdown
|
|
31
|
+
Bump bun to 1.3.14. Picks up the bugfix for the runtime panic on empty stdin.
|
|
29
32
|
```
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
<One short intro paragraph stating the change and why it matters.
|
|
35
|
-
Reference the failure mode or user-visible symptom when there is one.>
|
|
34
|
+
### Standard
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
- **Guidance.** Diff 11–500 lines (agent-side; hook infers shape from body length).
|
|
37
|
+
- **Hook enforcement.** Substantive prose between `TRIVIAL_BODY_CHAR_THRESHOLD` (200) and `HEAVY_MIN_BODY_CHARS_FOR_CLASSIFICATION` (500). No required headers.
|
|
38
|
+
- **Body.** Imperative-verb intro paragraph. Optional headers drawn from the Anthropic set.
|
|
39
|
+
- **Optional headers.** `## Summary`, `## Problem`, `## Fix`, `## Changes`, `## Test plan`, `## Tests`, `## Testing`, `## Approach`, `## Root cause`.
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
Example:
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
- `tests/foo.test.ts`: 2 new cases for X
|
|
43
|
+
```markdown
|
|
44
|
+
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.
|
|
44
45
|
|
|
45
46
|
## Test plan
|
|
46
47
|
|
|
47
|
-
- `
|
|
48
|
-
-
|
|
49
|
-
- Manual: reproduce on a branch named `feature/a,b`; confirm no rejection
|
|
48
|
+
- [ ] `pytest packages/claude-dev-env/hooks/blocking/test_pr_description_enforcer.py`
|
|
49
|
+
- [ ] Open a draft PR with a 45-word sentence and confirm the metric block fires
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
### Heavy
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
- **Criterion.** Diff > 500 lines, or a cross-cutting bug fix that touches multiple subsystems.
|
|
55
|
+
- **Body.** At least one of `## Problem` or `## Summary`. At least one of `## Test plan`, `## Testing`, `## Tests`, `## Verification`, or `## Validation`. Plus any additional Anthropic headers the change earns.
|
|
56
|
+
- **Hook enforcement.** Missing either required category triggers a Heavy-required-headers block message naming the absent category.
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
<Two- to four-sentence intro: scope, motivation, user-visible effect.
|
|
58
|
-
Link to the prior PR or issue that motivates this one if applicable.>
|
|
59
|
-
|
|
60
|
-
Fixes #<n>.
|
|
58
|
+
Example:
|
|
61
59
|
|
|
60
|
+
```markdown
|
|
62
61
|
## Problem
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
error text, a reproduction, or the symptomatic log line in a fenced
|
|
66
|
-
code block when it helps.>
|
|
63
|
+
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.
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
## Fix
|
|
66
|
+
|
|
67
|
+
Routes every `gh api .../reviews` and `.../comments` call through `--paginate --slurp | jq` so the cross-page filter sees the full set.
|
|
68
|
+
|
|
69
|
+
## Test plan
|
|
70
|
+
|
|
71
|
+
- [ ] Mock paginated API and assert `jq` filter operates on the merged stream
|
|
72
|
+
- [ ] Replay PR #467 review history and confirm the late bugbot comment surfaces
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
##
|
|
75
|
+
## Header vocabulary
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
| Header | Corpus count | Typical use |
|
|
78
|
+
|---|---:|---|
|
|
79
|
+
| `## Summary` | 43 | High-level overview, often two or three sentences |
|
|
80
|
+
| `## Problem` | 20 | Bug context — what broke, who hit it |
|
|
81
|
+
| `## Test plan` | 20 | Reviewer checklist of verification steps |
|
|
82
|
+
| `## Fix` | 18 | How the change addresses the problem |
|
|
83
|
+
| `## Changes` | 14 | Bulleted catalog of code-level updates |
|
|
84
|
+
| `## Tests` | 11 | New or expanded test coverage |
|
|
85
|
+
| `## Testing` | 10 | Manual or CI verification notes |
|
|
86
|
+
| `## Root cause` | 5 | Underlying defect analysis |
|
|
87
|
+
| `## Approach` | 2 | Design rationale for non-obvious solutions |
|
|
77
88
|
|
|
78
|
-
-
|
|
79
|
-
- `src/path/other.ts`: brief description
|
|
89
|
+
`## Test plan` and `## Root cause` use sentence-case in the corpus. The enforcer regex matches case-insensitively.
|
|
80
90
|
|
|
81
|
-
##
|
|
91
|
+
## Readability targets
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
- Command 2 (with output count when useful: "666 pass, 0 fail")
|
|
85
|
-
- Manual scenarios walked through
|
|
93
|
+
The enforcer measures three metrics on the intro paragraph and first body section combined.
|
|
86
94
|
|
|
87
|
-
|
|
95
|
+
| Metric | Target |
|
|
96
|
+
|---|---|
|
|
97
|
+
| Longest sentence | ≤ 28 words |
|
|
98
|
+
| Average sentence | ≤ 18 words |
|
|
99
|
+
| Flesch Reading Ease | ≥ 50 |
|
|
88
100
|
|
|
89
|
-
|
|
90
|
-
the diff. Omit this section when there is no caveat.>
|
|
91
|
-
```
|
|
101
|
+
The Flesch score uses `206.835 - 1.015 × (words/sentences) - 84.6 × (syllables/words)`. The hook implements the formula in pure stdlib with a vowel-group syllable heuristic.
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
Hit the targets by writing short sentences in common Anglo-Saxon words. The corpus first-paragraph average of 14.5 words is the target to beat.
|
|
94
104
|
|
|
95
|
-
|
|
96
|
-
- `## Components` -- a small table when the PR introduces multiple named artifacts.
|
|
97
|
-
- `## Backward compatibility` -- when an older consumer might still hit this code path.
|
|
98
|
-
- `## Context` -- background a reviewer outside the area would need.
|
|
105
|
+
## Escape hatch
|
|
99
106
|
|
|
100
|
-
|
|
107
|
+
The hook tracks a per-user readability strike counter at `~/.claude/state/pr_description_readability_strikes.json`. Counter increments on every triggering violation. The first two failures emit metric-specific block messages. The third triggering failure fires the escape-hatch message with four recovery actions.
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
### Action 1 — loosen thresholds 10%
|
|
103
110
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
| The failure being fixed | `## Problem` -- or no header when the intro paragraph carries it |
|
|
108
|
-
| The change itself | `## Changes` or `## Fix` |
|
|
109
|
-
| How it was verified | `## Test plan`, `## Validation`, `## Verification`, or `## Testing` |
|
|
110
|
-
| Things to know | `## Caveat`, `## Runtime behavior`, `## Backward compatibility`, `## Context` |
|
|
111
|
+
```bash
|
|
112
|
+
python <enforcer-path> --readability-loosen
|
|
113
|
+
```
|
|
111
114
|
|
|
112
|
-
|
|
115
|
+
Scales the three thresholds. Flesch floor × 0.9 (rounded down). Max-sentence ceiling × 10/9 (rounded up). Avg-sentence ceiling × 10/9 (rounded up). Cascades on repeat — the second loosen applies the same scaling to the already-loosened values.
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
- Use the full path from repo root, not just the basename, unless the basename is unambiguous within the PR.
|
|
116
|
-
- Bullet lists describing per-file changes lead with the backticked path and a colon:
|
|
117
|
-
- `` `src/foo.ts`: whitelists `,` in branch names ``
|
|
118
|
-
- `` `test/foo.test.ts`: 3 new cases for comma-bearing branches ``
|
|
119
|
-
- Prose calling out a single primary file bolds the backticked filename plus a colon: `` **`branch.ts`**: ... ``.
|
|
117
|
+
Caps:
|
|
120
118
|
|
|
121
|
-
|
|
119
|
+
- Max 3 successive loosens (`READABILITY_LOOSEN_CAP = 3`). A fourth `--readability-loosen` errors with `loosen cap reached; use --readability-disable or --readability-reset`.
|
|
120
|
+
- Flesch floor of 30 (`READABILITY_MIN_FLESCH_FLOOR`). Once `flesch_min` reaches 30 the loosen action errors.
|
|
121
|
+
- Max-sentence ceiling of 60 (`READABILITY_MAX_SENTENCE_WORDS_CEILING`). Once `max_sentence_words` reaches 60 the loosen action errors.
|
|
122
|
+
- Avg-sentence ceiling of 40 (`READABILITY_AVG_SENTENCE_WORDS_CEILING`). Once `avg_sentence_words` reaches 40 the loosen action errors.
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
- `Fixes #N` and `Closes #N` close the linked issue on merge -- use them deliberately.
|
|
125
|
-
- `Linear: CC-1723` -- one line, no Markdown, after the intro paragraph or at the bottom.
|
|
126
|
-
- "Same change as <repo>#<n>" / "Follow-up to #<n>" -- one-liners that orient a reviewer.
|
|
124
|
+
### Action 2 — disable readability entirely
|
|
127
125
|
|
|
128
|
-
|
|
126
|
+
```bash
|
|
127
|
+
python <enforcer-path> --readability-disable
|
|
128
|
+
```
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
- Don't add a "Generated with Claude Code" footer -- merged Anthropic PRs don't use one consistently, and the repo's commit trailer covers attribution.
|
|
130
|
+
Writes `{"enabled": false}` to `~/.claude/state/pr_description_readability_enabled.json`. Shape detection, Heavy required-headers, ceremony-on-Trivial, self-closing reference, `This PR` opening, vague-language, and minimum-length checks all stay active. The readability check is the only one silenced.
|
|
132
131
|
|
|
133
|
-
|
|
132
|
+
Re-enable with:
|
|
134
133
|
|
|
135
|
-
|
|
134
|
+
```bash
|
|
135
|
+
python <enforcer-path> --readability-enable
|
|
136
|
+
```
|
|
136
137
|
|
|
137
|
-
|
|
138
|
-
- The body contains vague phrases like `fix bug`, `update code`, `minor changes`, or `various fixes`.
|
|
138
|
+
### Action 3 — reset the strike counter
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
```bash
|
|
141
|
+
python <enforcer-path> --readability-reset
|
|
142
|
+
```
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
Zeroes the strike counter at `~/.claude/state/pr_description_readability_strikes.json`. Clears `loosens_used` and threshold overrides at `~/.claude/state/pr_description_readability_overrides.json`. The readability check returns to default thresholds and a clean strike count.
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
### Action 4 — report a false positive
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
- Active voice. "Add `,` to the whitelist" -- not "`,` was added to the whitelist".
|
|
148
|
-
- No filler. Start with the content, not "This PR..." or "In this change...".
|
|
149
|
-
- No restating the diff. Trust the reviewer to read the code; explain the parts they can't infer.
|
|
150
|
-
- No hedging ("should", "might", "I think") unless the uncertainty is real -- in which case say "not yet verified" and call it out.
|
|
148
|
+
Reply with the PR body and your intended commit message. The maintainer tunes the thresholds or refines the regex.
|
|
151
149
|
|
|
152
150
|
## What to avoid
|
|
153
151
|
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
152
|
+
- **Vague language.** `fix bug`, `update code`, `minor changes`, `various improvements`. Each trips the `VAGUE_LANGUAGE_PATTERN` check.
|
|
153
|
+
- **`This PR` openings.** Hard block. Open with an imperative verb.
|
|
154
|
+
- **Self-closing references.** `Fixes #<this PR>` in a `gh pr edit` body. Self-reference adds zero context. Triggers a block on `gh pr edit` and `gh pr comment` invocations where the PR number is known.
|
|
155
|
+
- **Code snippets in prose.** The diff shows the code. Bodies describe intent.
|
|
156
|
+
- **Implementation-detail dumping.** Reviewers do not need every parameter name and call site. Describe the behavior change.
|
|
157
|
+
- **Filler.** `In this PR I have made the following changes:` adds zero signal. Start with the action.
|
|
@@ -32,7 +32,7 @@ import tempfile
|
|
|
32
32
|
from pathlib import Path
|
|
33
33
|
from typing import TextIO
|
|
34
34
|
|
|
35
|
-
from
|
|
35
|
+
from hooks_constants.gh_pr_author_swap_constants import (
|
|
36
36
|
ALL_GH_AUTH_SWITCH_COMMAND_HEAD,
|
|
37
37
|
ALL_SHELL_QUOTE_CHARACTERS,
|
|
38
38
|
BASH_COMMENT_INTRODUCER_CHARACTER,
|
|
@@ -10,17 +10,11 @@ import json
|
|
|
10
10
|
import sys
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
|
|
13
|
+
_hooks_dir = str(Path(__file__).resolve().parent.parent)
|
|
14
|
+
if _hooks_dir not in sys.path:
|
|
15
|
+
sys.path.insert(0, _hooks_dir)
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
hooks_tree = Path(__file__).resolve().parent.parent
|
|
16
|
-
hooks_tree_string = str(hooks_tree)
|
|
17
|
-
if hooks_tree_string not in sys.path:
|
|
18
|
-
sys.path.insert(0, hooks_tree_string)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_insert_hooks_tree_for_imports()
|
|
22
|
-
|
|
23
|
-
from config.bot_mention_comment_blocker_constants import (
|
|
17
|
+
from hooks_constants.bot_mention_comment_blocker_constants import ( # noqa: E402
|
|
24
18
|
COPILOT_MENTION_TOKEN,
|
|
25
19
|
CORRECTIVE_MESSAGE_COPILOT,
|
|
26
20
|
CORRECTIVE_MESSAGE_CURSOR,
|