claude-dev-env 8.1.0 → 8.3.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 (122) hide show
  1. package/.agents/agents/poteto-agent.md +6 -0
  2. package/.agents/agents/test_agent_frontmatter.py +9 -9
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -1
  4. package/.agents/agents-archived/ARCHIVE-MANIFEST.md +15 -0
  5. package/.agents/{agents → agents-archived}/clean-coder.md +4 -4
  6. package/.agents/{agents → agents-archived}/code-quality-agent.md +5 -5
  7. package/.agents/{agents → agents-archived}/plan-packet-validator.md +2 -2
  8. package/.agents/{agents → agents-archived}/session-advisor.md +2 -2
  9. package/.agents/skills/_shared/end-of-run-gotcha-recommendations.md +2 -2
  10. package/.agents/skills/cert-classification-rule/SKILL.md +3 -3
  11. package/.agents/skills/e-code-review/SKILL.md +2 -2
  12. package/.agents/skills/e-code-review/reference/fix.md +1 -1
  13. package/.agents/skills/e-code-review/reference/loop.md +1 -1
  14. package/.agents/skills/e-code-review/reference/medium.md +4 -4
  15. package/.agents/skills/e-code-review/reference/xhigh.md +5 -5
  16. package/.agents/skills/everything-search/SKILL.md +3 -3
  17. package/.agents/skills/grok-spawn/SKILL.md +9 -6
  18. package/.agents/skills/grok-spawn/reference/flag-profiles.md +12 -8
  19. package/.agents/skills/issue-tracker/SKILL.md +3 -3
  20. package/.agents/skills/orchestrator/SKILL.md +19 -18
  21. package/.agents/skills/orchestrator/reference/consult-the-orchestrator.md +1 -1
  22. package/.agents/skills/orchestrator/reference/executor-consult-block.md +1 -1
  23. package/.agents/skills/orchestrator-refresh/SKILL.md +4 -2
  24. package/.agents/skills/plugin-eval-standalone-skill/SKILL.md +96 -0
  25. package/.agents/skills/plugin-eval-standalone-skill/reference/graders-and-commands.md +108 -0
  26. package/.agents/skills/privacy-hygiene/SKILL.md +5 -6
  27. package/.agents/skills/privacy-hygiene/reference/sweep-procedure.md +5 -6
  28. package/.agents/skills/pull-request/SKILL.md +14 -5
  29. package/.agents/skills/pull-request/reference/publication-tasks.md +1 -1
  30. package/.agents/skills/repairing-hook-boundaries/SKILL.md +1 -1
  31. package/.agents/skills/syncing-submodules/SKILL.md +1 -1
  32. package/.agents/skills/team-advisor/SKILL.md +11 -9
  33. package/.agents/skills/team-advisor/reference/advisor-docs-review.md +28 -1
  34. package/.agents/skills/usage-pause/SKILL.md +3 -3
  35. package/_shared/advisor/advisor-protocol.md +7 -3
  36. package/_shared/advisor/reference/advisor-block.md +8 -1
  37. package/audit-rubrics/category_rubrics/category-f-silent-failures.md +2 -2
  38. package/audit-rubrics/category_rubrics/category-i-concurrency.md +1 -1
  39. package/audit-rubrics/category_rubrics/category-m-producer-consumer-cardinality.md +1 -1
  40. package/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md +5 -5
  41. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +5 -5
  42. package/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md +3 -3
  43. package/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md +1 -1
  44. package/audit-rubrics/prompts/category-a-api-contracts.md +2 -2
  45. package/audit-rubrics/prompts/category-b-selector-engine-compat.md +3 -3
  46. package/audit-rubrics/prompts/category-c-resource-cleanup.md +3 -3
  47. package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +1 -1
  48. package/audit-rubrics/prompts/category-e-dead-code.md +3 -3
  49. package/audit-rubrics/prompts/category-f-silent-failures.md +12 -12
  50. package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
  51. package/audit-rubrics/prompts/category-h-security-boundaries.md +4 -4
  52. package/audit-rubrics/prompts/category-i-concurrency.md +6 -6
  53. package/audit-rubrics/prompts/category-j-code-rules-compliance.md +5 -5
  54. package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +1 -1
  55. package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +7 -7
  56. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
  57. package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +3 -3
  58. package/audit-rubrics/prompts/category-q-cross-surface-claims.md +2 -2
  59. package/bin/ever-shipped-skills.mjs +1 -0
  60. package/bin/ever-shipped-skills.test.mjs +4 -0
  61. package/bin/install.agents-home.test.mjs +44 -94
  62. package/bin/install.mjs +31 -24
  63. package/bin/install.test.mjs +21 -0
  64. package/bin/resolve-package-managed-directory.test.mjs +1 -1
  65. package/bin/subagent-model-policy.test.mjs +15 -8
  66. package/docs/BDD_SCENARIO_QUALITY.md +2 -2
  67. package/docs/CODE_RULES.md +7 -7
  68. package/docs/TEST_QUALITY.md +1 -1
  69. package/docs/nas-ssh-invocation.md +1 -1
  70. package/docs/references/advisor-tool.md +2 -2
  71. package/docs/references/prose-style-enforcement.md +1 -1
  72. package/docs/references/team-advisor-skill.md +2 -2
  73. package/docs/references/weak-executor-advisor.md +1 -1
  74. package/docs/subagent-model-routing.md +6 -5
  75. package/hooks/blocking/tdd_enforcer.py +2 -3
  76. package/hooks/blocking/test_bash_post_call_dispatcher.py +7 -24
  77. package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
  78. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +0 -3
  79. package/hooks/hooks_constants/hosted_hook_runner.py +1 -1
  80. package/hooks/hooks_constants/msys_path_conversion_advisor_constants.py +3 -35
  81. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +2 -10
  82. package/hooks/routing/subagent_model_routing.mjs +15 -0
  83. package/hooks/routing/subagent_model_routing.test.mjs +58 -25
  84. package/package.json +1 -1
  85. package/rules/archiving-agent-config.md +5 -8
  86. package/rules/git-workflow.md +3 -3
  87. package/rules/long-horizon-autonomy.md +1 -1
  88. package/rules/shell-invocation.md +0 -2
  89. package/rules/subagent-model-policy.json +5 -13
  90. package/rules/testing.md +0 -2
  91. package/scripts/active_capability_references.py +74 -19
  92. package/scripts/banned_prose_words.py +159 -0
  93. package/scripts/dev_env_scripts_constants/active_capability_constants.py +12 -10
  94. package/scripts/dev_env_scripts_constants/banned_prose_word_constants.py +95 -0
  95. package/scripts/dev_env_scripts_constants/grok_worker_constants.py +3 -3
  96. package/scripts/grok_headless_runner.py +19 -1
  97. package/scripts/grok_worker_preflight.py +67 -1
  98. package/scripts/policy_lint/adapter_retired_hook_prose.py +17 -7
  99. package/scripts/policy_lint/adapters.py +6 -6
  100. package/scripts/policy_lint/config/constants.py +8 -0
  101. package/scripts/policy_lint/registry.py +1 -1
  102. package/scripts/resolve_advisor_model_route.mjs +2 -2
  103. package/scripts/subagent_model_policy.mjs +4 -1
  104. package/scripts/sync_to_cursor/rules.py +1 -1
  105. package/scripts/test_active_capability_references.py +100 -33
  106. package/scripts/test_grok_headless_runner.py +35 -1
  107. package/scripts/test_grok_worker_preflight.py +73 -0
  108. package/scripts/tests/resolve_advisor_model_route.test.mjs +3 -3
  109. package/scripts/tests/test_banned_prose_words.py +103 -0
  110. package/scripts/tests/test_engine.py +1 -1
  111. package/scripts/tests/test_policy_lint_rules_retired_hook_prose.py +46 -0
  112. package/scripts/tests/test_rules.py +17 -6
  113. package/system-prompts/software-engineer.xml +7 -7
  114. package/hooks/advisory/msys_path_conversion_advisor.py +0 -200
  115. package/hooks/advisory/test_msys_path_conversion_advisor.py +0 -223
  116. package/hooks/hooks_constants/test_failure_recorder_constants.py +0 -34
  117. package/hooks/observability/test_failure_recorder.py +0 -221
  118. package/hooks/observability/test_test_failure_recorder.py +0 -173
  119. /package/.agents/{agents → agents-archived}/git-commit-crafter.md +0 -0
  120. /package/.agents/{agents → agents-archived}/issue-tracker.md +0 -0
  121. /package/.agents/{agents → agents-archived}/pr-description-writer.md +0 -0
  122. /package/.agents/{agents → agents-archived}/skill-writer-agent.md +0 -0
@@ -41,4 +41,4 @@ The reusable Variant C template for Category M is in [`../prompts/category-m-pro
41
41
 
42
42
  Categories A–K each examine one side of an interface in isolation. Category M examines the cardinality contract spanning two sides: the producer's "can my return value contain duplicates?" question and the consumer's "do I tolerate duplicates?" answer. A reviewer walking only A–K reads the producer, finds it correct on its own terms, and approves it — then reads the consumer separately and finds it also correct on its own terms. The bug emerges only when the two are exercised against the same input together.
43
43
 
44
- The pa#143 F10 case is the canonical worked example: `_extract_paths_from_everything_cli_stdout` returned `list[Path]` by walking the `es.exe` stdout line-by-line. The consumer iterated the list directly and ran one `INSERT INTO watched_dirs(path) VALUES (...)` per element against a table with a `UNIQUE(path)` constraint. When the subprocess emitted the same path on two lines (a real edge case — Everything's stdout can repeat results across drive letters), the list carried the path twice, so the writeback ran the same `INSERT` twice; the second `INSERT` raised `sqlite3.IntegrityError: UNIQUE constraint failed: watched_dirs.path` and the entire watchdog crashed. The fix was to change the producer to `frozenset[Path]`, which deduplicates at the boundary so each path reaches the writeback once; the consumer's per-element `INSERT` loop was already correct. The contract was the bug.
44
+ The pa#143 F10 case is the canonical worked example: `_extract_paths_from_everything_cli_stdout` returned `list[Path]` by walking the `es.exe` stdout line-by-line. The consumer iterated the list directly and ran one `INSERT INTO watched_dirs(path) VALUES (...)` per element against a table with a `UNIQUE(path)` constraint. When the subprocess emitted the same path on two lines (an edge case — Everything's stdout can repeat results across drive letters), the list carried the path twice, so the writeback ran the same `INSERT` twice; the second `INSERT` raised `sqlite3.IntegrityError: UNIQUE constraint failed: watched_dirs.path` and the entire watchdog crashed. The fix was to change the producer to `frozenset[Path]`, which deduplicates at the boundary so each path reaches the writeback once; the consumer's per-element `INSERT` loop was already correct. The contract was the bug.
@@ -1,15 +1,15 @@
1
1
  # Category N — Test-name scenario verifier
2
2
 
3
- **What this category audits:** tests whose names assert a specific scenario (`test_*_when_*`, `test_*_at_*`, `test_*_under_*`, `test_*_with_*`) must demonstrate via fixture inspection, monkeypatch state, or environment setup that the named condition is in effect when the system under test runs. Common failure: a test named `test_resolves_under_unc_prefix` constructs a regular drive-letter path, asserts the function returns a non-empty result, and passes — but the UNC-prefix branch was never exercised. The test is a load-bearing witness that the named scenario works, but it actually only exercises the no-op default path.
3
+ **What this category audits:** tests whose names assert a specific scenario (`test_*_when_*`, `test_*_at_*`, `test_*_under_*`, `test_*_with_*`) must demonstrate via fixture inspection, monkeypatch state, or environment setup that the named condition is in effect when the system under test runs. Common failure: a test named `test_resolves_under_unc_prefix` constructs a regular drive-letter path, asserts the function returns a non-empty result, and passes — but the UNC-prefix branch was never exercised. The test is a load-bearing witness that the named scenario works, but it only exercises the no-op default path.
4
4
 
5
- **Why this category is its own bucket:** Categories A–K catch defects in the production code. Category N catches defects in the *test corpus's claim to coverage*. A green test suite is only meaningful when each test's name accurately describes what was exercised. When a test passes "for the wrong reason" — e.g., the assertion matches a substring that appears in both the scenario-named branch AND the default branch — the suite reports coverage of a scenario it never actually ran.
5
+ **Why this category is its own bucket:** Categories A–K catch defects in the production code. Category N catches defects in the *test corpus's claim to coverage*. A green test suite is only meaningful when each test's name accurately describes what was exercised. When a test passes "for the wrong reason" — e.g., the assertion matches a substring that appears in both the scenario-named branch AND the default branch — the suite reports coverage of a scenario it never ran.
6
6
 
7
7
  **Examples of Category N findings:**
8
8
  - Cross-platform tests named `test_*_on_windows` / `test_*_on_linux` that don't gate on `sys.platform` or `monkeypatch.setattr(os, "name", ...)` and run unchanged on every platform. (ccc#476 F5, F21, F23, F26, F27)
9
9
  - String-shape tests that exercise only the no-op branch (`assert result == ""` after constructing an input that hits the early-return path, not the named scenario). (pa#135 F11, F15)
10
10
  - Integration tests with assertions like `<substring> not in executed_sql` where the substring shape never matches the SQL fragment shape — the assertion cannot fail by construction. (pa#136 F50)
11
11
  - Path-decision tests for `is_test_file` / `is_hook_infrastructure` / `_resolve_*_path` without a parametric matrix of canonical edge cases (empty string, tilde-prefix, UNC, drive-letter, symlinked, `..`-containing, trailing slash).
12
- - A test resolves `Path(__file__).parents[3]` expecting the `claude-dev-env/` package root, but the parents chain actually stops at `skills/` — the test cannot fail for the right reason and the asserted directory wiring is broken by construction.
12
+ - A test resolves `Path(__file__).parents[3]` expecting the `claude-dev-env/` package root, but the parents chain stops at `skills/` — the test cannot fail for the right reason and the asserted directory wiring is broken by construction.
13
13
  - A test imports the same function twice under two names (`from path_utils import is_config_file` plus `from path_utils import is_config_file as path_utils_is_config_file`) and asserts the two bound names produce the same result — the assertion cannot fail because both names are the same function object; the appearance of two parallel implementations is fake.
14
14
 
15
15
  **Companion reference:** see `../source-material-section-types.md`.
@@ -26,12 +26,12 @@ Decomposition is by the **kind of scenario claim** the test name makes vs the ev
26
26
  | N2 | Path-decision parametric matrices | Tests for `is_*_path` / `_resolve_*_path` / `*_path_exemptions` modules ship a parametrized matrix covering: empty string, single filename, tilde-prefix, UNC path, drive-letter path, symlinked path, `..`-containing path, trailing-slash path |
27
27
  | N3 | Tests that pass "for the wrong reason" | The assertion's substring matches both the scenario-named branch and the default branch — the test cannot distinguish which branch executed |
28
28
  | N4 | No-op branch exercised by scenario name | The constructed input hits an early-return / guard / no-feature-installed branch BEFORE the scenario-named code runs; the assertion succeeds against the no-op output |
29
- | N5 | Assertion shape mismatch | The assertion shape (`<substring> not in result`, `result == ""`, `len(result) > 0`) cannot fail by construction against the actual return type / value space |
29
+ | N5 | Assertion shape mismatch | The assertion shape (`<substring> not in result`, `result == ""`, `len(result) > 0`) cannot fail by construction against the return type / value space |
30
30
  | N6 | Cross-platform scenario gating | Tests named `_on_<platform>` MUST gate on `sys.platform`, `monkeypatch.setattr(os, ...)`, or `@pytest.mark.skipif(sys.platform != "<platform>")` — bare scenario names that run unchanged across platforms claim more than they prove |
31
31
  | N7 | Time / clock scenario gating | Tests named `_after_<duration>` / `_at_midnight` / `_during_business_hours` MUST inject a frozen clock (`freezegun.freeze_time`, `monkeypatch.setattr(time, "time", ...)`) — wall-clock tests are non-deterministic and may pass against the wrong scenario |
32
32
  | N8 | Concurrent / load scenario gating | Tests named `_under_load` / `_with_concurrent_writers` MUST spawn the concurrent workers and `wait()` on them — single-threaded tests cannot claim concurrent-scenario coverage |
33
33
  | N9 | Neutral-named tests (out of scope) | Tests named `test_returns_empty_list_for_unknown_key` / `test_handles_y` (no scenario claim in the name) are NOT subject to N1–N8; flag them only for assertion-shape mismatches under N5 |
34
- | N10 | Test fixture wiring correctness | The test's fixture / path / import wiring resolves to the artifact the test name claims. Path arithmetic (`Path(__file__).parents[k]`) reaches the directory the assertion expects — verify by walking the parents chain symbolically. Same-symbol dual imports (`from m import f` plus `from m import f as f_alias`) bind two names to the same function object, so any parity assertion between them is true by construction. Fixture file lookups (`open(Path(__file__).parent / 'fixture.txt')`) reach a file that actually exists. |
34
+ | N10 | Test fixture wiring correctness | The test's fixture / path / import wiring resolves to the artifact the test name claims. Path arithmetic (`Path(__file__).parents[k]`) reaches the directory the assertion expects — verify by walking the parents chain symbolically. Same-symbol dual imports (`from m import f` plus `from m import f as f_alias`) bind two names to the same function object, so any parity assertion between them is true by construction. Fixture file lookups (`open(Path(__file__).parent / 'fixture.txt')`) reach a file that exists. |
35
35
 
36
36
  Customize per-artifact: a pure-function test corpus with no scenario claims reduces N1–N4 to "verified clean — no scenario-named tests in scope"; a path-classifier PR may need N2 exhausted across 8+ canonical inputs.
37
37
 
@@ -37,11 +37,11 @@ Decomposition is by the **kind of docstring claim** that needs to be cross-check
37
37
  | O2 | Fixture docstring vs sibling-test behavior | An autouse / module-scope fixture docstring asserts an invariant (`readability is disabled`, `network is mocked`, `tmp_path is empty`). No sibling test in the same module explicitly opts out of the invariant. |
38
38
  | O3 | Predicate-name and -docstring vs body breadth | A boolean helper's name and docstring promise a narrow predicate. Walk the body's branches: every branch's `return True` path is consistent with the promised name. Bodies that accept inputs broader than the name (`_dir_value_resolves_to_shared_temp` also accepting HOME/TMP env-derived paths) are O3 findings. |
39
39
  | O4 | Step-ordering narrative | A docstring describes processing as `A then B then C`. Walk the body and confirm the call order matches. Mismatched order is an O4 finding regardless of whether the final output is the same. A docstring step enumeration that names the body's linear steps but omits a corrective workflow step the body guards inside an `if`/`elif` branch (`if not await cancel_and_reinitiate_update(...): return`) is also an O4 finding: the reader trusts the step list to be complete and misses the conditional path. The branch-guarded-dispatch shape of this drift — a docstring that names two or more linear-step callees while the body guards a two-or-more-token dispatch callee inside a branch whose name the prose never spells out — stays judgment for this lane, alongside re-ordered steps and plain unguarded steps the prose omits. |
40
- | O5 | Named-sentinel / filename references | A docstring names a sentinel marker, environment variable, filename, or magic string. Confirm the named token actually exists in the module body or in the repo's naming convention. |
41
- | O6 | Free-form `Args:`-adjacent claims | A docstring's `Returns:` / `Raises:` / `Note:` / `Example:` sections make claims (`returns shared-temp only`, `raises ValueError on missing key`). Verify each claim against the body. When a docstring enumerates the inputs a body counts (a "field counts as read when ..." list, a list of conditions treated as a match, a list of cases the body skips), list every union member and every suppressor the body applies (`read_names = a \| b \| c`, each early-return guard) and confirm each appears in the prose enumeration. A union member or suppressor the body applies but the prose omits is an O6 finding. When a docstring sentence excludes a named category of input from what the function flags (`X are not dispatch steps`, `Y is not a match`), confirm the axis the prose excludes on is the axis the body's branch condition actually keys on. A body that flags a call when it sits inside an `If.test` guard, paired with prose that excludes by the call's receiver shape (`method-on-local calls inside a branch are not dispatch steps`), is an O6 finding: a guarded method-on-local call is flagged even though the prose lists it as excluded — the exclusion is keyed to the wrong axis. A thin delegating method whose docstring names its actions and points at the home of the real body lists the same actions the delegated function's own summary lists; when an edit moves one action out of the delegated body, the same edit rewords both summaries — judgment for this lane. A conditional bullet in the delegated prose also names every exception the body honors — that conditional-completeness slice stays a judgment call for this lane. A `Returns:` that names the mechanism, tool, or output format the function produces (`instructing a StructuredOutput summary`, `returns a YAML document`, `emits a JSON object`) matches the artifact the body actually builds. A dataclass or `TypedDict` field documented in the class `Attributes:` block states what the field means for one record; when the code sets that field the same way for every record (a run-mode flag such as `is_dry_run = not is_execute`), the description states the run-mode meaning, not a per-record outcome — judgment for this lane. A workflow gate-outcome status flag whose in-code prose (a schema property `description`, an architecture `detail`/overview string) describes the outcome as bypassing or skipping matches the branch that handles it: when the code routes that outcome to a blocker (`blocker = ...; break`) that holds the PR in draft, prose that reads "skips without blocking" or "the gate is bypassed" for that outcome is an O6 finding. The remaining deterministic O6 shape is gated at Write/Edit time — see **Write-time gate inventory** below — so the audit lane focuses on the free-form shapes the gate cannot match. |
40
+ | O5 | Named-sentinel / filename references | A docstring names a sentinel marker, environment variable, filename, or magic string. Confirm the named token exists in the module body or in the repo's naming convention. |
41
+ | O6 | Free-form `Args:`-adjacent claims | A docstring's `Returns:` / `Raises:` / `Note:` / `Example:` sections make claims (`returns shared-temp only`, `raises ValueError on missing key`). Verify each claim against the body. When a docstring enumerates the inputs a body counts (a "field counts as read when ..." list, a list of conditions treated as a match, a list of cases the body skips), list every union member and every suppressor the body applies (`read_names = a \| b \| c`, each early-return guard) and confirm each appears in the prose enumeration. A union member or suppressor the body applies but the prose omits is an O6 finding. When a docstring sentence excludes a named category of input from what the function flags (`X are not dispatch steps`, `Y is not a match`), confirm the axis the prose excludes on is the axis the body's branch condition keys on. A body that flags a call when it sits inside an `If.test` guard, paired with prose that excludes by the call's receiver shape (`method-on-local calls inside a branch are not dispatch steps`), is an O6 finding: a guarded method-on-local call is flagged even though the prose lists it as excluded — the exclusion is keyed to the wrong axis. A thin delegating method whose docstring names its actions and points at the home of the body lists the same actions the delegated function's own summary lists; when an edit moves one action out of the delegated body, the same edit rewords both summaries — judgment for this lane. A conditional bullet in the delegated prose also names every exception the body honors — that conditional-completeness slice stays a judgment call for this lane. A `Returns:` that names the mechanism, tool, or output format the function produces (`instructing a StructuredOutput summary`, `returns a YAML document`, `emits a JSON object`) matches the artifact the body builds. A dataclass or `TypedDict` field documented in the class `Attributes:` block states what the field means for one record; when the code sets that field the same way for every record (a run-mode flag such as `is_dry_run = not is_execute`), the description states the run-mode meaning, not a per-record outcome — judgment for this lane. A workflow gate-outcome status flag whose in-code prose (a schema property `description`, an architecture `detail`/overview string) describes the outcome as bypassing or skipping matches the branch that handles it: when the code routes that outcome to a blocker (`blocker = ...; break`) that holds the PR in draft, prose that reads "skips without blocking" or "the gate is bypassed" for that outcome is an O6 finding. The remaining deterministic O6 shape is gated at Write/Edit time — see **Write-time gate inventory** below — so the audit lane focuses on the free-form shapes the gate cannot match. |
42
42
  | O7 | Module-doc-vs-split-module after refactor | When a refactor moves a responsibility to a sibling module, the originating module's docstring and the receiving module's docstring both describe the home of that responsibility. A module docstring should describe only the responsibilities it owns. |
43
43
  | O8 | Companion-doc ordering/content vs producer | When a PR changes a producer function's ordering or union, read that skill's companion `SKILL.md` and sibling `.md` docs for any sentence naming the same produced artifact (a file path, a JSON key, a named list). A doc sentence that claims the artifact is `sorted` / `alphabetical` / `in sorted order`, or holds `just the at-risk names` / `only the current set`, while the producer merges stored names with new names and appends — preserving file order, not re-sorting the union — is an O8 finding on both counts (wrong order claim, hidden merged-in entries). The finding stands even when the PR diff never touched the `.md` file, because the behavior change orphaned the doc claim. A producer docstring asserting that no consumer reads its output yet (`producer-only artifact`, `no submission-run consumer reads it yet`) is the same companion-doc producer/consumer drift — judgment for this lane. The O6 pattern recurs at the companion-doc layer: when a PR routes a gate outcome to a blocker, read every skill's `SKILL.md` and reference `.md` docs that name that gate for a line still calling the outcome a bypass — an O8 finding. |
44
- | O9 | Python docstring plainness for a general developer | A changed module / class / public-function docstring's narrative prose — the summary and description before the first `Args:` / `Returns:` / `Raises:` / `Yields:` section — reads plainly and paints a concrete scene a general developer follows on first read. Flag a narrative that stacks abstract machinery nouns into a wall (`the SIGINT install/restore/installability check, the atexit terminal-record registration, and the interrupted-run finalizer`), that defines a thing by what it is not (`the non-promoter-specific machinery`), or that runs one sentence long while joining clauses with an em-dash or a semicolon. The diagram-first shape carries this best: a summary line, then a `::` example block or a doctest that shows a concrete input and its marked outcome, then a couple of short prose lines. The deterministic run-on mark is gated at Write/Edit time by `check_docstring_runon_sentence` in `code_rules_docstrings.py`, and a narrative that runs more than six prose lines with no such block is gated by `check_docstring_prose_wall_without_illustration` in the same module, so this lane carries the judgment the gates cannot: whether a stranger to the code pictures the moment, the input, and the outcome after one read, and whether the diagram truly illustrates — a real input, a marked outcome, an `ok:` / `flag:` contrast a reader learns from. See `../../rules/plain-illustrative-docstrings.md`. |
44
+ | O9 | Python docstring plainness for a general developer | A changed module / class / public-function docstring's narrative prose — the summary and description before the first `Args:` / `Returns:` / `Raises:` / `Yields:` section — reads plainly and paints a concrete scene a general developer follows on first read. Flag a narrative that stacks abstract machinery nouns into a wall (`the SIGINT install/restore/installability check, the atexit terminal-record registration, and the interrupted-run finalizer`), that defines a thing by what it is not (`the non-promoter-specific machinery`), or that runs one sentence long while joining clauses with an em-dash or a semicolon. The diagram-first shape carries this best: a summary line, then a `::` example block or a doctest that shows a concrete input and its marked outcome, then a couple of short prose lines. The deterministic run-on mark is gated at Write/Edit time by `check_docstring_runon_sentence` in `code_rules_docstrings.py`, and a narrative that runs more than six prose lines with no such block is gated by `check_docstring_prose_wall_without_illustration` in the same module, so this lane carries the judgment the gates cannot: whether a stranger to the code pictures the moment, the input, and the outcome after one read, and whether the diagram illustrates — an input, a marked outcome, an `ok:` / `flag:` contrast a reader learns from. See `../../rules/plain-illustrative-docstrings.md`. |
45
45
 
46
46
  ---
47
47
 
@@ -85,7 +85,7 @@ Read the body and the docstring side by side. Apply each check that matches the
85
85
  - **Suppressor / skip lists.** A body with several early returns that suppress the check names each suppressor in the prose.
86
86
  - **Shared fallback routes.** A summary that scopes a fallback call to one condition names every condition that reaches that call — judgment for this lane.
87
87
  - **Step order.** A docstring that says `A then B then C` matches the call order in the body. A step enumeration that names the body's linear steps also names every corrective step the body guards inside an `if`/`elif` branch — judgment for this lane.
88
- - **Delegation pointer summaries.** A thin delegating method whose docstring names its actions and points at the home of the real body lists the same actions the delegated function's own summary lists — judgment for this lane, including a conditional bullet in the delegated prose that names every exception the body honors.
88
+ - **Delegation pointer summaries.** A thin delegating method whose docstring names its actions and points at the home of the body lists the same actions the delegated function's own summary lists — judgment for this lane, including a conditional bullet in the delegated prose that names every exception the body honors.
89
89
  - **JS/`.mjs` resume-task, `@returns` object, sibling return keys, bare-flag directives.** See the JavaScript gate inventory above.
90
90
  - **Returns-clause cardinality.** A `Returns:` clause that names a dict-key prefix family with a plural noun matches the count of keys in that family in the returned dict literal — judgment for this lane.
91
91
  - **Length-constant superlative vs exact gate.** A module docstring that describes an integer `*_LENGTH` constant with a superlative or range word matches how the code consumes the constant — judgment for this lane.
@@ -95,7 +95,7 @@ Read the body and the docstring side by side. Apply each check that matches the
95
95
  - **Module summary scope versus data-schema constants.** A module whose one-line docstring scopes its contents to user-facing text names every category of constant the body holds. Gated form: `check_module_docstring_scope_omits_data_schema_constants`.
96
96
  - **Field meaning: run mode versus per record.** A dataclass or `TypedDict` field documented in the class `Attributes:` block states what the field means for one record. When the code sets that field the same way for every record, the description states the run-mode meaning — judgment for this lane.
97
97
  - **Predicate breadth.** A boolean helper whose prose promises a narrow check accepts only the inputs the prose names — no broader input class the name and prose do not mention.
98
- - **Exclusion-clause distinguisher.** A docstring sentence that says a named category of input "are not" / "is not" the thing the function flags keys the exclusion to the same axis the body's classification keys on. Read the body's actual branch condition, then state the exclusion on that same axis.
98
+ - **Exclusion-clause distinguisher.** A docstring sentence that says a named category of input "are not" / "is not" the thing the function flags keys the exclusion to the same axis the body's classification keys on. Read the body's branch condition, then state the exclusion on that same axis.
99
99
  - **Companion-doc ordering and content claims.** A `SKILL.md` (or sibling `.md`) sentence that names a produced artifact and claims its order or its content matches the producer function's docstring and body for that same artifact. The two move together in one commit, even when the producer edit does not touch the `.md` file.
100
100
  - **Gate-outcome status flags.** A workflow gate outcome the body routes to a blocker (`blocker = ...; break`) reads as blocked in every in-code prose string (a schema `description`, an architecture `detail`/overview string) and companion doc, never as a bypass — judgment for this lane.
101
101
  - **TYPE_CHECKING gate claim vs code.** A docstring that names a `TYPE_CHECKING` gate-detection step matches a module whose code handles TYPE_CHECKING — judgment for this lane.
@@ -2,7 +2,7 @@
2
2
 
3
3
  **What this category audits:** identifiers and reference data whose label asserts a contract the body does not deliver. The label may be too broad (`is_inside_function` flag set on def but never reset on scope exit), too narrow (`_is_docstring_section_header` matching only terminating headers), or shaped as one thing while behaving as another (`FILE_PATH_PATTERN` regex matching `client/server` because it lacks path-shape anchors; a hard-deny replacement word list containing ordinary technical English).
4
4
 
5
- The label-vs-body gap is its own failure mode independent of behavior-equivalence (L) because nothing was rewritten — the contract is broken at the moment the name is first chosen. The hook-enforced naming rules (J5 abbreviations, J6 vague nouns) ban specific identifiers but say nothing about precision-of-fit between what a name promises and what its body actually does.
5
+ The label-vs-body gap is its own failure mode independent of behavior-equivalence (L) because nothing was rewritten — the contract is broken at the moment the name is first chosen. The hook-enforced naming rules (J5 abbreviations, J6 vague nouns) ban specific identifiers but say nothing about precision-of-fit between what a name promises and what its body does.
6
6
 
7
7
  **Examples of Category P findings:**
8
8
  - A flag `is_inside_function` set on `def` and never reset on scope exit — name asserts state the body fails to keep.
@@ -25,7 +25,7 @@ For every regex, brace or token scanner, or word-list the diff adds or edits, bu
25
25
 
26
26
  Walk each input character by character (or token by token) and write down what the pattern returns. A pattern that returns the wrong answer on any of the three inputs is a Category P finding; cite the input that breaks it. The pattern is clean only after all three inputs return the answer the contract promises.
27
27
 
28
- `hooks/blocking/` is the priority surface. Its patterns gate every write, so a pattern that matches too much or too little there fires on real edits across the whole codebase.
28
+ `hooks/blocking/` is the priority surface. Its patterns gate every write, so a pattern that matches too much or too little there fires on edits across the whole codebase.
29
29
 
30
30
  Two canonical failures show why the hand trace is binding:
31
31
 
@@ -56,4 +56,4 @@ The reusable Variant C template for Category P is in [`../prompts/category-p-nam
56
56
 
57
57
  ## Why Category P matters as its own bucket
58
58
 
59
- Category L (behavior-equivalence) audits a rewrite against a prior implementation — it only fires when there is a `before` state to compare. Category P audits a fresh identifier whose label asserts a contract; the bug is that the body never delivered the named contract, even on the first commit. The hook-enforced J5 / J6 naming rules ban specific identifiers (`ctx`, `cfg`, `data`, `result`, `handle_*`) but say nothing about whether the identifier the author chose actually matches the body's reach. P is the bucket that catches a regex named `FILE_PATH_PATTERN` that accepts `TCP/IP`, a hard-deny word list that bans `function` and `address`, and a predicate named for the general case that only handles a subset — at audit time, before the gate ships and starts producing false positives in production.
59
+ Category L (behavior-equivalence) audits a rewrite against a prior implementation — it only fires when there is a `before` state to compare. Category P audits a fresh identifier whose label asserts a contract; the bug is that the body never delivered the named contract, even on the first commit. The hook-enforced J5 / J6 naming rules ban specific identifiers (`ctx`, `cfg`, `data`, `result`, `handle_*`) but say nothing about whether the identifier the author chose matches the body's reach. P is the bucket that catches a regex named `FILE_PATH_PATTERN` that accepts `TCP/IP`, a hard-deny word list that bans `function` and `address`, and a predicate named for the general case that only handles a subset — at audit time, before the gate ships and starts producing false positives in production.
@@ -30,7 +30,7 @@ Decomposition is by the **pair of surfaces** that must agree.
30
30
  | ID | Axis name | Concrete checks |
31
31
  |---|---|---|
32
32
  | Q1 | Term sweep | Pull out every identifier, API field, and config key the diff introduces or reads. Sweep all changed prose surfaces for near-miss variants — hyphen or space in place of an underscore, singular where the code is plural (or the reverse), a shared prefix with a divergent tail. Run `packages/claude-dev-env/_shared/pr-loop/scripts/terminology_sweep.py` for mechanical recall — it reads a unified diff on stdin or via `--diff-file`, prints one `file:line` finding per near-miss, exits 1 when findings exist and 0 when clean — then adjudicate each finding, since a near-miss can be an intentional distinct term. |
33
- | Q2 | PR-description claim verification | Fetch the PR body. For each falsifiable claim, verify it two ways against the diff and the repo. A dead-code claim ("removes the last caller of X") gets a reference search across the repo. A performance or ceiling claim ("caps at 30 seconds", "cuts calls in half") gets checked against the actual config values and call sites the diff sets. |
33
+ | Q2 | PR-description claim verification | Fetch the PR body. For each falsifiable claim, verify it two ways against the diff and the repo. A dead-code claim ("removes the last caller of X") gets a reference search across the repo. A performance or ceiling claim ("caps at 30 seconds", "cuts calls in half") gets checked against the config values and call sites the diff sets. |
34
34
  | Q3 | Message-vs-guard consistency | For every log, error, and evidence string the diff adds or edits, read the condition that gates it. The message text matches what the guard guarantees — a string that asserts an outcome fires only on a branch that reaches that outcome. Temporal wording matches the lifetime of the state variable it names: "this run" versus "this round", "is open" versus "was opened", "reached" versus "attempted". |
35
35
 
36
36
  Customize per-artifact: a change that adds no new identifier and no PR-body claim reduces Q to "verify every message the diff touches still matches its guard." A rename that spreads a term across many docs may need Q1 alone to list every prose surface.
@@ -20,7 +20,7 @@ ID prefix: `find`.
20
20
 
21
21
  **A2. Return-type annotation vs every code path**
22
22
  - For each annotated function, walk every code path: explicit `return X`, fall-through to implicit `None`, exception-handler exit, generator `yield` paths, async coroutine return value.
23
- - Verify each path's actual return value is assignable to the declared annotation; flag `-> bool` functions that can return `None`, `-> list[T]` functions that can return `None` on an early exit, etc.
23
+ - Verify each path's return value is assignable to the declared annotation; flag `-> bool` functions that can return `None`, `-> list[T]` functions that can return `None` on an early exit, etc.
24
24
  - For functions that raise instead of returning on some path, confirm the annotation does not promise a value the caller will dereference.
25
25
  - Inspect `try/except/finally` chains for paths that return from `finally` and override `try`/`except` returns.
26
26
  - For async functions, confirm the annotation refers to the awaited type, not the coroutine wrapper.
@@ -46,7 +46,7 @@ ID prefix: `find`.
46
46
  - Verify kwargs are valid for the targeted runtime version (`capture_output`, `text`, `encoding`, `check`, `timeout`); flag combinations that conflict (`stdout=PIPE` + `capture_output=True`).
47
47
  - Exception contract under `check=True` (raises `CalledProcessError` on non-zero exit) — verify callers either propagate or handle, with no silent swallow that masks failure.
48
48
  - Verify quoting/escaping of arguments crossing the subprocess boundary, especially when interpolating untrusted strings.
49
- - Verify the resolved executable path is real on the target platform, not assumed (`which` / `Get-Command` failure paths).
49
+ - Verify the resolved executable path exists on the target platform, not assumed (`which` / `Get-Command` failure paths).
50
50
 
51
51
  **A6. Shell/host-language cmdlet or function parameter sets and binding**
52
52
  - For every shell or host-language function/cmdlet declaration with parameter sets (PowerShell `param(...)` with `ParameterSetName=`, Bash `getopts`, etc.), verify the declaration matches every invocation pattern. Confirm a default parameter set is declared if no-arg invocation is reachable.
@@ -11,8 +11,8 @@ ID prefix: `find`.
11
11
 
12
12
  **B1. CSS / DOM selector vs target browser engine**
13
13
  - Every CSS selector in the diff — verify pseudo-class support (`:has()`, `:is()`, `:where()`, `:focus-visible`, `:focus-within`) against every browser engine in the declared support matrix; flag any selector that requires an engine version newer than the declared minimum.
14
- - Every attribute selector, `::part()`, `::slotted()`, and shadow-DOM piercing pattern — verify the target engine actually exposes the matching DOM (e.g. shadow boundaries, scoped styles).
15
- - Every selector fed to `document.querySelector` / `querySelectorAll` / jQuery `$()` / Selenium `By.css_selector` / Playwright `page.locator` — verify the **runtime** selector engine matches the **target browser** engine; a Node-side jsdom selector parse can succeed where the real browser fails (or vice versa).
14
+ - Every attribute selector, `::part()`, `::slotted()`, and shadow-DOM piercing pattern — verify the target engine exposes the matching DOM (e.g. shadow boundaries, scoped styles).
15
+ - Every selector fed to `document.querySelector` / `querySelectorAll` / jQuery `$()` / Selenium `By.css_selector` / Playwright `page.locator` — verify the **runtime** selector engine matches the **target browser** engine; a Node-side jsdom selector parse can succeed where the browser fails (or vice versa).
16
16
  - Every CSS feature query (`@supports`), media query level, and container query — verify availability across the declared engine matrix and that fallback paths exist for engines that do not parse the syntax.
17
17
  - Every test-runner DOM assertion (snapshot, `outerHTML`/`innerHTML` equality, computed-style read) — verify the runner uses the same engine as the production target, or document the divergence.
18
18
  - Cross-engine quirks: WebKit-only `-webkit-` prefixes, Gecko-only `-moz-`, IE/Edge legacy filters; flag any production reliance on a single-engine prefix without a standard fallback.
@@ -35,7 +35,7 @@ ID prefix: `find`.
35
35
 
36
36
  **B4. Shell / CLI / cmdlet syntax vs runtime version**
37
37
  - Every PowerShell cmdlet — verify availability across the declared edition matrix (Windows PowerShell 5.1 ↔ PowerShell 7+ ↔ PowerShell on Linux/macOS) and version-specific parameter sets.
38
- - Every shebang (`#!/usr/bin/env pwsh`, `#!/bin/bash`, `#!/usr/bin/env python3`) vs the actual interpreter resolved at runtime — flag mismatches between declared and invoked interpreter (e.g. shebang says `pwsh` but Python `subprocess.run(["powershell", …])` resolves to PS 5.1 on Windows).
38
+ - Every shebang (`#!/usr/bin/env pwsh`, `#!/bin/bash`, `#!/usr/bin/env python3`) vs the interpreter resolved at runtime — flag mismatches between declared and invoked interpreter (e.g. shebang says `pwsh` but Python `subprocess.run(["powershell", …])` resolves to PS 5.1 on Windows).
39
39
  - Every parameter set in a `param(...)` block — verify `[CmdletBinding(DefaultParameterSetName=…)]` is set when ambiguity is possible; missing default = `Parameter set cannot be resolved` at runtime.
40
40
  - Every cmdlet flag combination — verify both flags belong to the same parameter set per Microsoft docs (e.g. `-RepetitionInterval` is an `-Once` parameter, NOT `-Daily`).
41
41
  - Every bash-ism — `[[ ... ]]` (bash 3+ only, not POSIX `sh`), arrays, process substitution `<(…)`, `${var,,}` lower-case expansion (bash 4+), associative arrays — verify against the declared minimum shell.
@@ -21,7 +21,7 @@ Inline the artifact under review here (full diff, full file bodies, or a faithfu
21
21
  - For each site, identify the corresponding release: `with`/context manager, `try`/`finally` `close()`, RAII destructor, `using` block, deferred close, explicit `.close()` reachable on every code path including early `return`, raised exception, and `break`/`continue`.
22
22
  - Single-call helpers that open-write-close internally (e.g. `pathlib.Path.write_text`, `pathlib.Path.read_bytes`, `fs.writeFileSync`) do not return a handle to user code and are not a leak site.
23
23
  - Distinguish file objects from file descriptors: a wrapped FD (`os.fdopen`) is owned by the file object and closed when the object closes; a raw FD from `os.open` belongs to C8.
24
- - Shape B proof-of-absence requires ≥3 adversarial probes specific to this sub-bucket. Examples: (a) does any code path open a file via stdlib (`open`, `io.open`, `os.fdopen`, `pathlib.Path.open`, language-equivalent)? (b) do any error/exception callbacks (e.g. an `onerror=` handler) ever receive an object carrying an open FD that the callback must close? (c) do any helper APIs that look like single-call helpers actually return a handle the caller is expected to close?
24
+ - Shape B proof-of-absence requires ≥3 adversarial probes specific to this sub-bucket. Examples: (a) does any code path open a file via stdlib (`open`, `io.open`, `os.fdopen`, `pathlib.Path.open`, language-equivalent)? (b) do any error/exception callbacks (e.g. an `onerror=` handler) ever receive an object carrying an open FD that the callback must close? (c) do any helper APIs that look like single-call helpers return a handle the caller is expected to close?
25
25
 
26
26
  **C2. Subprocess / child processes**
27
27
  - Enumerate every site that spawns a child process: `subprocess.run`, `subprocess.Popen`, `os.system`, `os.spawn*`, `multiprocessing.Process`, `child_process.spawn`, `child_process.exec`, `Process.Start`, `os/exec.Command`, shell-out wrappers.
@@ -44,8 +44,8 @@ Inline the artifact under review here (full diff, full file bodies, or a faithfu
44
44
  - For each site, verify a release path on every exit branch: explicit `close()`, `with` block, connection-pool return, `dispose`/`destroy`, `defer client.Close()`.
45
45
  - Pooled clients have their own lifecycle: the pool itself must be closed on application shutdown, and individual borrows must be returned even on exception.
46
46
  - Side-channel connections introduced by libraries (telemetry, metrics, tracing) count too if they are configured by code in the diff.
47
- - Local-only invocations of cmdlets, CLIs, or RPC endpoints should be distinguished from genuine network calls.
48
- - Shape B proof-of-absence requires ≥3 adversarial probes. Examples: (a) does any imported stdlib or third-party module implicitly open a network socket on import, on first use, or at module construction time? (b) do any local cmdlet / CLI invocations actually reach a remote service (e.g. cloud-provider auth lookup, package-registry resolution, AppX execution-alias) under common configurations? (c) do any name-resolution or service-discovery calls (DNS, mDNS, AppX alias, `Get-Command`) hit network paths under common Windows / macOS / Linux configurations?
47
+ - Local-only invocations of cmdlets, CLIs, or RPC endpoints should be distinguished from network calls.
48
+ - Shape B proof-of-absence requires ≥3 adversarial probes. Examples: (a) does any imported stdlib or third-party module implicitly open a network socket on import, on first use, or at module construction time? (b) do any local cmdlet / CLI invocations reach a remote service (e.g. cloud-provider auth lookup, package-registry resolution, AppX execution-alias) under common configurations? (c) do any name-resolution or service-discovery calls (DNS, mDNS, AppX alias, `Get-Command`) hit network paths under common Windows / macOS / Linux configurations?
49
49
 
50
50
  **C5. Locks, semaphores, mutexes**
51
51
  - Enumerate every synchronization primitive constructed: `threading.Lock`, `threading.RLock`, `threading.Semaphore`, `threading.BoundedSemaphore`, `threading.Event`, `threading.Condition`, `multiprocessing.Lock`/`Semaphore`/`Manager` locks, `asyncio.Lock`/`Semaphore`/`Event`/`Condition`, advisory file locks (`fcntl.flock`, `msvcrt.locking`), DB advisory locks, distributed locks.
@@ -65,7 +65,7 @@ Inline the artifact under audit using one `###` header per section. Pick the chu
65
65
 
66
66
  **D7. Async/sync ordering of side effects**
67
67
  - Scan the entire artifact for `async def`, `await`, `asyncio.gather` / `asyncio.create_task` / `asyncio.run`, JavaScript `async` / `await` / `Promise.all`, or any other deferred-execution primitive. If any are present, walk the ordering of side effects.
68
- - For each `await` site, identify whether a side effect that should have happened *before* the suspension point is actually flushed before yielding control. Probe what an interleaved coroutine could observe.
68
+ - For each `await` site, identify whether a side effect that should have happened *before* the suspension point is flushed before yielding control. Probe what an interleaved coroutine could observe.
69
69
  - For purely synchronous artifacts, cite proof-of-absence with explicit keyword counts ("0 occurrences of `async`, `await`, `asyncio` across all [N] sections").
70
70
 
71
71
  **D8. Class-attribute vs instance-attribute confusion**
@@ -49,7 +49,7 @@ Inline the artifact under this section using the section types defined in the ch
49
49
  - Tuple-unpack discards (`for path, _, _ in os.walk(...)`) are out of scope — E5 specifically scopes "function parameters never read"; state this exclusion explicitly.
50
50
  - `*args` / `**kwargs` / TypeScript rest spreads: confirm at least one consumer (forwarded to another call, iterated, indexed) or mark the parameter unused.
51
51
  - Cross-language parameter declarations (PowerShell `param(...)`, shell positional `$1..$N`, Bash `getopts`): confirm each named parameter has at least one body reference.
52
- - 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
+ - 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 uses?
53
53
 
54
54
  **E6. Removed-but-not-deleted symbol references**
55
55
  - 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.
@@ -88,7 +88,7 @@ Q3: Which symbol most likely will *become* dead code after a near-future refacto
88
88
 
89
89
  Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket E1-E9, 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 9 sub-buckets — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
90
90
 
91
- Note: most Category E findings are P2 (style / cleanup) unless the dead code masks an actual bug; the adversarial-pass quota uses P2 here.
91
+ Note: most Category E findings are P2 (style / cleanup) unless the dead code masks a bug; the adversarial-pass quota uses P2 here.
92
92
 
93
93
  ---
94
94
 
@@ -184,7 +184,7 @@ Q3: Which symbol most likely will *become* dead code after a near-future refacto
184
184
 
185
185
  Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket E1-E9, 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 9 sub-buckets — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
186
186
 
187
- Note: most Category E findings are P2 (style / cleanup) unless the dead code masks an actual bug; the adversarial-pass quota uses P2 here.
187
+ Note: most Category E findings are P2 (style / cleanup) unless the dead code masks a bug; the adversarial-pass quota uses P2 here.
188
188
 
189
189
  ## Diff (4 new files, all lines in scope)
190
190
 
@@ -25,7 +25,7 @@ Repeat for every section in scope.
25
25
  **F1. Catch-all except clauses**
26
26
  - Locate every bare `except:`, `except Exception:`, `except BaseException:` (and language-equivalents: PowerShell `try { } catch { }` with no type filter, JS `catch (e) {}`, Go `_ = err`, Rust `let _ = result`).
27
27
  - For each, classify the body: `pass` / `continue` / log-only / re-raise. Only re-raise (or a documented benign-case-with-trace) is compliant.
28
- - For the swallowing variants, enumerate which exception subclasses the protected call actually raises and identify which are real failures vs benign known cases (e.g., for `os.rmdir`: `FileNotFoundError`, `PermissionError`, `NotADirectoryError`, `OSError(ENOTEMPTY)`, `OSError(EBUSY)` — only `ENOTEMPTY` is benign).
28
+ - For the swallowing variants, enumerate which exception subclasses the protected call raises and identify which are failures vs benign known cases (e.g., for `os.rmdir`: `FileNotFoundError`, `PermissionError`, `NotADirectoryError`, `OSError(ENOTEMPTY)`, `OSError(EBUSY)` — only `ENOTEMPTY` is benign).
29
29
  - Check for asymmetry: do two error handlers in the same module disagree on whether to log? A handler that logs to stderr next to one that silently swallows is a Category F smell even when each is locally defensible.
30
30
  - Check for double-swallow: does an outer callback (e.g. `os.walk(..., onerror=...)`) already report errors that an inner `except` then swallows again?
31
31
 
@@ -37,7 +37,7 @@ Repeat for every section in scope.
37
37
 
38
38
  **F3. Default fallback values masking failure**
39
39
  - `dict.get(key, default)` where the absence of the key is itself a bug.
40
- - Stale-payload-key shape: for each external-input dict (`*_payload`, event, request, parsed-JSON body), list every string-literal key read from it. Check each key against the contract the module's docstring and its other reads set up — a payload whose other reads bind to same-named variables and whose docstring names a key set is an established contract. Flag a lone read of a key outside that contract: it resolves to the `.get` default on every real payload and silently records an empty value into the field it feeds. A key consumed inline with a meaningful default (`resolve(payload.get("cwd", "."))`) is legitimate; the flag is the dropped key whose value the consuming field needs but never receives.
40
+ - Stale-payload-key shape: for each external-input dict (`*_payload`, event, request, parsed-JSON body), list every string-literal key read from it. Check each key against the contract the module's docstring and its other reads set up — a payload whose other reads bind to same-named variables and whose docstring names a key set is an established contract. Flag a lone read of a key outside that contract: it resolves to the `.get` default on every payload and silently records an empty value into the field it feeds. A key consumed inline with a meaningful default (`resolve(payload.get("cwd", "."))`) is legitimate; the flag is the dropped key whose value the consuming field needs but never receives.
41
41
  - `or default` short-circuits hiding `None` returns from fallible calls.
42
42
  - `getattr(obj, attr, default)` masking `AttributeError` from the wrong object type.
43
43
  - argparse `default=...` for values that should fail-loud when absent.
@@ -65,7 +65,7 @@ Repeat for every section in scope.
65
65
  - `os.write`, `os.read`, `socket.send`, `socket.recv` whose short-write/short-read return values are discarded.
66
66
  - File-handle methods (`f.write`) whose returned byte count is not checked when partial writes are possible (Windows pipes, network sockets).
67
67
  - PowerShell pipelines piped to `| Out-Null` that may be hiding a non-terminating warning.
68
- - Functions that return a status object alongside a side effect — verify the side effect's success is the actual contract and the discarded return is informational only.
68
+ - Functions that return a status object alongside a side effect — verify the side effect's success is the contract and the discarded return is informational only.
69
69
  - Exception-only contracts (e.g. `os.rmdir` returns `None`, raises on failure) belong under F1, not F6 — note the cross-reference rather than double-booking.
70
70
 
71
71
  **F7. PowerShell error-suppression patterns**
@@ -74,12 +74,12 @@ Repeat for every section in scope.
74
74
  - `2>$null`, `*>$null`, `$ErrorActionPreference = 'SilentlyContinue'` mutations.
75
75
  - `$?` not consulted after a native-binary call where exit code matters.
76
76
  - The opposite hazard: a fallback path that drops `-ErrorAction SilentlyContinue` from a `Get-Command` (or similar) call where the `$null` was the entire contract — produces a terminating error on the path that was supposed to be silent-then-handled-by-the-next-line.
77
- - Semantic-success-but-functional-failure: `Get-Command` finds the Microsoft Store stub `python.exe` whose `.Source` resolves but whose execution opens the Store. The cmdlet did not error, but the resolved interpreter cannot run code. Verify whether scripts validate that resolved paths actually do their job.
78
- - Mismatched user-facing messages: a `-Remove` / `-Uninstall` flow that prints "X removed." regardless of whether anything was actually unregistered.
77
+ - Semantic-success-but-functional-failure: `Get-Command` finds the Microsoft Store stub `python.exe` whose `.Source` resolves but whose execution opens the Store. The cmdlet did not error, but the resolved interpreter cannot run code. Verify whether scripts validate that resolved paths do their job.
78
+ - Mismatched user-facing messages: a `-Remove` / `-Uninstall` flow that prints "X removed." regardless of whether anything was unregistered.
79
79
 
80
80
  **F8. Test-level swallowing**
81
81
  - `try/except` / `try/catch` inside a test body that catches and logs instead of asserting. Tests must fail loudly when their target raises.
82
- - `pytest.warns` used where `pytest.raises` was the actual contract.
82
+ - `pytest.warns` used where `pytest.raises` was the contract.
83
83
  - Tests asserting only on the success path with no negative-space coverage of the swallowed-error branch.
84
84
  - Tests whose name encodes an assertion ("does not crash") but whose body asserts nothing — the test passes incidentally and would also pass if the function silently failed every operation.
85
85
  - Coverage gaps for known F1 / F2 / F6 swallowing branches: if the audited code has `except OSError: pass`, is there a test that exercises that branch and verifies the post-conditions (e.g. directory still present, no log line emitted)?
@@ -88,12 +88,12 @@ Repeat for every section in scope.
88
88
  **F9. Gate-validator self-defeat via parse-failure swallow**
89
89
  - Locate every `ast.parse` / `tokenize` / `json.loads` / `yaml.safe_load` call inside gate / validator / hook code that parses the audited input as code or structured data.
90
90
  - For each, classify the catch branch: does it return `[]` / `None` / `True` (a clean signal) on parse failure? A clean-signal default masks the gate being broken — the check fires zero findings whenever the parse fails.
91
- - Trace what the dispatcher actually feeds the check: when the dispatcher passes a partial fragment (e.g., the Edit tool's `new_string` rather than the full file content), the parse fails on every Edit and the check is dead in production while its tests pass against full-file fixtures.
92
- - Adversarial probes: (a) feed the check a syntactically incomplete fragment and confirm whether the catch branch returns a clean signal; (b) compare the content surface the test fixtures use against the content surface the dispatcher supplies at runtime; (c) verify the catch branch distinguishes "input is genuinely clean" from "input could not be parsed" — a single clean-signal return for both is an F9 finding.
91
+ - Trace what the dispatcher feeds the check: when the dispatcher passes a partial fragment (e.g., the Edit tool's `new_string` rather than the full file content), the parse fails on every Edit and the check is dead in production while its tests pass against full-file fixtures.
92
+ - Adversarial probes: (a) feed the check a syntactically incomplete fragment and confirm whether the catch branch returns a clean signal; (b) compare the content surface the test fixtures use against the content surface the dispatcher supplies at runtime; (c) verify the catch branch distinguishes "input is clean" from "input could not be parsed" — a single clean-signal return for both is an F9 finding.
93
93
 
94
94
  **F10. Guard helper returns success-default on unverifiable input**
95
95
  - Locate every guard helper / predicate / classifier that decides whether to allow or block, and inspect the path taken when it cannot validate the input shape (missing positional args, wrong arity, unexpected `None`, malformed payload).
96
- - Flag any guard that short-circuits to `True` / `Ok` / the success sentinel on unverifiable input. Guards must default-deny when they cannot verify; default-allow on unverifiable input lets real violations through downstream.
96
+ - Flag any guard that short-circuits to `True` / `Ok` / the success sentinel on unverifiable input. Guards must default-deny when they cannot verify; default-allow on unverifiable input lets violations through downstream.
97
97
  - Adversarial probes: (a) construct an input that fails the guard's shape check and confirm whether the guard returns allow or deny; (b) supply `None` / missing args / wrong arity and trace the return; (c) verify the guard's "cannot verify" branch is distinct from its "verified clean" branch — collapsing both into a success return is an F10 finding.
98
98
 
99
99
  ## Cross-bucket questions to answer at the end
@@ -367,7 +367,7 @@ Write-Host "$TaskName registered — runs every ${IntervalMinutes}min against '$
367
367
 
368
368
  **F1. Catch-all except clauses**
369
369
  - `sweep_empty_dirs.py` line 28-29: `except OSError: continue` absorbs `os.path.getctime` failures inside the `os.walk` bottom-up loop. Verify this is the documented "file vanished mid-walk" case and not masking a permission denial that leaves stale empty dirs forever.
370
- - `sweep_empty_dirs.py` line 35-36: `except OSError: pass` absorbs `os.rmdir` failures. The intent comment in the rubric says "silently skips non-empty dirs". Enumerate the OSError subclasses `os.rmdir` actually raises: `FileNotFoundError`, `PermissionError`, `NotADirectoryError`, `OSError(ENOTEMPTY)`, `OSError(EBUSY)`. The non-empty case (ENOTEMPTY) is the *only* benign one — every other subclass is a real failure being swallowed without logging.
370
+ - `sweep_empty_dirs.py` line 35-36: `except OSError: pass` absorbs `os.rmdir` failures. The intent comment in the rubric says "silently skips non-empty dirs". Enumerate the OSError subclasses `os.rmdir` raises: `FileNotFoundError`, `PermissionError`, `NotADirectoryError`, `OSError(ENOTEMPTY)`, `OSError(EBUSY)`. The non-empty case (ENOTEMPTY) is the *only* benign one — every other subclass is a failure being swallowed without logging.
371
371
  - Critical asymmetry: `_log_walk_error` (line 14-15) DOES log walk-level errors to stderr, but the inner `except OSError: pass` at line 35-36 logs nothing on the rmdir path. The PR ships two error handlers that disagree on whether errors deserve a stderr trace.
372
372
  - The walk-level handler `_log_walk_error` is wired through `os.walk(root, onerror=_log_walk_error, topdown=False)` at lines 22-24. Verify whether the `except OSError: continue` at line 28-29 ever fires for an error `os.walk`'s `onerror` already reported, producing a double-swallow with the second handler being silent.
373
373
  - `main()` at lines 65-83 has no top-level `try/except` around `sweep(...)`, so unhandled errors inside the loop body (other than KeyboardInterrupt at line 81) would crash the watcher. Verify whether any silent-failure within `sweep()` could leave the watcher running but no longer making progress (e.g., sweep returns empty list every cycle because every rmdir fails).
@@ -410,9 +410,9 @@ Write-Host "$TaskName registered — runs every ${IntervalMinutes}min against '$
410
410
  - `Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue` at line 22 of installer — null is captured into `$task`, then explicitly checked by `if (-not $task)` at line 23. The null absorption is handled. Compliant.
411
411
  - `Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue` at line 42 of installer — the `-ErrorAction SilentlyContinue` makes the absence-of-task case non-terminating. Verify intent: should `-Remove` against a never-installed task be a success (current) or a clearly-flagged no-op? The `Write-Host "$TaskName removed."` at line 43 will print regardless, mis-stating the outcome.
412
412
  - ⚠️ `Get-Command py -ErrorAction SilentlyContinue` at line 65 immediately followed by `.Source` access at line 66: `$_py = Get-Command py -ErrorAction SilentlyContinue` returns `$null` when `py` is absent; the line 66 expression `$_py.Source` would throw on `$null`, BUT the `if ($_py)` guard short-circuits to the `else` branch first. The `py`-side path is safe.
413
- - ⚠️⚠️ The fallback at line 66 is `(Get-Command python).Source` — *missing* `-ErrorAction SilentlyContinue`. When neither `py` nor `python` is on PATH, `Get-Command python` raises a terminating `CommandNotFoundException`, which propagates past the `if (-not $PythonPath)` guard at line 67 entirely. The intended diagnostic message at line 68 (`"Cannot find Python (py or python) on PATH."`) is never reached. This is the OPPOSITE F7 hazard: the script is *loud* on a failure path that was *intended to be silent-then-handled-by-line-67*. The asymmetry between line 65 (suppressed) and line 66's else-branch (not suppressed) is a real bug.
413
+ - ⚠️⚠️ The fallback at line 66 is `(Get-Command python).Source` — *missing* `-ErrorAction SilentlyContinue`. When neither `py` nor `python` is on PATH, `Get-Command python` raises a terminating `CommandNotFoundException`, which propagates past the `if (-not $PythonPath)` guard at line 67 entirely. The intended diagnostic message at line 68 (`"Cannot find Python (py or python) on PATH."`) is never reached. This is the OPPOSITE F7 hazard: the script is *loud* on a failure path that was *intended to be silent-then-handled-by-line-67*. The asymmetry between line 65 (suppressed) and line 66's else-branch (not suppressed) is a bug.
414
414
  - No `2>$null`, `*>$null`, or `$ErrorActionPreference` mutation in this script. No `$?` consultation either, but each cmdlet either has explicit error handling or uses default error action.
415
- - Adversarial probe — does `Get-Command python` find the Microsoft Store stub on a fresh Windows 11 install? The stub's `.Source` resolves to `%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe`, which when invoked opens the Store rather than running Python. The installer would register a scheduled task whose `-Execute` path opens the Microsoft Store every IntervalMinutes — a silent semantic failure even though `Get-Command` did not error. Verify whether the installer needs to validate that the resolved interpreter actually executes Python.
415
+ - Adversarial probe — does `Get-Command python` find the Microsoft Store stub on a fresh Windows 11 install? The stub's `.Source` resolves to `%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe`, which when invoked opens the Store rather than running Python. The installer would register a scheduled task whose `-Execute` path opens the Microsoft Store every IntervalMinutes — a silent semantic failure even though `Get-Command` did not error. Verify whether the installer needs to validate that the resolved interpreter executes Python.
416
416
 
417
417
  **F8. Test-level swallowing**
418
418
  - `test_sweep_empty_dirs.py` has no try/except inside any test body. All tests use direct asserts (lines 38-39, 47-49, 64-67, 76-78). No `pytest.warns`, no `pytest.raises` either — failure paths are not exercised at all.
@@ -89,7 +89,7 @@ ID prefix: `find`.
89
89
  - Python side: `DEFAULT_AGE_SECONDS: int = 120` and `DEFAULT_POLL_INTERVAL: int = 30` at lines 3-4 of `sweep_config.py`. Python `int` is arbitrary precision — no 32-bit ceiling. `argparse` `type=int` (lines 44, 48 of `sweep_empty_dirs.py`) likewise produces a Python int. `time.sleep(arguments.interval)` at line 69 accepts any non-negative number; no overflow risk on the value itself.
90
90
  - PowerShell side: `[int]$IntervalMinutes = 5` (line 7) and `[int]$AgeSeconds = 120` (line 10) of `Install-SweepEmptyDirs.ps1`. `[int]` in PowerShell is `System.Int32` — range `-2147483648` to `2147483647`. Both defaults are 4-5 orders of magnitude below the ceiling. The user-overridable values would have to exceed 2^31-1 (≈ 68 years in seconds, ≈ 4084 years in minutes) to overflow.
91
91
  - `New-TimeSpan -Minutes $IntervalMinutes` at line 71 of `Install-SweepEmptyDirs.ps1`: `TimeSpan` is internally `Int64` ticks (100ns each). 5 minutes × 60 × 10⁷ = 3×10⁹ ticks — well within `Int64` range. Even 4084-year overflow on `[int]$IntervalMinutes` would be caught at the `[int]` cast, not at the `TimeSpan` construction.
92
- - Cross-language defaults: PowerShell `[int]$AgeSeconds = 120` (line 10) is passed through argv as the bare token `120` to Python's `argparse type=int`. Python parses it back to an arbitrary-precision int. No precision drift at this magnitude. **But:** the two defaults (PS line 10 and Python sweep_config.py line 3) are independently hardcoded with no shared source of truth — drift in a future edit is the real G4 hazard, not arithmetic overflow today.
92
+ - Cross-language defaults: PowerShell `[int]$AgeSeconds = 120` (line 10) is passed through argv as the bare token `120` to Python's `argparse type=int`. Python parses it back to an arbitrary-precision int. No precision drift at this magnitude. **But:** the two defaults (PS line 10 and Python sweep_config.py line 3) are independently hardcoded with no shared source of truth — drift in a future edit is the G4 hazard, not arithmetic overflow today.
93
93
  - Shape A candidate (P2): `[int]$AgeSeconds = 120` and `DEFAULT_AGE_SECONDS: int = 120` are duplicated literals across two files with no validation that they match. A future edit to one without the other produces a silent default drift. Cite `Install-SweepEmptyDirs.ps1:10` and `sweep_config.py:3`.
94
94
  - Adversarial probes: (a) what happens if the user invokes `Install-SweepEmptyDirs.ps1 -AgeSeconds 2147483648`? PowerShell's `[int]` cast would throw at parameter binding — verify whether the script handles this or crashes uninformatively. (b) `New-TimeSpan -Minutes` accepts `[int32]` per Microsoft docs — is `$IntervalMinutes = 0` accepted, and does `-RepetitionInterval (TimeSpan.Zero)` register a degenerate task? (c) `time.sleep(arguments.interval)` at line 69 with `arguments.interval = 0` — does the busy loop spin without yielding to the OS?
95
95
 
@@ -98,7 +98,7 @@ ID prefix: `find`.
98
98
  - `created = os.path.getctime(each_directory_path)` at line 27 returns `float`.
99
99
  - `if now - created >= min_age_seconds:` at line 30 — float minus float compared against `int` (`min_age_seconds: int`). Python promotes the int to float for comparison. The comparison is `>=`, not `==`, so IEEE-754 epsilon noise does not produce stale-result equality bugs of the `0.1 + 0.2 != 0.3` kind.
100
100
  - The float subtraction `now - created` carries ~15-16 significant decimal digits. For Unix timestamps in 2026 (~1.78×10⁹), epsilon-magnitude noise is on the order of 10⁻⁷ seconds — irrelevant against a 120-second threshold.
101
- - Shape B probes: (a) for very small `min_age_seconds` (e.g., `min_age_seconds=0` as in `test_skips_nonempty_dir` at line 73), the comparison `now - created >= 0` is dominated by `getctime` filesystem-resolution rounding (FAT32 = 2-second granularity, NTFS = 100ns) — does this matter for the test's intent? (b) does `time.time()` ever return a value equal to `os.path.getctime` for a directory just created, producing `0.0 >= 0` = True and immediate deletion? (c) is the float subtraction monotonic under wall-clock adjustment (NTP slew, manual clock change) — could `now < created` produce a negative result that compares False against a positive `min_age_seconds`?
101
+ - Shape B probes: (a) for very small `min_age_seconds` (e.g., `min_age_seconds=0` as in `test_skips_nonempty_dir` at line 73), the comparison `now - created >= 0` is dominated by `getctime` filesystem-resolution rounding (FAT32 = 2-second granularity, NTFS = 100ns) — does this matter for the test's intent? (b) does `time.time()` ever return a value equal to `os.path.getctime` for a directory just created, producing `0.0 >= 0` = `True` and immediate deletion? (c) is the float subtraction monotonic under wall-clock adjustment (NTP slew, manual clock change) — could `now < created` produce a negative result that compares False against a positive `min_age_seconds`?
102
102
 
103
103
  **G6. Date / time arithmetic** ⭐ canonical surface
104
104
  - `time.time()` at line 20 of `sweep_empty_dirs.py` returns a UTC-anchored Unix timestamp (seconds since 1970-01-01T00:00:00Z) per Python docs. There is no DST math, no timezone arithmetic, no leap-second handling in the file.
@@ -30,7 +30,7 @@ ID prefix: `find`.
30
30
  **H3. Path traversal**
31
31
  - Surface check: any filesystem operation (`open`, `os.walk`, `os.rmdir`, `Path(…).read_text`, `shutil.copy`, `Get-Content`, `Test-Path`, `Remove-Item`) whose path is built from attacker-controlled input?
32
32
  - Shape A pattern: user input joined to a base path without `realpath`/`normpath` and without a containment check verifying the resolved path stays under the intended root; symlink-following enabled where it shouldn't be; UNC / device-namespace paths (`\\?\`, `\\.\`) accepted without filtering; trailing-dot or trailing-space Windows pathname tricks.
33
- - Shape B probes (when no traversal surface exists): (1) `os.walk` / equivalent does not follow symlinks (`followlinks=False` in Python; `-NoFollowSymlink` semantics in PowerShell). (2) UNC / drive-letter / reparse-point handling — document whether the artifact honors them as given (operator authority) or rejects them. (3) Path normalization — does the code call `realpath`/`normpath` before filesystem ops? (4) TOCTOU between any pre-flight check (`isdir`, `Test-Path`) and the actual filesystem op. (5) Pre-flight gate identification — what is the only validation between attacker input and the filesystem syscall?
33
+ - Shape B probes (when no traversal surface exists): (1) `os.walk` / equivalent does not follow symlinks (`followlinks=False` in Python; `-NoFollowSymlink` semantics in PowerShell). (2) UNC / drive-letter / reparse-point handling — document whether the artifact honors them as given (operator authority) or rejects them. (3) Path normalization — does the code call `realpath`/`normpath` before filesystem ops? (4) TOCTOU between any pre-flight check (`isdir`, `Test-Path`) and the filesystem op. (5) Pre-flight gate identification — what is the only validation between attacker input and the filesystem syscall?
34
34
 
35
35
  **H4. Authentication bypass**
36
36
  - Surface check: any HTTP / RPC / IPC entry point that should require authentication?
@@ -54,7 +54,7 @@ ID prefix: `find`.
54
54
 
55
55
  **H8. CSRF / state-changing without token**
56
56
  - Surface check: any state-changing HTTP handler (POST, PUT, DELETE, PATCH) reachable by an authenticated browser session?
57
- - Shape A pattern: state-changing handler with no CSRF token validation; SameSite-cookie assumption used as the sole CSRF defense without verifying the framework actually sets it; pre-flight CORS check trusted as authentication; same-origin assumed without enforcement.
57
+ - Shape A pattern: state-changing handler with no CSRF token validation; SameSite-cookie assumption used as the sole CSRF defense without verifying the framework sets it; pre-flight CORS check trusted as authentication; same-origin assumed without enforcement.
58
58
  - Shape B probes (when no CSRF surface exists): (1) confirm no `@app.route`-style POST handler, no `@router.post`, no `flask.Flask`, no `fastapi.FastAPI`, no `aiohttp.web.RouteTableDef`, no Express `app.post`. (2) Confirm any local trigger surface (named pipe, Unix socket, COM endpoint, scheduled task) is local-only and not reachable by a remote unauthenticated caller. (3) Confirm no inter-process listener exists that an unprivileged caller could poke to trigger the state change.
59
59
 
60
60
  **H9. Deserialization**
@@ -71,7 +71,7 @@ ID prefix: `find`.
71
71
 
72
72
  Q1: Are there any inputs that cross two H sub-buckets? (e.g., a path that flows through H3-style filesystem handling AND becomes an interpolated argument in an H2 shell command — are the two trust assumptions consistent across both sites?)
73
73
  Q2: What's the worst injection / leakage hazard introduced by this artifact? Cite `<file>:<line>` for the specific construction.
74
- Q3: Which input is most fragile to a future API addition — i.e., where would a future change most likely turn an operator-trust assumption into an actual attacker-reachable surface? Name the line(s) most likely to break.
74
+ Q3: Which input is most fragile to a future API addition — i.e., where would a future change most likely turn an operator-trust assumption into an attacker-reachable surface? Name the line(s) most likely to break.
75
75
 
76
76
  ## Output
77
77
 
@@ -167,7 +167,7 @@ This sub-bucket has TWO distinct command-string-build sites in the diff, both wi
167
167
 
168
168
  Q1: Are there any inputs that cross two H sub-buckets? (For PR #394, the candidate is `arguments.root` flowing through H3-style path-handling code that ALSO becomes the `$Target` interpolated into the H2 PowerShell argument string when an operator re-runs the installer with the same path. Are the two trust assumptions consistent?)
169
169
  Q2: What's the worst injection / leakage hazard introduced by this PR? Cite `<file>:<line>` for the specific construction. (Candidate: `Install-SweepEmptyDirs.ps1:69` `New-ScheduledTaskAction -Argument` interpolating `$Target` — embedded-`$` and trailing-backslash hazards are both reachable in normal Windows path-naming.)
170
- Q3: Which input is most fragile to a future API addition — i.e., where would a future change most likely turn an operator-trust assumption into an actual attacker-reachable surface? Name the line(s) most likely to break. (Candidate: `test_sweep_empty_dirs.py:25` `_set_creation_time_windows` — if a future test parametrizes `path` from a non-tempfile source, the f-string-into-single-quoted-PowerShell pattern flips from "fragile-but-bounded" to "exploitable.")
170
+ Q3: Which input is most fragile to a future API addition — i.e., where would a future change most likely turn an operator-trust assumption into an attacker-reachable surface? Name the line(s) most likely to break. (Candidate: `test_sweep_empty_dirs.py:25` `_set_creation_time_windows` — if a future test parametrizes `path` from a non-tempfile source, the f-string-into-single-quoted-PowerShell pattern flips from "fragile-but-bounded" to "exploitable.")
171
171
 
172
172
  ## Output
173
173
 
@@ -1,6 +1,6 @@
1
1
  Audit [REPO/ARTIFACT] [TARGET_ID] for **Category I only** (concurrency hazards). Skip A–H, J–P. 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.
2
2
 
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.]
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 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
 
5
5
  ID prefix: `find`.
6
6
 
@@ -46,23 +46,23 @@ Inline the artifact under audit below this section. Chunking guidance — pick t
46
46
  - Compound filesystem ops where no single syscall expresses the intent: "delete if older than X" (must `stat` then `unlink`), "rename if doesn't exist" (must `stat` then `rename` — POSIX `rename` is atomic but overwrites, Windows `rename` fails on overwrite).
47
47
  - DB compound ops outside a transaction: read-then-update without `SELECT FOR UPDATE`, `UPDATE ... WHERE version = ?` (optimistic locking missing).
48
48
  - CPython GIL gives some atomicity to single bytecode ops (list.append, dict.__setitem__) but not to `+=` or `dict.setdefault` callbacks.
49
- - Adversarial probes: (a) does any single statement compile to multiple bytecodes (e.g., `obj.attr += 1`) that the GIL does not protect? (b) is there a JSON config rewritten by `json.dump(open(path, 'w'))` — non-atomic; concurrent reader sees a truncated file mid-write? (c) does a "transactional" wrapper actually start a transaction, or just open a connection?
49
+ - Adversarial probes: (a) does any single statement compile to multiple bytecodes (e.g., `obj.attr += 1`) that the GIL does not protect? (b) is there a JSON config rewritten by `json.dump(open(path, 'w'))` — non-atomic; concurrent reader sees a truncated file mid-write? (c) does a "transactional" wrapper start a transaction, or just open a connection?
50
50
 
51
51
  **I6. Thread-local / async-local context bleed**
52
52
  - `threading.local()` instances surviving thread-pool reuse (the same OS thread services many tasks; the `local` is keyed to the OS thread, not the logical task).
53
53
  - `contextvars.ContextVar` set without `Context.run(...)` — propagation across `asyncio.create_task` is automatic but copying-on-create; mutations after task creation do not propagate.
54
- - Request-scoped state stored on a module global (Flask `g`, Django thread-local request) leaking when the framework's scoping does not match the actual concurrency model.
54
+ - Request-scoped state stored on a module global (Flask `g`, Django thread-local request) leaking when the framework's scoping does not match the concurrency model.
55
55
  - ORM session-per-request that is reused across requests due to a misconfigured scope.
56
56
  - `asyncio` task-local state inside an executor (`run_in_executor` runs in a thread, not a coroutine — `contextvars` may or may not propagate depending on Python version and library version).
57
57
  - Adversarial probes: (a) does any helper called from both sync and async paths assume the same context-storage primitive? (b) is there a pool-warmup that pre-populates `threading.local` and assumes it stays populated forever? (c) does logging context (correlation ID) propagate across `loop.run_in_executor`?
58
58
 
59
59
  **I7. Cancellation handling**
60
60
  - Every `await` inside an `async def` is a cancellation point. Cleanup code that follows `await` may be skipped on `CancelledError`.
61
- - `asyncio.shield(...)` to protect critical cleanup; verify that what is shielded is genuinely critical and not just convenient.
61
+ - `asyncio.shield(...)` to protect critical cleanup; verify that what is shielded is critical and not just convenient.
62
62
  - `try/except Exception:` swallowing `CancelledError` — `CancelledError` inherits from `BaseException` in 3.8+ but the codebase may run on older Python.
63
63
  - Synchronous cancellation: `KeyboardInterrupt` landing between two syscalls; `SIGTERM` arriving mid-cleanup.
64
64
  - `asyncio.timeout()` (3.11+) vs `asyncio.wait_for(...)` semantics — both raise `CancelledError`, both can race with the wrapped task completing.
65
- - Adversarial probes: (a) does any `finally` block contain an `await` that could itself be cancelled, leaving cleanup half-done? (b) does the code rely on `__aexit__` running to release a resource, and does the cancellation path actually invoke `__aexit__`? (c) is there a `task.cancel()` call without `await task` afterward to surface the cancellation result?
65
+ - Adversarial probes: (a) does any `finally` block contain an `await` that could itself be cancelled, leaving cleanup half-done? (b) does the code rely on `__aexit__` running to release a resource, and does the cancellation path invoke `__aexit__`? (c) is there a `task.cancel()` call without `await task` afterward to surface the cancellation result?
66
66
 
67
67
  **I8. Signal handling in multi-threaded code**
68
68
  - Python: signals are always delivered to the main thread. A custom signal handler installed by `signal.signal(...)` on a non-main thread silently no-ops.
@@ -131,7 +131,7 @@ This is the highest-signal sub-bucket. There are TWO concrete TOCTOU windows in
131
131
  - Delete the directory entirely → `getctime` raises `FileNotFoundError` (subclass of `OSError`), absorbed by the except at sweep_empty_dirs.py:28-29 (`continue`). Race handled.
132
132
  - Repopulate the directory with files → `os.rmdir` raises `OSError` (`ENOTEMPTY`), absorbed by sweep_empty_dirs.py:35-36 (`pass`). Race handled — but `removed.append(...)` at sweep_empty_dirs.py:34 was NOT yet appended (it's inside the same try). Verify the append is correctly gated by successful `rmdir`.
133
133
  - Replace the directory with a symlink → `os.rmdir` on a symlink targeting a populated dir behaves OS-dependently; on Windows `os.rmdir` removes a directory symlink without touching the target, on POSIX it raises `ENOTDIR`. Verify the protective except is broad enough.
134
- - Update creation time on the directory between `getctime` (sweep_empty_dirs.py:27) and the `if` check (sweep_empty_dirs.py:30) → no real window here because the local `created` is already captured; the `now - created` comparison uses the captured value. Verify by re-reading the loop body.
134
+ - Update creation time on the directory between `getctime` (sweep_empty_dirs.py:27) and the `if` check (sweep_empty_dirs.py:30) → no window here because the local `created` is already captured; the `now - created` comparison uses the captured value. Verify by re-reading the loop body.
135
135
  - `topdown=False` means children are visited before parents. Verify that another process creating a file inside a child directory between the child's `rmdir` and the parent's `rmdir` does not cause the parent's `rmdir` to silently spare a now-non-empty parent — and that this is the *intended* behavior (skip parents whose children re-populated).
136
136
 
137
137
  *Window 2 — PowerShell Test-Path vs Register-ScheduledTask.*
@@ -36,7 +36,7 @@ A `test_*.py` name or a `.mjs` extension takes the line out of the write-time ga
36
36
  - Walk every numeric or non-trivial literal other than `0`, `1`, `-1` inside production function bodies.
37
37
  - Test files follow the same no-new-comment policy.
38
38
  - Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed rather than added or justified. Module-level declarations are J3-scope, not J1-scope.
39
- - For each literal found, decide: structural value that belongs in `config/` (flag) vs. truly local arithmetic constant tied to the line's logic (defendable).
39
+ - For each literal found, decide: structural value that belongs in `config/` (flag) vs. local arithmetic constant tied to the line's logic (defendable).
40
40
  - Adversarial probes must each verify a distinct angle: (a) does the literal duplicate an existing value already centralized in `config/`? (b) does the literal silently couple two languages (e.g., a Python config value and a hand-typed PowerShell / shell mirror)? (c) does the literal appear in user-facing help/doc text in a way that would silently lie if the canonical value changed?
41
41
 
42
42
  **J2. String-template magic**
@@ -53,12 +53,12 @@ A `test_*.py` name or a `.mjs` extension takes the line out of the write-time ga
53
53
  **J5. Abbreviations**
54
54
  - Walk every parameter, local, and attribute name across production, test, and JavaScript changed lines. Flag: `ctx`, `cfg`, `msg`, `btn`, `idx`, `cnt`, `elem`, `val`, `tmp`, `str`, `num`, `arr`, `obj`, `fn`, `cb`, `req`, `res`. Loop counters `i`/`j`/`k` and `e` for exceptions are exempt.
55
55
  - This audit walks changed test-file and `.mjs` / `.js` lines for this rule, even though the write-time hook skips them.
56
- - Adversarial probes: (a) is there a borderline name (e.g., `removed`, `arguments`) that someone might mis-classify as an abbreviation but is actually a full English word? Confirm. (b) does any callback / parameter / attribute use a short variant of a domain term that is technically a full word but conventionally abbreviates a longer one? (c) does any variable in a comprehension or lambda use a single letter outside the `i`/`j`/`k`/`e` exemption?
56
+ - Adversarial probes: (a) is there a borderline name (e.g., `removed`, `arguments`) that someone might mis-classify as an abbreviation but is a full English word? Confirm. (b) does any callback / parameter / attribute use a short variant of a domain term that is technically a full word but conventionally abbreviates a longer one? (c) does any variable in a comprehension or lambda use a single letter outside the `i`/`j`/`k`/`e` exemption?
57
57
 
58
58
  **J6. Vague names**
59
59
  - Flag any name from the vague list: `result`, `data`, `output`, `response`, `value`, `item`, `temp`, `info`, `stuff`, `thing`. Vague verb prefixes for function names: `handle`, `process`, `manage`, `do`.
60
60
  - This audit walks changed test-file and `.mjs` / `.js` lines for this rule, even though the write-time hook skips them.
61
- - Adversarial probes: (a) does any local variable use a domain-adjacent name that is actually on the vague list (e.g., `result` from a parser, `data` from a fetch)? (b) does any newly-introduced function name start with a vague prefix? (c) does any public attribute / dict key use a vague label that the call site has to disambiguate by surrounding context?
61
+ - Adversarial probes: (a) does any local variable use a domain-adjacent name that is on the vague list (e.g., `result` from a parser, `data` from a fetch)? (b) does any newly-introduced function name start with a vague prefix? (c) does any public attribute / dict key use a vague label that the call site has to disambiguate by surrounding context?
62
62
 
63
63
  **J7. Type hints**
64
64
  - Walk every function across production, test, and JavaScript changed lines. Verify parameter and return types are present, no `Any`, no `# type: ignore`.
@@ -71,7 +71,7 @@ A `test_*.py` name or a `.mjs` extension takes the line out of the write-time ga
71
71
  - Comments tied to untouched code remain unchanged; a changed comment is removed with the code it describes.
72
72
  - Test files follow the same no-new-comment policy.
73
73
  - Changed directive, TODO, FIXME, HACK, XXX, and type-ignore comments are removed rather than added or justified.
74
- - Adversarial probes: (a) is there any `# type:` or marker comment that is actually inert prose rather than a real type-checker / linter directive? (b) is any docstring carrying inline-comment content (line-level explanations rather than module/function description)? (c) does any newly-added blank line between code stanzas function as a comment substitute, suggesting the author wanted to add a comment but couldn't?
74
+ - Adversarial probes: (a) is there any `# type:` or marker comment that is inert prose rather than a type-checker / linter directive? (b) is any docstring carrying inline-comment content (line-level explanations rather than module/function description)? (c) does any newly-added blank line between code stanzas function as a comment substitute, suggesting the author wanted to add a comment but couldn't?
75
75
 
76
76
  **J9. Logging format**
77
77
  - Walk every `log_*(...)` call. Must be `log_*("template with {}", arg)`, not `log_*(f"...")`.
@@ -179,7 +179,7 @@ ID prefix: `find`.
179
179
  - `config/sweep_config.py` — line 140 is a module-level docstring. No inline comments. Clean.
180
180
  - Test file — the same no-new-comment rule applies.
181
181
  - `Install-SweepEmptyDirs.ps1` — line 227 (`#!/usr/bin/env pwsh`) is a shebang, exempt. No inline `#` comments added in the PowerShell file. Clean.
182
- - Adversarial probes: (a) is there any `# type:` comment that is actually inert noise rather than a type-checker directive? No occurrences in the diff. (b) is any docstring carrying inline-comment content as line-level explanations rather than module/function description? Module docstring on line 62 is one line ("Delete empty directories older than 2 minutes under a given root."); function docstrings on 78 and 102 are one-liners. Clean. (c) does any newly-added blank line between code stanzas function as a comment substitute? Visual whitespace is allowed.
182
+ - Adversarial probes: (a) is there any `# type:` comment that is inert noise rather than a type-checker directive? No occurrences in the diff. (b) is any docstring carrying inline-comment content as line-level explanations rather than module/function description? Module docstring on line 62 is one line ("Delete empty directories older than 2 minutes under a given root."); function docstrings on 78 and 102 are one-liners. Clean. (c) does any newly-added blank line between code stanzas function as a comment substitute? Visual whitespace is allowed.
183
183
 
184
184
  **J9. Logging format**
185
185
  - Walk every `log_*(...)` call. Must be `log_*("template with {}", arg)`, not `log_*(f"...")`.
@@ -34,7 +34,7 @@ ID prefix: `find`.
34
34
  **M3. Consumer-expects-set anti-pattern**
35
35
  - For every consumer that calls `set(producer())`, `dict.fromkeys(producer())`, `dict((k, v) for k, v in producer())`, `INSERT ... ON CONFLICT`, or `pandas.DataFrame.set_index`, walk back to the producer: should the producer have returned the set/dict directly?
36
36
  - The anti-pattern is a sign that the producer's `list[X]` return type lied about cardinality — the consumer is paying for the deduplication that the producer should have done.
37
- - Adversarial probes: (a) does any test mock the producer with a list containing duplicates — does the consumer's set-conversion silently drop them? (b) does the consumer's set / dict size differ from the producer's list length in production logs? (c) does the consumer raise `RuntimeError: duplicate key` on real-world inputs?
37
+ - Adversarial probes: (a) does any test mock the producer with a list containing duplicates — does the consumer's set-conversion silently drop them? (b) does the consumer's set / dict size differ from the producer's list length in production logs? (c) does the consumer raise `RuntimeError: duplicate key` on production inputs?
38
38
 
39
39
  **M4. `extend(...)` into list consumers (acceptable)**
40
40
  - For every consumer whose only operation is `accumulator.extend(producer())` into a list, verify the accumulator's downstream consumers tolerate duplicates.