claude-dev-env 1.49.0 → 1.50.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 (43) hide show
  1. package/audit-rubrics/category_rubrics/category-a-api-contracts.md +86 -0
  2. package/audit-rubrics/category_rubrics/category-b-selector-engine-compat.md +36 -0
  3. package/audit-rubrics/category_rubrics/category-c-resource-cleanup.md +35 -0
  4. package/audit-rubrics/category_rubrics/category-d-scoping-and-ordering.md +35 -0
  5. package/audit-rubrics/category_rubrics/category-e-dead-code.md +38 -0
  6. package/audit-rubrics/category_rubrics/category-f-silent-failures.md +38 -0
  7. package/audit-rubrics/category_rubrics/category-g-bounds-and-overflow.md +38 -0
  8. package/audit-rubrics/category_rubrics/category-h-security-boundaries.md +40 -0
  9. package/audit-rubrics/category_rubrics/category-i-concurrency.md +38 -0
  10. package/audit-rubrics/category_rubrics/category-j-code-rules-compliance.md +46 -0
  11. package/audit-rubrics/category_rubrics/category-k-codebase-conflicts.md +59 -0
  12. package/audit-rubrics/category_rubrics/category-l-behavior-equivalence.md +45 -0
  13. package/audit-rubrics/category_rubrics/category-m-producer-consumer-cardinality.md +44 -0
  14. package/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md +45 -0
  15. package/audit-rubrics/prompts/category-a-api-contracts.md +399 -0
  16. package/audit-rubrics/prompts/category-b-selector-engine-compat.md +401 -0
  17. package/audit-rubrics/prompts/category-c-resource-cleanup.md +420 -0
  18. package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +414 -0
  19. package/audit-rubrics/prompts/category-e-dead-code.md +420 -0
  20. package/audit-rubrics/prompts/category-f-silent-failures.md +420 -0
  21. package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +383 -0
  22. package/audit-rubrics/prompts/category-h-security-boundaries.md +423 -0
  23. package/audit-rubrics/prompts/category-i-concurrency.md +429 -0
  24. package/audit-rubrics/prompts/category-j-code-rules-compliance.md +463 -0
  25. package/audit-rubrics/prompts/category-k-codebase-conflicts.md +328 -0
  26. package/audit-rubrics/prompts/category-l-behavior-equivalence.md +128 -0
  27. package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +129 -0
  28. package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +132 -0
  29. package/audit-rubrics/source-material-section-types.md +51 -0
  30. package/docs/CODE_RULES.md +6 -1
  31. package/hooks/blocking/code_rules_enforcer.py +323 -11
  32. package/hooks/blocking/md_to_html_blocker.py +2 -2
  33. package/hooks/blocking/test_code_rules_enforcer.py +65 -0
  34. package/hooks/blocking/test_code_rules_enforcer_docstring_args_signature.py +256 -0
  35. package/hooks/blocking/test_code_rules_enforcer_ignored_must_check_return.py +256 -0
  36. package/hooks/blocking/test_code_rules_enforcer_naming_pattern.py +137 -1
  37. package/hooks/blocking/test_md_to_html_blocker.py +38 -0
  38. package/hooks/hooks_constants/blocking_check_limits.py +2 -0
  39. package/hooks/hooks_constants/code_rules_enforcer_constants.py +15 -1
  40. package/hooks/hooks_constants/md_to_html_blocker_constants.py +1 -1
  41. package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +11 -4
  42. package/package.json +2 -1
  43. package/skills/bugteam/reference/teardown-publish-permissions.md +7 -2
@@ -0,0 +1,420 @@
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.
2
+
3
+ [ARTIFACT METADATA]
4
+ - Repo / artifact: [REPO_OR_ARTIFACT_NAME]
5
+ - Target ID: [PR_NUMBER / COMMIT_SHA / FILE_SET / TICKET_ID]
6
+ - Title / summary: [SHORT_DESCRIPTION]
7
+ - Head SHA / revision: [REVISION_IDENTIFIER]
8
+ - Languages in scope: [PYTHON / TYPESCRIPT / POWERSHELL / GO / ...]
9
+
10
+ ID prefix: `find`.
11
+
12
+ ## Source material
13
+
14
+ Inline the artifact under this section using the section types defined in the chunking guide (`../source-material-section-types.md`). For Category E, every line of the artifact that introduces or modifies imports, function definitions, control-flow exits, conditionals, parameter lists, or test fixtures must be in scope. Mark out-of-scope blocks (vendored code, generated files, third-party snapshots) explicitly so the audit walk does not flag them.
15
+
16
+ [INLINE THE DIFF / FILE BODIES / SNIPPET HERE — one fenced block per file or per logical unit, with file path and line numbers preserved.]
17
+
18
+ ## Sub-buckets (each requires Shape A finding OR Shape B with ≥3 adversarial probes)
19
+
20
+ **E1. New imports without references**
21
+ - Walk every `import` line introduced or modified by the artifact. For each, locate at least one body reference in the same file (function call, attribute access, type annotation, decorator, default-argument expression).
22
+ - Confirm `__all__` re-exports: if the file declares `__all__`, an import that appears only in `__all__` still counts as referenced; if no `__all__` is declared, the exemption is inert and must be stated as such.
23
+ - Confirm `# noqa` markers: every `# noqa` on an import line must be justified by a specific lint code (e.g., `E402` for module-level imports following a `sys.path` mutation). State the justification.
24
+ - Confirm `TYPE_CHECKING` blocks: imports inside `if TYPE_CHECKING:` are referenced only by string-form annotations or runtime `typing.get_type_hints` consumers; verify at least one such consumer or mark the exemption inert.
25
+ - Constants-only or re-export-only files (no imports of their own) — state explicitly that there is nothing to sweep.
26
+
27
+ **E2. Functions / methods defined but never called**
28
+ - For every function, method, or callable defined or modified by the artifact, enumerate call sites: direct calls, callback registrations (`onerror=`, `key=`, signal handlers), decorator applications, `__main__` guards, framework-driven discovery (pytest collection, Django URL resolution, FastAPI route decorators, Click groups), and string-form references resolved at runtime (`getattr`, dispatch tables).
29
+ - Leading-underscore names: a single internal call site is sufficient; explicitly verify nothing outside the file imports the underscore-prefixed name.
30
+ - Public names: confirm at least one call site inside or outside the file, or confirm the function is part of a documented public API.
31
+ - Framework-discovered callables (test functions, route handlers, CLI commands): state which collector picks them up and confirm the artifact's path matches that collector's pattern.
32
+
33
+ **E3. Code after unconditional return / raise / exit**
34
+ - For every `return`, `raise`, `sys.exit`, `os._exit`, PowerShell `exit`, `throw`, Go `panic`, JavaScript `throw` introduced or modified by the artifact, verify nothing executable follows at the same indentation / brace level.
35
+ - Confirm `for ... else` and `while ... else` clauses: an `else` after a loop runs only when the loop completes without `break`; verify the absence-or-presence is intentional.
36
+ - Confirm `try / except / finally` flow: code after a fully-exhaustive `try` whose every branch returns or raises is unreachable.
37
+ - Module-scope tail: nothing should follow `if __name__ == "__main__": main()` at module scope.
38
+ - Adversarial probes for proof-of-absence: (a) any statement at module scope after the `__main__` guard? (b) any statement after a `pass` or `continue` that could only run if the exception handler fell through? (c) any loop with an `else:` clause that was not intended?
39
+
40
+ **E4. Always-true / always-false conditions**
41
+ - Walk every `if`, `elif`, `while`, ternary, and short-circuit expression introduced or modified by the artifact.
42
+ - Intentional infinite loops (`while True:` watch loops, event loops) are NOT dead by E4 standards; flag the pattern explicitly so a reader understands why it is exempt.
43
+ - Runtime-bound conditions (parameter values, `os.path.isdir`, `Test-Path`, environment lookups) are not constant; state the runtime source.
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
+
46
+ **E5. Unused parameters**
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
+ - Tuple-unpack discards (`for path, _, _ in os.walk(...)`) are out of scope — E5 specifically scopes "function parameters never read"; state this exclusion explicitly.
49
+ - `*args` / `**kwargs` / TypeScript rest spreads: confirm at least one consumer (forwarded to another call, iterated, indexed) or mark the parameter unused.
50
+ - Cross-language parameter declarations (PowerShell `param(...)`, shell positional `$1..$N`, Bash `getopts`): confirm each named parameter has at least one body reference.
51
+ - Adversarial probes for proof-of-absence: (a) any test fixture parameter (e.g., `def test_x(tmp_path):`) declared but never used? (b) any non-Python script parameter declared but never referenced? (c) any CLI flag parsed by argparse / Click / Cobra but unreachable on every invocation path the artifact actually uses?
52
+
53
+ **E6. Removed-but-not-deleted symbol references**
54
+ - If the artifact deletes or renames a symbol, confirm every import, call site, string reference, and docstring/comment mentioning the old name has been updated or removed.
55
+ - If the artifact is purely additive (no `-` lines outside new files), state this explicitly so the sub-bucket is provably empty rather than skipped.
56
+ - Forward references inside the same artifact (file A imports a symbol that the same artifact introduces in file B) are NOT stale; flag the pair so a reader can verify the resolution.
57
+ - Adversarial probes for proof-of-absence: (a) does any string literal name a symbol from another module that no longer exists? (b) does any docstring or comment reference a deprecated function? (c) does any external manifest (entry points, route tables, fixture lists) reference a removed symbol?
58
+
59
+ **E7. Test fixtures / helpers defined but never used**
60
+ - For every test file in scope, enumerate `@pytest.fixture`, `@pytest.fixture(scope=...)`, `setUp` / `tearDown` methods, factory helpers, mock builders, and module-level test constants. Verify each has at least one consumer test.
61
+ - Module-level constants in test files: each must satisfy the file-global-constants use-count rule (≥2 references) OR be referenced by exactly one test plus one helper that the tests call.
62
+ - Helpers defined inside a single test body that are never called within that body are dead.
63
+ - Adversarial probes for proof-of-absence: (a) any test that defines a local helper (e.g., `def make_dir(...):`) and never calls it? (b) any imported test name from the production module that no test exercises? (c) any module-level constant whose call graph collapses to zero references after the diff?
64
+
65
+ **E8. Stub / placeholder code without TODO**
66
+ - Distinguish real-behavior `pass` / `continue` / empty-handler bodies (intentional swallowing of expected exceptions, no-op branches that exist to satisfy a contract) from scaffolding stubs.
67
+ - Real-behavior bodies do NOT require a TODO; the audit must state the rationale (e.g., "rmdir race with concurrent writer is intentionally swallowed").
68
+ - Scaffolding bodies (`pass`, `...`, `raise NotImplementedError`, empty `else { }`, single-statement `return None` placeholders) without a `# TODO` comment ARE Category E findings under the project's "Document Temporary Code" rule.
69
+ - Adversarial probes for proof-of-absence: (a) any empty brace block in PowerShell / TypeScript / Go (`{ }` with no statements)? (b) any function whose entire body is `pass` / `return` / `return None`? (c) any branch that exits cleanly only because the surrounding loop is no-op for an empty input — is the no-op intentional or a placeholder?
70
+
71
+ ## Cross-bucket questions to answer at the end
72
+
73
+ Q1: Are there imports unused locally but consumed by a re-export pattern in another file? Cite the cross-file pair if found, or state the hypothesis "none — neither file declares `__all__`" with the supporting evidence.
74
+
75
+ Q2: What is the worst unused-code hazard introduced by this artifact? Cite `<file>:<line>`. Evaluate candidates by (a) whether the dead branch is unreachable on every code path, (b) whether the dead branch is unreachable only on the dominant invocation path but reachable elsewhere, and (c) whether the symbol is parsed but never consumed. Decide P1 vs P2 explicitly.
76
+
77
+ Q3: Which symbol most likely will *become* dead code after a near-future refactor? Identify call-graph leaves with a single consumer where the consumer is itself volatile (single-call-site helpers, constants whose only consumer is a flag the team is debating removing, callbacks tied to a library API that may be replaced).
78
+
79
+ ## Output
80
+
81
+ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket E1-E8, produce Shape A or Shape B (with ≥3 probes). Cross-bucket Q1-Q3 answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 P2 dead-code instances across these 8 sub-buckets — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
82
+
83
+ Note: most Category E findings are P2 (style / cleanup) unless the dead code masks an actual bug; the adversarial-pass quota uses P2 here.
84
+
85
+ ---
86
+
87
+ # Worked example: jl-cmd/claude-code-config PR #394
88
+
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
+
91
+ PR: feat(scripts): add sweep-empty-dirs utility and scheduled-task installer
92
+ Head SHA: 62c9c169ee7a44824e5da25c4cf8b74fdca08a53
93
+ ID prefix: `find`.
94
+
95
+ ## Sub-buckets (each requires Shape A finding OR Shape B with ≥3 adversarial probes)
96
+
97
+ **E1. New imports without references**
98
+ - Walk every `import` line introduced by this PR.
99
+ - `sweep_empty_dirs.py` imports: `argparse` (line 5), `os` (line 6), `sys` (line 7), `time` (line 8), `from config.sweep_config import DEFAULT_AGE_SECONDS` (line 10), `from config.sweep_config import DEFAULT_POLL_INTERVAL` (line 11). For each, locate at least one body reference: `argparse.ArgumentParser` in `_build_parser`; `os.walk` / `os.path.getctime` / `os.rmdir` / `os.path.isdir` in `sweep` and `main`; `sys.stderr` / `sys.exit` in `_log_walk_error` and `main`; `time.time()` in `sweep` and `time.sleep` in `main`; `DEFAULT_AGE_SECONDS` and `DEFAULT_POLL_INTERVAL` in `_build_parser`'s `default=` kwargs.
100
+ - `test_sweep_empty_dirs.py` imports: `datetime` (line 5), `os` (line 6), `subprocess` (line 7), `sys` (line 8), `tempfile` (line 9), `time` (line 10), `from pathlib import Path` (line 11), `from sweep_empty_dirs import sweep` (line 16). For each, locate at least one body reference: `datetime.datetime.fromtimestamp` and `datetime.timezone.utc` in `_set_creation_time_windows`; `os.path.join` / `os.mkdir` / `os.makedirs` / `os.path.isdir` across all five tests; `subprocess.run` in `_set_creation_time_windows`; `sys.path.insert` at lines 13-14; `tempfile.TemporaryDirectory` across all five tests; `time.time()` across multiple tests; `Path(...).write_text` in `test_skips_nonempty_dir` and `Path(__file__)` at line 12; `sweep` across all five tests.
101
+ - `__all__` re-exports — neither file defines `__all__`; this exemption is inert.
102
+ - `# noqa` markers — exactly one: `from sweep_empty_dirs import sweep # noqa: E402` (line 16 of test file). Verify the marker is justified by the preceding `sys.path.insert` block (it is — E402 covers module-level import not at top of file, which is exactly what this is).
103
+ - `TYPE_CHECKING` blocks — none in either file; exemption inert.
104
+ - `config/sweep_config.py` declares two module-level constants and no imports — nothing to sweep here.
105
+
106
+ **E2. Functions / methods defined but never called**
107
+ - `_log_walk_error(os_error: OSError) -> None` (line 14 of `sweep_empty_dirs.py`) — passed as `onerror=` to `os.walk` at line 21. The leading underscore is the only signal that this is a callback rather than a public API; verify no other call sites exist.
108
+ - `sweep(root: str, min_age_seconds: int) -> list[str]` (line 18) — called by `main` (line 60, 66) and by every one of the five test functions in `test_sweep_empty_dirs.py`.
109
+ - `_build_parser() -> argparse.ArgumentParser` (line 39) — called by `main` at line 51. Single call site; verify nothing in the broader codebase imports `_build_parser` (the leading underscore signals private-by-convention).
110
+ - `main() -> None` (line 50) — called by the `if __name__ == "__main__":` block at lines 71-72.
111
+ - `_set_creation_time_windows(path: str, timestamp: float) -> None` (line 19 of test file) — called from `test_deletes_empty_dir_older_than_threshold` (line 30), `test_deletes_nested_empty_dirs` (lines 49-51), and `test_empty_root_does_not_crash` (line 60). Three call sites; not dead.
112
+ - Each of the five `test_*` functions — called by pytest's collector; verify the file's path matches the project's pytest collection pattern (`scripts/tests/test_*.py`).
113
+
114
+ **E3. Code after unconditional return / raise / exit**
115
+ - `sweep_empty_dirs.py` line 60: `sys.exit(1)` is followed by no statements at the same indentation level inside `main`'s `if not os.path.isdir(...)` block — the `if` body ends and execution flows to the `if arguments.once:` check.
116
+ - `sweep_empty_dirs.py` line 64: `return` inside `if arguments.once:` is the last statement of that branch.
117
+ - `sweep_empty_dirs.py` line 70: `print("\nstopped.")` is the last statement of the `except KeyboardInterrupt` handler — verify nothing follows at the `try` block's indentation level.
118
+ - `sweep_empty_dirs.py` line 72: `main()` is the last statement at module scope after the `if __name__ == "__main__":` guard — nothing follows.
119
+ - `Install-SweepEmptyDirs.ps1` lines 27, 44, 53, 62, 69: every `exit 1` and every `return` (lines 38, 44, 49) — verify nothing executable follows at the same scope.
120
+ - Adversarial probes for proof-of-absence: (a) is there any `print` or assignment at module scope after `if __name__ == "__main__": main()`? (b) inside `sweep`, after the `except OSError: pass` block at line 30, is there a statement that could only run if the exception handler fell through? (c) the `for` loop at line 21 has no `else:` clause — confirm none was intended.
121
+
122
+ **E4. Always-true / always-false conditions**
123
+ - `sweep_empty_dirs.py` line 67: `while True:` — intentional infinite loop, exited only via `KeyboardInterrupt`. Not dead by E4 standards (it is the canonical watch-loop pattern).
124
+ - `sweep_empty_dirs.py` line 26: `if now - created >= min_age_seconds:` — both operands are runtime values; no constant reduction.
125
+ - `sweep_empty_dirs.py` line 53: `if not os.path.isdir(arguments.root):` — runtime check, not constant.
126
+ - `sweep_empty_dirs.py` line 57: `if arguments.once:` — bound to `--once` switch; runtime.
127
+ - `Install-SweepEmptyDirs.ps1` lines 14, 25, 31, 36, 41, 46: each `if (...)` checks a parameter or a `Test-Path` result; none reduces to a constant.
128
+ - `test_sweep_empty_dirs.py` line 13: `if str(_SCRIPTS_DIR) not in sys.path:` — runtime membership test; not constant.
129
+ - 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
+ **E5. Unused parameters**
132
+ - `_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
+ - `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
+ - `_build_parser() -> argparse.ArgumentParser` (line 39) — zero parameters; nothing to verify.
135
+ - `main() -> None` (line 50) — zero parameters; nothing to verify.
136
+ - `_set_creation_time_windows(path: str, timestamp: float) -> None` (line 19 of test file) — `path` interpolated into the PowerShell command (line 22); `timestamp` used at line 20. Both used.
137
+ - The `for each_directory_path, _, _ in os.walk(...)` tuple unpack at line 21 of `sweep_empty_dirs.py` — the two `_` placeholders discard `dirnames` and `filenames` from the `os.walk` 3-tuple. This is the standard Python idiom for "this loop only cares about the directory path", not an unused-parameter finding. **E5 specifically scopes "function parameters never read"; tuple-unpack discards are out of scope.**
138
+ - No function in this PR accepts `*args` or `**kwargs`.
139
+ - Adversarial probes for proof-of-absence: (a) does any test function declare a fixture parameter (e.g., `def test_x(tmp_path):`) that is never used? — none of the five tests use pytest fixtures; they each construct their own `tempfile.TemporaryDirectory()`. (b) does the PowerShell script declare any `param(...)` entry that is never referenced in the body? — `$Target` (used at lines 41-44, 53), `$IntervalMinutes` (used at line 51, 53), `$AgeSeconds` (used at line 50, 53), `$Remove` (used at line 25), `$Status` (used at line 14). All five used. (c) does `New-ScheduledTaskAction -Argument "$ScriptPath --once --age $AgeSeconds ""$Target"""` consume every flag? — `--once`, `--age $AgeSeconds`, and the positional `$Target` map to argparse `args.once`, `args.age`, `args.root` respectively; `args.interval` is NOT passed because `--once` exits before the loop starts. `args.interval` is therefore parsed-but-ignored when invoked through the scheduled task, but it IS used on the manual `python sweep_empty_dirs.py <root>` watch path (line 65, 68). Not dead — used by the non-task code path.
140
+
141
+ **E6. Removed-but-not-deleted symbol references**
142
+ - This PR adds 4 new files; it removes nothing. There is no rename, no deletion, no shim left behind.
143
+ - Verify by scanning the diff for any `-` line in any file outside the four new files — the PR's diff scope is purely additive at the file level (every change is `+` against `/dev/null`).
144
+ - `from sweep_empty_dirs import sweep` at line 16 of the test file is a forward reference to a symbol the same PR introduces — not a stale reference.
145
+ - `from config.sweep_config import DEFAULT_AGE_SECONDS` and `DEFAULT_POLL_INTERVAL` at lines 10-11 of the main script are forward references to symbols this same PR introduces in `config/sweep_config.py` — not stale.
146
+ - Adversarial probes for proof-of-absence: (a) does any string in the four files name a symbol from another module that no longer exists? — the only cross-file string references are the import paths above, both of which resolve. (b) does any docstring or comment reference a deprecated function? — no. (c) does the PowerShell `New-ScheduledTaskAction -Execute $PythonPath` reference a python that may not exist? — that is a runtime / installer concern, not a Category E removed-symbol concern; out of scope here.
147
+
148
+ **E7. Test fixtures / helpers defined but never used**
149
+ - `test_sweep_empty_dirs.py` defines no `@pytest.fixture` decorators. Nothing to flag under "fixture with no consumers".
150
+ - `_set_creation_time_windows` is the only test helper; it has three call sites (E2 above). Used.
151
+ - `_SCRIPTS_DIR` (line 12) is a module-level constant used at line 13 (`if str(_SCRIPTS_DIR) not in sys.path:`) and line 14 (`sys.path.insert(0, str(_SCRIPTS_DIR))`). Two references; satisfies the file-global-constants use-count rule.
152
+ - No test data builders or mock factories are defined in this PR.
153
+ - Adversarial probes for proof-of-absence: (a) does any of the five tests define a local helper inside its body that is then never called? (e.g., a `def make_dir(...):` defined but unused) — scan each test body. (b) does any test import a name from `sweep_empty_dirs` that it never uses? — only `sweep` is imported, and every test calls it. (c) does the `_SCRIPTS_DIR` block survive if pytest is invoked with `sweep_empty_dirs.py` already on `sys.path`? — yes, the membership-test guards the insert, so the constant is still meaningfully consumed even on the second run.
154
+
155
+ **E8. Stub / placeholder code without TODO**
156
+ - `sweep_empty_dirs.py` line 30: `except OSError: pass` — the `pass` is the entire handler body, intentionally swallowing rmdir failures (e.g., directory became non-empty between the walk and the rmdir, race with another writer). This is a real-behavior `pass`, not a stub. No TODO is required because the handler IS the intended behavior.
157
+ - `sweep_empty_dirs.py` line 28: the second `except OSError: continue` similarly is intended behavior (skip the directory whose ctime is unreadable), not a stub.
158
+ - No `...` literal anywhere in the four files.
159
+ - No `raise NotImplementedError` anywhere.
160
+ - No `# TODO` markers in the diff — the project's own rule (`code-standards.md` → "Document Temporary Code") requires TODOs only for scaffolding/placeholder code. The two `pass`/`continue` bodies above are production behavior, not scaffolding.
161
+ - Adversarial probes for proof-of-absence: (a) does the PowerShell script have an empty `else { }` or empty branch body? — scan lines 14-71 for any `{ }` with no statements between the braces. (b) does any function body consist of a single `pass` or `return` with no work done? — every function body in this PR performs at least one statement. (c) does the `Status` branch (lines 14-31) exit cleanly even when `$task.Triggers` is empty? — the `foreach` loop at line 26 is a no-op for an empty collection, which is correct behavior, not a stub.
162
+
163
+ ## Cross-bucket questions to answer at the end
164
+
165
+ Q1: Are there imports unused locally but consumed by a re-export pattern in another file? Cite the cross-file pair if found. (Hypothesis: none — neither `sweep_empty_dirs.py` nor `test_sweep_empty_dirs.py` defines `__all__`, so re-export is not in play. `config/sweep_config.py` declares two constants that ARE consumed by `sweep_empty_dirs.py` lines 10-11; this is normal cross-file consumption, not a re-export.)
166
+ Q2: What's the worst unused-code hazard introduced by this PR? Cite `<file>:<line>`. Candidates to evaluate: `arguments.interval` is parsed but unreachable on the `--once` path (line 57 short-circuits before the watch loop at line 67); the scheduled task always uses `--once`, so the `--interval` argparse declaration at line 45 is dead code on the only invocation path the installer creates. Decide: P2 (style) because the manual watch-mode path still uses it, vs P1 if you treat "the only installer path never exercises this branch" as functionally dead.
167
+ Q3: Which symbol most likely will *become* dead code after a near-future refactor? Candidates: `_log_walk_error` (sole call site is the `os.walk(..., onerror=...)` kwarg — if a future refactor switches to `pathlib.Path.rglob` for walking, this helper has no other consumer and silently becomes orphaned); `DEFAULT_POLL_INTERVAL` (sole consumer is `_build_parser`'s `--interval` default — if Q2's hazard is resolved by removing `--interval` from the `--once`-only installer flow, this constant has zero consumers in the script and the file-global-constants use-count rule is broken).
168
+
169
+ ## Output
170
+
171
+ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket E1-E8, produce Shape A or Shape B (with ≥3 probes). Cross-bucket Q1-Q3 answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 P2 dead-code instances across these 8 sub-buckets — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
172
+
173
+ Note: most Category E findings are P2 (style / cleanup) unless the dead code masks an actual bug; the adversarial-pass quota uses P2 here.
174
+
175
+ ## Diff (4 new files, all lines in scope)
176
+
177
+ ### packages/claude-dev-env/scripts/sweep_empty_dirs.py (102 lines)
178
+ ```python
179
+ #!/usr/bin/env python3
180
+ """Delete empty directories older than 2 minutes under a given root."""
181
+
182
+ import argparse
183
+ import os
184
+ import sys
185
+ import time
186
+
187
+ from config.sweep_config import DEFAULT_AGE_SECONDS
188
+ from config.sweep_config import DEFAULT_POLL_INTERVAL
189
+
190
+
191
+ def _log_walk_error(os_error: OSError) -> None:
192
+ print(f"warning: cannot scan {os_error.filename} — {os_error.strerror}", file=sys.stderr)
193
+
194
+
195
+ def sweep(root: str, min_age_seconds: int) -> list[str]:
196
+ """Remove empty directories under *root* older than *min_age_seconds*."""
197
+
198
+ now = time.time()
199
+ removed: list[str] = []
200
+
201
+ for each_directory_path, _, _ in os.walk(
202
+ root, onerror=_log_walk_error, topdown=False
203
+ ):
204
+ try:
205
+ created = os.path.getctime(each_directory_path)
206
+ except OSError:
207
+ continue
208
+ if now - created >= min_age_seconds:
209
+ try:
210
+ os.rmdir(each_directory_path)
211
+ print(f"deleted: {each_directory_path}")
212
+ removed.append(each_directory_path)
213
+ except OSError:
214
+ pass
215
+
216
+ return removed
217
+
218
+
219
+ def _build_parser() -> argparse.ArgumentParser:
220
+ parser = argparse.ArgumentParser(description="Delete empty directories older than a given age.")
221
+ parser.add_argument("root", help="Root directory to scan")
222
+ parser.add_argument("--age", type=int, default=DEFAULT_AGE_SECONDS,
223
+ help=f"Minimum age in seconds (default: {DEFAULT_AGE_SECONDS} = 2 minutes)")
224
+ parser.add_argument("--once", action="store_true",
225
+ help="Single pass and exit instead of watching in a loop")
226
+ parser.add_argument("--interval", type=int, default=DEFAULT_POLL_INTERVAL,
227
+ help=f"Poll interval in seconds when looping (default: {DEFAULT_POLL_INTERVAL})")
228
+ return parser
229
+
230
+
231
+ def main() -> None:
232
+ parser = _build_parser()
233
+ arguments = parser.parse_args()
234
+
235
+ if not os.path.isdir(arguments.root):
236
+ print(f"error: not a directory: {arguments.root}", file=sys.stderr)
237
+ sys.exit(1)
238
+
239
+ if arguments.once:
240
+ sweep(arguments.root, arguments.age)
241
+ return
242
+
243
+ print(f"watching {arguments.root} every {arguments.interval}s (age threshold: {arguments.age}s)")
244
+ try:
245
+ while True:
246
+ sweep(arguments.root, arguments.age)
247
+ time.sleep(arguments.interval)
248
+ except KeyboardInterrupt:
249
+ print("\nstopped.")
250
+
251
+
252
+ if __name__ == "__main__":
253
+ main()
254
+ ```
255
+
256
+ ### packages/claude-dev-env/scripts/config/sweep_config.py (11 lines)
257
+ ```python
258
+ """Centralized timing configuration for sweep_empty_dirs."""
259
+
260
+ DEFAULT_AGE_SECONDS: int = 120
261
+ DEFAULT_POLL_INTERVAL: int = 30
262
+ ```
263
+
264
+ ### packages/claude-dev-env/scripts/tests/test_sweep_empty_dirs.py (88 lines)
265
+ ```python
266
+ """Tests for sweep_empty_dirs.py"""
267
+
268
+ from __future__ import annotations
269
+
270
+ import datetime
271
+ import os
272
+ import subprocess
273
+ import sys
274
+ import tempfile
275
+ import time
276
+ from pathlib import Path
277
+
278
+ _SCRIPTS_DIR = Path(__file__).resolve().parent.parent
279
+ if str(_SCRIPTS_DIR) not in sys.path:
280
+ sys.path.insert(0, str(_SCRIPTS_DIR))
281
+
282
+ from sweep_empty_dirs import sweep # noqa: E402
283
+
284
+
285
+ def _set_creation_time_windows(path: str, timestamp: float) -> None:
286
+ dt = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
287
+ date_str = dt.strftime("%Y-%m-%d %H:%M:%S")
288
+ subprocess.run(
289
+ ["powershell", "-Command",
290
+ f"(Get-Item '{path}').CreationTimeUtc = [DateTime]'{date_str}'"],
291
+ check=True, capture_output=True,
292
+ )
293
+
294
+
295
+ def test_deletes_empty_dir_older_than_threshold() -> None:
296
+ with tempfile.TemporaryDirectory() as tmp:
297
+ empty_dir = os.path.join(tmp, "old_empty")
298
+ os.mkdir(empty_dir)
299
+ _set_creation_time_windows(empty_dir, time.time() - 300)
300
+ removed = sweep(tmp, min_age_seconds=120)
301
+ assert empty_dir in removed
302
+ assert not os.path.isdir(empty_dir)
303
+
304
+
305
+ def test_skips_empty_dir_newer_than_threshold() -> None:
306
+ with tempfile.TemporaryDirectory() as tmp:
307
+ fresh_dir = os.path.join(tmp, "fresh_empty")
308
+ os.mkdir(fresh_dir)
309
+ removed = sweep(tmp, min_age_seconds=120)
310
+ assert fresh_dir not in removed
311
+ assert os.path.isdir(fresh_dir)
312
+
313
+
314
+ def test_deletes_nested_empty_dirs() -> None:
315
+ with tempfile.TemporaryDirectory() as tmp:
316
+ leaf = os.path.join(tmp, "parent", "child", "leaf")
317
+ os.makedirs(leaf)
318
+ _set_creation_time_windows(os.path.join(tmp, "parent"), time.time() - 300)
319
+ _set_creation_time_windows(os.path.join(tmp, "parent", "child"), time.time() - 300)
320
+ _set_creation_time_windows(leaf, time.time() - 300)
321
+ removed = sweep(tmp, min_age_seconds=120)
322
+ assert leaf in removed
323
+ assert os.path.join(tmp, "parent", "child") in removed
324
+ assert os.path.join(tmp, "parent") in removed
325
+
326
+
327
+ def test_empty_root_does_not_crash() -> None:
328
+ with tempfile.TemporaryDirectory() as tmp:
329
+ _set_creation_time_windows(tmp, time.time() - 300)
330
+ sweep(tmp, min_age_seconds=120)
331
+
332
+
333
+ def test_skips_nonempty_dir() -> None:
334
+ with tempfile.TemporaryDirectory() as tmp:
335
+ nonempty_dir = os.path.join(tmp, "has_stuff")
336
+ os.mkdir(nonempty_dir)
337
+ Path(nonempty_dir, "keepme.txt").write_text("hello")
338
+ removed = sweep(tmp, min_age_seconds=0)
339
+ assert nonempty_dir not in removed
340
+ assert os.path.isdir(nonempty_dir)
341
+ ```
342
+
343
+ ### packages/claude-dev-env/scripts/Install-SweepEmptyDirs.ps1 (90 lines)
344
+ ```powershell
345
+ #!/usr/bin/env pwsh
346
+ param(
347
+ [Parameter(ParameterSetName = "install")]
348
+ [string]$Target,
349
+
350
+ [Parameter(ParameterSetName = "install")]
351
+ [int]$IntervalMinutes = 5,
352
+
353
+ [Parameter(ParameterSetName = "install")]
354
+ [int]$AgeSeconds = 120,
355
+
356
+ [Parameter(ParameterSetName = "remove")]
357
+ [switch]$Remove,
358
+
359
+ [Parameter(ParameterSetName = "status")]
360
+ [switch]$Status
361
+ )
362
+
363
+ $TaskName = "SweepEmptyDirs"
364
+
365
+ if ($Status) {
366
+ $task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue
367
+ if (-not $task) {
368
+ Write-Host "STATUS: $TaskName is not registered."
369
+ return
370
+ }
371
+ Write-Host "STATUS: $TaskName is registered."
372
+ Write-Host " State: $($task.State)"
373
+ Write-Host " Actions:"
374
+ foreach ($action in $task.Actions) {
375
+ Write-Host " $($action.Execute) $($action.Arguments)"
376
+ }
377
+ Write-Host " Triggers:"
378
+ foreach ($trigger in $task.Triggers) {
379
+ Write-Host " $($trigger.Repetition.Interval) (starting $($trigger.StartBoundary))"
380
+ }
381
+ return
382
+ }
383
+
384
+ if ($Remove) {
385
+ Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue
386
+ Write-Host "$TaskName removed."
387
+ return
388
+ }
389
+
390
+ $ScriptDir = Split-Path -Parent $PSCommandPath
391
+ $ScriptPath = Join-Path $ScriptDir "sweep_empty_dirs.py"
392
+
393
+ if (-not (Test-Path $ScriptPath)) {
394
+ Write-Error "sweep_empty_dirs.py not found at: $ScriptPath"
395
+ exit 1
396
+ }
397
+
398
+ if (-not $Target) {
399
+ Write-Error "Parameter -Target is required (the directory to watch)."
400
+ exit 1
401
+ }
402
+
403
+ if (-not (Test-Path $Target)) {
404
+ Write-Error "Target directory does not exist: $Target"
405
+ exit 1
406
+ }
407
+
408
+ $_py = Get-Command py -ErrorAction SilentlyContinue
409
+ $PythonPath = if ($_py) { $_py.Source } else { (Get-Command python).Source }
410
+ if (-not $PythonPath) {
411
+ Write-Error "Cannot find Python (py or python) on PATH."
412
+ exit 1
413
+ }
414
+ $Action = New-ScheduledTaskAction -Execute $PythonPath -Argument "$ScriptPath --once --age $AgeSeconds ""$Target"""
415
+ $Trigger = New-ScheduledTaskTrigger -Daily -At "00:00" -RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes)
416
+ $Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
417
+
418
+ Register-ScheduledTask -TaskName $TaskName -Action $Action -Trigger $Trigger -Settings $Settings -Force | Out-Null
419
+ Write-Host "$TaskName registered — runs every ${IntervalMinutes}min against '$Target' (age ≥ ${AgeSeconds}s)."
420
+ ```