claude-dev-env 1.50.1 → 1.50.3

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 (91) hide show
  1. package/_shared/pr-loop/audit-contract.md +3 -3
  2. package/audit-rubrics/category_rubrics/category-e-dead-code.md +3 -2
  3. package/audit-rubrics/prompts/category-a-api-contracts.md +1 -1
  4. package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
  5. package/audit-rubrics/prompts/category-c-resource-cleanup.md +2 -2
  6. package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +2 -2
  7. package/audit-rubrics/prompts/category-e-dead-code.md +5 -4
  8. package/audit-rubrics/prompts/category-f-silent-failures.md +2 -2
  9. package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
  10. package/audit-rubrics/prompts/category-h-security-boundaries.md +2 -2
  11. package/audit-rubrics/prompts/category-i-concurrency.md +2 -2
  12. package/audit-rubrics/prompts/category-j-code-rules-compliance.md +2 -2
  13. package/audit-rubrics/prompts/category-k-codebase-conflicts.md +2 -2
  14. package/docs/CODE_RULES.md +1 -1
  15. package/hooks/blocking/code_rules_annotations_length.py +167 -0
  16. package/hooks/blocking/code_rules_banned_identifiers.py +385 -0
  17. package/hooks/blocking/code_rules_boolean_mustcheck.py +350 -0
  18. package/hooks/blocking/code_rules_comments.py +337 -0
  19. package/hooks/blocking/code_rules_constants_config.py +252 -0
  20. package/hooks/blocking/code_rules_docstrings.py +308 -0
  21. package/hooks/blocking/code_rules_enforcer.py +98 -5807
  22. package/hooks/blocking/code_rules_imports_logging.py +276 -0
  23. package/hooks/blocking/code_rules_magic_values.py +180 -0
  24. package/hooks/blocking/code_rules_mock_completeness.py +295 -0
  25. package/hooks/blocking/code_rules_naming_collection.py +264 -0
  26. package/hooks/blocking/code_rules_optional_params.py +288 -0
  27. package/hooks/blocking/code_rules_paths_syspath.py +186 -0
  28. package/hooks/blocking/code_rules_probe_chains.py +305 -0
  29. package/hooks/blocking/code_rules_probe_detection.py +257 -0
  30. package/hooks/blocking/code_rules_probe_recording.py +225 -0
  31. package/hooks/blocking/code_rules_scope_binding.py +151 -0
  32. package/hooks/blocking/code_rules_shared.py +301 -0
  33. package/hooks/blocking/code_rules_string_magic.py +207 -0
  34. package/hooks/blocking/code_rules_test_assertions.py +226 -0
  35. package/hooks/blocking/code_rules_test_branching_except.py +181 -0
  36. package/hooks/blocking/code_rules_test_isolation.py +341 -0
  37. package/hooks/blocking/code_rules_type_escape.py +341 -0
  38. package/hooks/blocking/code_rules_typeddict_stub.py +305 -0
  39. package/hooks/blocking/code_rules_unused_imports.py +256 -0
  40. package/hooks/blocking/tdd_enforcer.py +31 -0
  41. package/hooks/blocking/test_code_rules_constants_config.py +26 -0
  42. package/hooks/blocking/test_code_rules_enforcer_banned_noun_word.py +5 -2
  43. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -5
  44. package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +21 -15
  45. package/hooks/blocking/test_code_rules_enforcer_config_path.py +20 -16
  46. package/hooks/blocking/test_code_rules_enforcer_exempt_marker_chained.py +4 -2
  47. package/hooks/blocking/test_code_rules_enforcer_function_length.py +18 -13
  48. package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +1 -2
  49. package/hooks/blocking/test_code_rules_enforcer_ignored_must_check_return.py +22 -12
  50. package/hooks/blocking/test_code_rules_enforcer_split_annotations_length.py +55 -0
  51. package/hooks/blocking/test_code_rules_enforcer_split_banned.py +170 -0
  52. package/hooks/blocking/test_code_rules_enforcer_split_comments.py +60 -0
  53. package/hooks/blocking/test_code_rules_enforcer_split_config_path.py +52 -0
  54. package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +236 -0
  55. package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +296 -0
  56. package/hooks/blocking/test_code_rules_enforcer_split_entry_2.py +238 -0
  57. package/hooks/blocking/test_code_rules_enforcer_split_isolation_1.py +271 -0
  58. package/hooks/blocking/test_code_rules_enforcer_split_isolation_2.py +283 -0
  59. package/hooks/blocking/test_code_rules_enforcer_split_isolation_3.py +268 -0
  60. package/hooks/blocking/test_code_rules_enforcer_split_isolation_4.py +85 -0
  61. package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +303 -0
  62. package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +111 -0
  63. package/hooks/blocking/test_code_rules_enforcer_split_mustcheck.py +87 -0
  64. package/hooks/blocking/test_code_rules_enforcer_split_naming.py +107 -0
  65. package/hooks/blocking/test_code_rules_enforcer_split_optional_params.py +325 -0
  66. package/hooks/blocking/test_code_rules_enforcer_split_paths_syspath.py +110 -0
  67. package/hooks/blocking/test_code_rules_enforcer_split_shared.py +44 -0
  68. package/hooks/blocking/test_code_rules_enforcer_split_string_magic.py +55 -0
  69. package/hooks/blocking/test_code_rules_enforcer_split_test_assertions.py +56 -0
  70. package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +21 -15
  71. package/hooks/blocking/test_code_rules_paths_syspath.py +26 -0
  72. package/hooks/blocking/test_tdd_enforcer.py +116 -0
  73. package/hooks/hooks_constants/blocking_check_limits.py +3 -0
  74. package/hooks/hooks_constants/code_rules_enforcer_constants.py +8 -0
  75. package/hooks/hooks_constants/sys_path_insert_constants.py +1 -0
  76. package/package.json +1 -1
  77. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +13 -7
  78. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +21 -11
  79. package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +92 -0
  80. package/skills/bugteam/CONSTRAINTS.md +1 -1
  81. package/skills/bugteam/PROMPTS.md +20 -48
  82. package/skills/bugteam/SKILL.md +5 -5
  83. package/skills/bugteam/reference/audit-and-teammates.md +1 -1
  84. package/skills/bugteam/reference/audit-contract.md +4 -4
  85. package/skills/bugteam/reference/design-rationale.md +1 -1
  86. package/skills/findbugs/SKILL.md +21 -12
  87. package/skills/fixbugs/SKILL.md +1 -1
  88. package/skills/qbug/SKILL.md +5 -5
  89. package/skills/qbug/test_qbug_skill_audit_schema.py +13 -23
  90. package/skills/refine/SKILL.md +1 -1
  91. package/hooks/blocking/test_code_rules_enforcer.py +0 -2669
@@ -21,7 +21,7 @@ Each finding an audit produces MUST be one of exactly two shapes.
21
21
  "id": "loop<N>-<K>",
22
22
  "file": "path/relative/to/repo/root.py",
23
23
  "line": 123,
24
- "category": "A | B | C | D | E | F | G | H | I | J",
24
+ "category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N",
25
25
  "severity": "P0 | P1 | P2",
26
26
  "excerpt": "verbatim code snippet from the offending line(s)",
27
27
  "failure_mode": "one sentence describing what goes wrong and when",
@@ -37,7 +37,7 @@ Used when an audit investigates a category and does NOT find a bug. Bare "verifi
37
37
 
38
38
  ```json
39
39
  {
40
- "category": "A | B | C | D | E | F | G | H | I | J",
40
+ "category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N",
41
41
  "files_opened": ["file1.py", "file2.py"],
42
42
  "lines_quoted": [
43
43
  {"file": "file1.py", "line": 88, "text": "verbatim line content"}
@@ -120,7 +120,7 @@ Sequence:
120
120
  3. Run `py_compile` (or language-equivalent) on each modified file.
121
121
  4. Compute `fix_diff` against pre-fix contents for the modified set.
122
122
  5. Run `bugteam_code_rules_gate.py` with explicit paths for every modified file.
123
- 6. Spawn a scoped audit of `fix_diff` with full A–J rigor, Shape A/B contract, adversarial pass, AND Haiku secondary in parallel (paranoid mode on post-fix).
123
+ 6. Spawn a scoped audit of `fix_diff` with full A–N rigor, Shape A/B contract, adversarial pass, AND Haiku secondary in parallel (paranoid mode on post-fix).
124
124
  7. Read the previous loop's outcome XML (`<worktree_path>/.bugteam-pr<N>-loop<L-1>.outcomes.xml`) and obtain its total finding count. If this is the first loop (L <= 1) or the file does not exist, skip this comparison. Compute the post-fix total: previous total minus bugs fixed in this round plus new violations found in the post-fix audit (step 6). If the post-fix total exceeds the previous total, flag all new findings as same-loop fix-targets and revise. An increase in total findings across loop transitions is a regression.
125
125
  8. Any new findings become same-loop fix-targets. Internal iteration count increments by one.
126
126
  9. After 3 internal iterations with fresh findings each time, exit `stuck: post-fix audit not converging`.
@@ -1,6 +1,6 @@
1
1
  # Category E — Dead code and unused imports
2
2
 
3
- **What this category audits:** imports the diff adds but leaves unreferenced, functions defined but never called, branches unreachable due to a prior return, conditions that are always true or always false, parameters that are accepted but never used, removed-but-not-deleted symbols.
3
+ **What this category audits:** imports the diff adds but leaves unreferenced (dead imports), functions defined but never called, code made unreachable by a prior return or raise (dead returns), conditions that are always true or always false (dead branches), parameters that are accepted but never used (dead parameters), local variables assigned but never read (dead locals), removed-but-not-deleted symbols.
4
4
 
5
5
  **Examples of Category E findings:**
6
6
  - A new `import` line with zero corresponding references in the file.
@@ -8,6 +8,7 @@
8
8
  - Code after an unconditional `return` or `raise`.
9
9
  - A condition like `if False:` or `while True: ... return` where the loop body always returns immediately.
10
10
  - An accepted parameter that the function body never uses.
11
+ - A local variable assigned and never read afterward in the same function.
11
12
 
12
13
  **Companion reference:** see `../source-material-section-types.md`.
13
14
 
@@ -21,7 +22,7 @@
21
22
  | E2 | Functions / methods defined but never called | Internal helpers defined in this PR with no call sites in this PR or elsewhere. |
22
23
  | E3 | Code after unconditional return / raise / exit | Statements following a top-level `return`, `raise`, `sys.exit`, `os._exit` that cannot execute. |
23
24
  | E4 | Always-true / always-false conditions | `if True:` / `if False:` / conditions provably constant given context. |
24
- | E5 | Unused parameters | Parameters declared but never read inside the function body. |
25
+ | E5 | Unused parameters and locals | Parameters declared but never read inside the function body; local variables assigned but never read afterward in the same scope. |
25
26
  | E6 | Removed-but-not-deleted symbol references | Symbols renamed/removed elsewhere with stale import or call sites left behind. |
26
27
  | E7 | Test fixtures / helpers defined but never used | Pytest fixtures, test data builders, mock factories with no callers. |
27
28
  | E8 | Stub / placeholder code without TODO | `pass`, `...`, `raise NotImplementedError` left without explanation or tracking. |
@@ -95,7 +95,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket A1–A9, produce Shape
95
95
 
96
96
  # Worked example: jl-cmd/claude-code-config PR #394 (May 2026 audit experiment)
97
97
 
98
- Audit jl-cmd/claude-code-config PR #394 for **Category A only** (API contract verification). Skip B–J. Sub-bucket forced-exhaustion mode: Category A is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
98
+ Audit jl-cmd/claude-code-config PR #394 for **Category A only** (API contract verification). Skip B–N. Sub-bucket forced-exhaustion mode: Category A is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
99
99
 
100
100
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
101
101
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category B only** (selector / query / engine compatibility). Skip A, C–K. Sub-bucket forced-exhaustion mode: Category B is decomposed into 7 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category B only** (selector / query / engine compatibility). Skip A, C–N. Sub-bucket forced-exhaustion mode: Category B is decomposed into 7 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA: repo, ref/SHA, PR or commit range, file count, language matrix, declared engine/runtime/browser/DB targets — fill before running.]
4
4
  ID prefix: `find`.
@@ -80,7 +80,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket B1–B7, produce Shape
80
80
 
81
81
  # Worked example: jl-cmd/claude-code-config PR #394
82
82
 
83
- Audit jl-cmd/claude-code-config PR #394 for **Category B only** (selector / query / engine compatibility). Skip A, C–K. Sub-bucket forced-exhaustion mode: Category B is decomposed into 7 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
83
+ Audit jl-cmd/claude-code-config PR #394 for **Category B only** (selector / query / engine compatibility). Skip A, C–N. Sub-bucket forced-exhaustion mode: Category B is decomposed into 7 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
84
84
 
85
85
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
86
86
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category C only** (resource cleanup and lifecycle). Skip A, B, D–K. Sub-bucket forced-exhaustion mode: Category C is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category C only** (resource cleanup and lifecycle). Skip A, B, D–N. Sub-bucket forced-exhaustion mode: Category C is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA]
4
4
  - Repository / artifact: [REPO_OR_ARTIFACT_NAME]
@@ -98,7 +98,7 @@ Read-only. No edits, no commits.
98
98
 
99
99
  # Worked example: jl-cmd/claude-code-config PR #394
100
100
 
101
- Audit jl-cmd/claude-code-config PR #394 for **Category C only** (resource cleanup and lifecycle). Skip A, B, D–K. Sub-bucket forced-exhaustion mode: Category C is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
101
+ Audit jl-cmd/claude-code-config PR #394 for **Category C only** (resource cleanup and lifecycle). Skip A, B, D–N. Sub-bucket forced-exhaustion mode: Category C is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
102
102
 
103
103
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
104
104
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category D only** (variable scoping, ordering, and unbound references). Skip A–C, E–K. Sub-bucket forced-exhaustion mode: Category D is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category D only** (variable scoping, ordering, and unbound references). Skip A–C, E–N. Sub-bucket forced-exhaustion mode: Category D is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA]
4
4
  - Repo / artifact: [REPO_OR_ARTIFACT]
@@ -89,7 +89,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket D1–D8, produce Shape
89
89
 
90
90
  # Worked example: jl-cmd/claude-code-config PR #394
91
91
 
92
- Audit jl-cmd/claude-code-config PR #394 for **Category D only** (variable scoping, ordering, and unbound references). Skip A–C, E–K. Sub-bucket forced-exhaustion mode: Category D is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
92
+ Audit jl-cmd/claude-code-config PR #394 for **Category D only** (variable scoping, ordering, and unbound references). Skip A–C, E–N. Sub-bucket forced-exhaustion mode: Category D is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
93
93
 
94
94
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
95
95
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category E only** (dead code and unused imports). Skip A–D, F–K. Sub-bucket forced-exhaustion mode: Category E is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category E only** (dead code and unused imports). Skip A–D, F–N. Sub-bucket forced-exhaustion mode: Category E is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA]
4
4
  - Repo / artifact: [REPO_OR_ARTIFACT_NAME]
@@ -43,8 +43,9 @@ Inline the artifact under this section using the section types defined in the ch
43
43
  - Runtime-bound conditions (parameter values, `os.path.isdir`, `Test-Path`, environment lookups) are not constant; state the runtime source.
44
44
  - Adversarial probes for proof-of-absence: (a) any `if 1:` / `if 0:` / `if True:` / `if False:` literals in the diff? (b) any condition of the form `if x:` where `x` was just assigned a literal in the line above? (c) any `assert True` / `assert False` in test bodies? (d) any short-circuit like `x or DEFAULT` where `x` was just constructed and is statically truthy?
45
45
 
46
- **E5. Unused parameters**
46
+ **E5. Unused parameters and locals**
47
47
  - For every function or method introduced or modified by the artifact, verify each declared parameter is read at least once in the body (including in default-argument expressions for inner functions, in closures, or in type guards).
48
+ - For every function or method introduced or modified by the artifact, verify each local variable assigned in the body is read at least once afterward in the same scope; an assignment whose value is never read is a dead local.
48
49
  - Tuple-unpack discards (`for path, _, _ in os.walk(...)`) are out of scope — E5 specifically scopes "function parameters never read"; state this exclusion explicitly.
49
50
  - `*args` / `**kwargs` / TypeScript rest spreads: confirm at least one consumer (forwarded to another call, iterated, indexed) or mark the parameter unused.
50
51
  - Cross-language parameter declarations (PowerShell `param(...)`, shell positional `$1..$N`, Bash `getopts`): confirm each named parameter has at least one body reference.
@@ -86,7 +87,7 @@ Note: most Category E findings are P2 (style / cleanup) unless the dead code mas
86
87
 
87
88
  # Worked example: jl-cmd/claude-code-config PR #394
88
89
 
89
- Audit jl-cmd/claude-code-config PR #394 for **Category E only** (dead code and unused imports). Skip A–D, F–K. Sub-bucket forced-exhaustion mode: Category E is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
90
+ Audit jl-cmd/claude-code-config PR #394 for **Category E only** (dead code and unused imports). Skip A–D, F–N. Sub-bucket forced-exhaustion mode: Category E is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
90
91
 
91
92
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
92
93
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -128,7 +129,7 @@ ID prefix: `find`.
128
129
  - `test_sweep_empty_dirs.py` line 13: `if str(_SCRIPTS_DIR) not in sys.path:` — runtime membership test; not constant.
129
130
  - Adversarial probes for proof-of-absence: (a) does the diff introduce any `if 1:` / `if 0:` / `if True:` / `if False:` literals? grep the diff text. (b) any condition of the form `if x:` where `x` was just assigned a literal in the line above? (c) any `assert True` or `assert False` in test bodies? (none — verify).
130
131
 
131
- **E5. Unused parameters**
132
+ **E5. Unused parameters and locals**
132
133
  - `_log_walk_error(os_error: OSError) -> None` (line 14) — parameter `os_error` is read twice in the body (`os_error.filename`, `os_error.strerror`). Used.
133
134
  - `sweep(root: str, min_age_seconds: int) -> list[str]` (line 18) — `root` is passed to `os.walk` (line 21); `min_age_seconds` is read at line 26. Both used.
134
135
  - `_build_parser() -> argparse.ArgumentParser` (line 39) — zero parameters; nothing to verify.
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category F only** (silent failures). Skip A–E, G–K. Sub-bucket forced-exhaustion mode: Category F is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category F only** (silent failures). Skip A–E, G–N. Sub-bucket forced-exhaustion mode: Category F is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA]
4
4
  - Title / short description: [TITLE]
@@ -98,7 +98,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket F1-F8, produce Shape A
98
98
 
99
99
  # Worked example: jl-cmd/claude-code-config PR #394
100
100
 
101
- Audit jl-cmd/claude-code-config PR #394 for **Category F only** (silent failures). Skip A–E, G–K. Sub-bucket forced-exhaustion mode: Category F is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
101
+ Audit jl-cmd/claude-code-config PR #394 for **Category F only** (silent failures). Skip A–E, G–N. Sub-bucket forced-exhaustion mode: Category F is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
102
102
 
103
103
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
104
104
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category G only** (off-by-one, bounds, integer overflow). Skip A–F, H–K. Sub-bucket forced-exhaustion mode: Category G is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category G only** (off-by-one, bounds, integer overflow). Skip A–F, H–N. Sub-bucket forced-exhaustion mode: Category G is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA]
4
4
  - Repository / artifact: [REPO_OR_ARTIFACT]
@@ -61,7 +61,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket G1-G8, produce Shape A
61
61
 
62
62
  # Worked example: jl-cmd/claude-code-config PR #394
63
63
 
64
- Audit jl-cmd/claude-code-config PR #394 for **Category G only** (off-by-one, bounds, integer overflow). Skip A–F, H–K. Sub-bucket forced-exhaustion mode: Category G is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
64
+ Audit jl-cmd/claude-code-config PR #394 for **Category G only** (off-by-one, bounds, integer overflow). Skip A–F, H–N. Sub-bucket forced-exhaustion mode: Category G is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
65
65
 
66
66
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
67
67
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category H only** (security boundaries). Skip A–G, I–K. Sub-bucket forced-exhaustion mode: Category H is decomposed into 10 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category H only** (security boundaries). Skip A–G, I–N. Sub-bucket forced-exhaustion mode: Category H is decomposed into 10 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  ## ARTIFACT METADATA — trust model
4
4
 
@@ -83,7 +83,7 @@ Note: Category H findings tend toward P0/P1 since they're security-relevant —
83
83
 
84
84
  # Worked example: jl-cmd/claude-code-config PR #394
85
85
 
86
- Audit jl-cmd/claude-code-config PR #394 for **Category H only** (security boundaries). Skip A–G, I–K. Sub-bucket forced-exhaustion mode: Category H is decomposed into 10 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
86
+ Audit jl-cmd/claude-code-config PR #394 for **Category H only** (security boundaries). Skip A–G, I–N. Sub-bucket forced-exhaustion mode: Category H is decomposed into 10 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
87
87
 
88
88
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
89
89
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category I only** (concurrency hazards). Skip A–H, J–K. Sub-bucket forced-exhaustion mode: Category I is decomposed into [N] sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category I only** (concurrency hazards). Skip A–H, J–N. Sub-bucket forced-exhaustion mode: Category I is decomposed into [N] sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA — including: is this code single-threaded, threaded, asyncio, multiprocessing, or mixed? Name the runtime (CPython 3.x, Node, Go, JVM, .NET, PowerShell runspace, browser JS), the concurrency primitives actually present (`threading`, `asyncio`, `multiprocessing`, `concurrent.futures`, `Thread`, `goroutine`, `Promise`, `Task`, `Start-ThreadJob`, `ForEach-Object -Parallel`, etc.), and the inter-process surface (shared filesystem, shared DB, shared cache, shared queue, signals). State explicitly which primitives are absent so each sub-bucket has a Shape B basis.]
4
4
 
@@ -88,7 +88,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket I1–I8, produce Shape
88
88
 
89
89
  # Worked example: jl-cmd/claude-code-config PR #394
90
90
 
91
- Audit jl-cmd/claude-code-config PR #394 for **Category I only** (concurrency hazards). Skip A–H, J–K. Sub-bucket forced-exhaustion mode: Category I is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
91
+ Audit jl-cmd/claude-code-config PR #394 for **Category I only** (concurrency hazards). Skip A–H, J–N. Sub-bucket forced-exhaustion mode: Category I is decomposed into 8 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
92
92
 
93
93
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
94
94
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category J only** (CODE_RULES.md compliance). Skip A–I, K. Sub-bucket forced-exhaustion mode: Category J is decomposed into 12 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category J only** (CODE_RULES.md compliance). Skip A–I, K–N. Sub-bucket forced-exhaustion mode: Category J is decomposed into 12 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA]
4
4
  - Artifact: [PR title / commit subject / file set / patch series]
@@ -100,7 +100,7 @@ Note: most Category J findings are P2 (style / cleanup) since they don't affect
100
100
 
101
101
  # Worked example: jl-cmd/claude-code-config PR #394
102
102
 
103
- Audit jl-cmd/claude-code-config PR #394 for **Category J only** (CODE_RULES.md compliance). Skip A–I, K. Sub-bucket forced-exhaustion mode: Category J is decomposed into 12 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
103
+ Audit jl-cmd/claude-code-config PR #394 for **Category J only** (CODE_RULES.md compliance). Skip A–I, K–N. Sub-bucket forced-exhaustion mode: Category J is decomposed into 12 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
104
104
 
105
105
  PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
106
106
  Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
@@ -1,4 +1,4 @@
1
- Audit [REPO/ARTIFACT] [TARGET_ID] for **Category K only** (codebase conflicts — incomplete propagation). Skip A–J. Sub-bucket forced-exhaustion mode: Category K is decomposed into 9 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
1
+ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category K only** (codebase conflicts — incomplete propagation). Skip A–J, L–N. Sub-bucket forced-exhaustion mode: Category K is decomposed into 9 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
2
2
 
3
3
  [ARTIFACT METADATA — including the BEFORE state of changed surfaces, so the agent can compare before vs after]
4
4
 
@@ -79,7 +79,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket K1-K9, produce Shape A
79
79
 
80
80
  Note: PR #397 is the K canonical case, NOT #394.
81
81
 
82
- Audit jl-cmd/claude-code-config PR #397 for **Category K only** (codebase conflicts — incomplete propagation). Skip A–J. Sub-bucket forced-exhaustion mode: Category K is decomposed into 9 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
82
+ Audit jl-cmd/claude-code-config PR #397 for **Category K only** (codebase conflicts — incomplete propagation). Skip A–J, L–N. Sub-bucket forced-exhaustion mode: Category K is decomposed into 9 sub-buckets below. Each sub-bucket REQUIRES at least one Shape A finding OR exactly one Shape B proof-of-absence with **at least 3 adversarial probes** specific to that sub-bucket. A sub-bucket returning neither is a protocol gap.
83
83
 
84
84
  PR: fix(hooks): improve hedging-language guardrail to surface user questions
85
85
  Base SHA: 76f9c1a0048729b87c44626a3380dc840065c2fa (origin/main at PR open time)
@@ -354,7 +354,7 @@ These principles cannot be reduced to a regex or AST visitor. They live in user-
354
354
 
355
355
  ### Audit-rubric reference
356
356
 
357
- For multi-file architectural reviews see [`packages/claude-dev-env/audit-rubrics/`](../audit-rubrics/). Categories A–F, I, K stay as agent rubrics rather thanblocking rules because they require multi-file reasoning that single-file hooks cannot perform.
357
+ For multi-file architectural reviews see [`packages/claude-dev-env/audit-rubrics/`](../audit-rubrics/). Categories A–N are maintained as agent rubrics. Category J (CODE_RULES.md compliance) mirrors the hook-enforced rules as an audit-side rubric; the other categories stay agent rubrics because they rest on multi-file reasoning beyond a single-file hook's reach.
358
358
 
359
359
  ---
360
360
 
@@ -0,0 +1,167 @@
1
+ """Parameter-annotation, return-annotation, and function-length checks."""
2
+
3
+ import ast
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ _blocking_directory = str(Path(__file__).resolve().parent)
8
+ _hooks_directory = str(Path(__file__).resolve().parent.parent)
9
+ if _blocking_directory not in sys.path:
10
+ sys.path.insert(0, _blocking_directory)
11
+ if _hooks_directory not in sys.path:
12
+ sys.path.insert(0, _hooks_directory)
13
+
14
+ from code_rules_shared import ( # noqa: E402
15
+ _collect_annotated_arguments,
16
+ _definition_docstring_line_span,
17
+ _function_definition_line_span,
18
+ _scope_violations_to_changed_lines,
19
+ is_hook_infrastructure,
20
+ is_migration_file,
21
+ is_test_file,
22
+ is_workflow_registry_file,
23
+ )
24
+
25
+ from hooks_constants.code_rules_enforcer_constants import ( # noqa: E402
26
+ ALL_SELF_AND_CLS_PARAMETER_NAMES,
27
+ FUNCTION_LENGTH_BLOCKING_MESSAGE_SUFFIX,
28
+ FUNCTION_LENGTH_BLOCKING_THRESHOLD,
29
+ )
30
+
31
+
32
+ def check_parameter_annotations(content: str, file_path: str) -> list[str]:
33
+ if is_test_file(file_path):
34
+ return []
35
+ if is_workflow_registry_file(file_path) or is_migration_file(file_path):
36
+ return []
37
+ try:
38
+ tree = ast.parse(content)
39
+ except SyntaxError:
40
+ return []
41
+ issues: list[str] = []
42
+ for each_node in ast.walk(tree):
43
+ if not isinstance(each_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
44
+ continue
45
+ for each_arg in _collect_annotated_arguments(each_node):
46
+ if each_arg.arg in ALL_SELF_AND_CLS_PARAMETER_NAMES:
47
+ continue
48
+ if each_arg.annotation is None:
49
+ issues.append(
50
+ f"Line {each_arg.lineno}: parameter {each_arg.arg!r} on {each_node.name!r} missing type annotation (CODE_RULES §6)"
51
+ )
52
+ return issues
53
+
54
+
55
+ def check_return_annotations(content: str, file_path: str) -> list[str]:
56
+ if is_test_file(file_path):
57
+ return []
58
+ if is_workflow_registry_file(file_path) or is_migration_file(file_path):
59
+ return []
60
+ try:
61
+ tree = ast.parse(content)
62
+ except SyntaxError:
63
+ return []
64
+ issues: list[str] = []
65
+ for each_node in ast.walk(tree):
66
+ if not isinstance(each_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
67
+ continue
68
+ if each_node.returns is None:
69
+ issues.append(
70
+ f"Line {each_node.lineno}: function {each_node.name!r} missing return type annotation (CODE_RULES §6)"
71
+ )
72
+ return issues
73
+
74
+
75
+ def check_function_length(
76
+ content: str,
77
+ file_path: str,
78
+ all_changed_lines: set[int] | None = None,
79
+ defer_scope_to_caller: bool = False,
80
+ ) -> list[str]:
81
+ """Flag functions whose executable span exceeds cognitive-load thresholds.
82
+
83
+ Function executable spans — the definition span (signature line through
84
+ last body statement, inclusive) minus the leading docstring lines of the
85
+ function and of every function or class nested within it, per
86
+ ``_definition_docstring_line_span`` summed over the nested definitions —
87
+ at or above ``FUNCTION_LENGTH_BLOCKING_THRESHOLD`` appear in
88
+ the returned issues list and block the write at the
89
+ gate. The threshold rests on the small-function guidance in Robert C.
90
+ Martin, *Clean Code* Chapter Three ("Functions") and the Google Python Style
91
+ Guide's ~forty-line function review hint
92
+ (https://google.github.io/styleguide/pyguide.html) — a measure of
93
+ executable complexity, paired with the Guide's complete-docstring mandate
94
+ for public APIs, so documentation lines never count against the gate; this
95
+ gate blocks on body growth that pushes a function past that span. It does
96
+ not derive from CODE_RULES file-length guidance, which governs advisory
97
+ file-length signals and argues against hard numeric blocks.
98
+
99
+ The issue message carries ``Function NAME (defined at line X) is Y lines``
100
+ precisely so the gate's ``function_length_span_range`` can recover the
101
+ function's full declared span (lines ``X`` through ``X + Y - 1``). The
102
+ gate classifies the violation blocking when that span intersects the
103
+ diff's added lines — the body grew this diff — and advisory otherwise — a
104
+ pre-existing, untouched long function in a file the diff happened to
105
+ touch. Anchoring to the span rather than a single ``Line N:`` definition
106
+ line lets body growth on any interior line block correctly even when the
107
+ ``def`` line itself is untouched.
108
+
109
+ Exempt: test files (test bodies are sometimes long by necessity), Django
110
+ migrations (auto-generated), workflow registries (registry entries), and
111
+ hook infrastructure.
112
+
113
+ Args:
114
+ content: The Python source to analyze.
115
+ file_path: The path of the file being checked.
116
+ all_changed_lines: Post-edit line numbers the current edit touched, or
117
+ None to treat the whole file as in scope. When provided, a violation
118
+ blocks only when the function's declared span intersects the changed
119
+ lines.
120
+ defer_scope_to_caller: When True, return every violation so the
121
+ commit/push gate's ``split_violations_by_scope`` can scope by added
122
+ line and report the in-scope set.
123
+
124
+ Returns:
125
+ Blocking issues. When *defer_scope_to_caller* is True every violation is
126
+ returned for the gate to scope; otherwise every violation in scope is
127
+ returned.
128
+ """
129
+ if is_test_file(file_path):
130
+ return []
131
+ if is_hook_infrastructure(file_path):
132
+ return []
133
+ if is_workflow_registry_file(file_path) or is_migration_file(file_path):
134
+ return []
135
+
136
+ try:
137
+ parsed_tree = ast.parse(content)
138
+ except SyntaxError:
139
+ return []
140
+
141
+ all_violations_in_walk_order: list[tuple[range, str]] = []
142
+ for each_node in ast.walk(parsed_tree):
143
+ if not isinstance(each_node, (ast.FunctionDef, ast.AsyncFunctionDef)):
144
+ continue
145
+ line_span = _function_definition_line_span(each_node)
146
+ if line_span < FUNCTION_LENGTH_BLOCKING_THRESHOLD:
147
+ continue
148
+ docstring_line_total = sum(
149
+ _definition_docstring_line_span(each_definition)
150
+ for each_definition in ast.walk(each_node)
151
+ if isinstance(
152
+ each_definition, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
153
+ )
154
+ )
155
+ executable_line_span = line_span - docstring_line_total
156
+ if executable_line_span >= FUNCTION_LENGTH_BLOCKING_THRESHOLD:
157
+ span_range = range(each_node.lineno, each_node.lineno + line_span)
158
+ message = (
159
+ f"Function {each_node.name!r} (defined at line {each_node.lineno}) "
160
+ f"is {line_span} lines - {FUNCTION_LENGTH_BLOCKING_MESSAGE_SUFFIX}"
161
+ )
162
+ all_violations_in_walk_order.append((span_range, message))
163
+ return _scope_violations_to_changed_lines(
164
+ all_violations_in_walk_order,
165
+ all_changed_lines,
166
+ defer_scope_to_caller,
167
+ )