claude-dev-env 8.1.0 → 8.2.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 (78) hide show
  1. package/.agents/agents/clean-coder.md +4 -4
  2. package/.agents/agents/code-quality-agent.md +4 -4
  3. package/.agents/agents/plan-packet-validator.md +2 -2
  4. package/.agents/agents/session-advisor.md +1 -1
  5. package/.agents/skills/cert-classification-rule/SKILL.md +3 -3
  6. package/.agents/skills/e-code-review/SKILL.md +2 -2
  7. package/.agents/skills/e-code-review/reference/fix.md +1 -1
  8. package/.agents/skills/e-code-review/reference/loop.md +1 -1
  9. package/.agents/skills/e-code-review/reference/medium.md +4 -4
  10. package/.agents/skills/e-code-review/reference/xhigh.md +5 -5
  11. package/.agents/skills/everything-search/SKILL.md +3 -3
  12. package/.agents/skills/issue-tracker/SKILL.md +1 -1
  13. package/.agents/skills/orchestrator/SKILL.md +3 -3
  14. package/.agents/skills/orchestrator/reference/consult-the-orchestrator.md +1 -1
  15. package/.agents/skills/orchestrator/reference/executor-consult-block.md +1 -1
  16. package/.agents/skills/orchestrator-refresh/SKILL.md +2 -2
  17. package/.agents/skills/plugin-eval-standalone-skill/SKILL.md +96 -0
  18. package/.agents/skills/plugin-eval-standalone-skill/reference/graders-and-commands.md +108 -0
  19. package/.agents/skills/privacy-hygiene/SKILL.md +5 -6
  20. package/.agents/skills/privacy-hygiene/reference/sweep-procedure.md +5 -6
  21. package/.agents/skills/repairing-hook-boundaries/SKILL.md +1 -1
  22. package/.agents/skills/syncing-submodules/SKILL.md +1 -1
  23. package/.agents/skills/team-advisor/SKILL.md +9 -7
  24. package/.agents/skills/team-advisor/reference/advisor-docs-review.md +28 -1
  25. package/.agents/skills/usage-pause/SKILL.md +3 -3
  26. package/_shared/advisor/advisor-protocol.md +7 -3
  27. package/_shared/advisor/reference/advisor-block.md +8 -1
  28. package/audit-rubrics/category_rubrics/category-f-silent-failures.md +2 -2
  29. package/audit-rubrics/category_rubrics/category-i-concurrency.md +1 -1
  30. package/audit-rubrics/category_rubrics/category-m-producer-consumer-cardinality.md +1 -1
  31. package/audit-rubrics/category_rubrics/category-n-test-name-scenario-verifier.md +5 -5
  32. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +5 -5
  33. package/audit-rubrics/category_rubrics/category-p-name-vs-behavior-contract.md +3 -3
  34. package/audit-rubrics/category_rubrics/category-q-cross-surface-claims.md +1 -1
  35. package/audit-rubrics/prompts/category-a-api-contracts.md +2 -2
  36. package/audit-rubrics/prompts/category-b-selector-engine-compat.md +3 -3
  37. package/audit-rubrics/prompts/category-c-resource-cleanup.md +3 -3
  38. package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +1 -1
  39. package/audit-rubrics/prompts/category-e-dead-code.md +3 -3
  40. package/audit-rubrics/prompts/category-f-silent-failures.md +12 -12
  41. package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
  42. package/audit-rubrics/prompts/category-h-security-boundaries.md +4 -4
  43. package/audit-rubrics/prompts/category-i-concurrency.md +6 -6
  44. package/audit-rubrics/prompts/category-j-code-rules-compliance.md +5 -5
  45. package/audit-rubrics/prompts/category-m-producer-consumer-cardinality.md +1 -1
  46. package/audit-rubrics/prompts/category-n-test-name-scenario-verifier.md +7 -7
  47. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +2 -2
  48. package/audit-rubrics/prompts/category-p-name-vs-behavior-contract.md +3 -3
  49. package/audit-rubrics/prompts/category-q-cross-surface-claims.md +2 -2
  50. package/bin/ever-shipped-skills.mjs +1 -0
  51. package/bin/ever-shipped-skills.test.mjs +4 -0
  52. package/docs/BDD_SCENARIO_QUALITY.md +2 -2
  53. package/docs/CODE_RULES.md +7 -7
  54. package/docs/TEST_QUALITY.md +1 -1
  55. package/docs/nas-ssh-invocation.md +1 -1
  56. package/docs/references/advisor-tool.md +2 -2
  57. package/docs/references/prose-style-enforcement.md +1 -1
  58. package/docs/references/team-advisor-skill.md +2 -2
  59. package/docs/references/weak-executor-advisor.md +1 -1
  60. package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
  61. package/package.json +1 -1
  62. package/rules/archiving-agent-config.md +5 -8
  63. package/rules/long-horizon-autonomy.md +1 -1
  64. package/scripts/active_capability_references.py +74 -19
  65. package/scripts/banned_prose_words.py +159 -0
  66. package/scripts/dev_env_scripts_constants/active_capability_constants.py +12 -10
  67. package/scripts/dev_env_scripts_constants/banned_prose_word_constants.py +95 -0
  68. package/scripts/policy_lint/adapter_retired_hook_prose.py +17 -7
  69. package/scripts/policy_lint/adapters.py +6 -6
  70. package/scripts/policy_lint/config/constants.py +8 -0
  71. package/scripts/policy_lint/registry.py +1 -1
  72. package/scripts/sync_to_cursor/rules.py +1 -1
  73. package/scripts/test_active_capability_references.py +100 -33
  74. package/scripts/tests/test_banned_prose_words.py +103 -0
  75. package/scripts/tests/test_engine.py +1 -1
  76. package/scripts/tests/test_policy_lint_rules_retired_hook_prose.py +46 -0
  77. package/scripts/tests/test_rules.py +12 -1
  78. package/system-prompts/software-engineer.xml +7 -7
@@ -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.
@@ -11,7 +11,7 @@ Audit [REPO/ARTIFACT] [TARGET_ID] for **Category N only** (test-name scenario ve
11
11
 
12
12
  ID prefix: `find`.
13
13
 
14
- [ONE-PARAGRAPH FRAME: enumerate every test whose name includes a scenario claim (`_when_*`, `_at_*`, `_under_*`, `_with_*`, `_on_*`, `_after_*`, `_during_*`). State the audit goal: for each scenario-named test, verify the body sets up the named condition via fixture / monkeypatch / environment gate so the production code's scenario-named branch actually runs during the act phase.]
14
+ [ONE-PARAGRAPH FRAME: enumerate every test whose name includes a scenario claim (`_when_*`, `_at_*`, `_under_*`, `_with_*`, `_on_*`, `_after_*`, `_during_*`). State the audit goal: for each scenario-named test, verify the body sets up the named condition via fixture / monkeypatch / environment gate so the production code's scenario-named branch runs during the act phase.]
15
15
 
16
16
  ## Source material ([N] files/sections, all lines in scope)
17
17
 
@@ -37,12 +37,12 @@ ID prefix: `find`.
37
37
 
38
38
  **N5. Assertion shape mismatch**
39
39
  - For every assertion, verify the assertion's shape can fail by construction. `assert <substring> not in result` where the substring is misspelled relative to the production output, or `assert result == ""` when the production function returns `None` on the negative case, or `len(result) > 0` when the production function returns an empty list on the no-feature path.
40
- - Adversarial probes: (a) inspect each assertion's shape against the production function's actual return-value space; (b) check for assertions where the substring shape never appears in the production output by construction; (c) check for `assert x is True` where the production function returns truthy non-bool values.
40
+ - Adversarial probes: (a) inspect each assertion's shape against the production function's return-value space; (b) check for assertions where the substring shape never appears in the production output by construction; (c) check for `assert x is True` where the production function returns truthy non-bool values.
41
41
 
42
42
  **N6. Cross-platform scenario gating**
43
43
  - For every test named `_on_windows` / `_on_linux` / `_on_macos`, verify the body gates on `sys.platform`, `monkeypatch.setattr(os, "name", ...)`, or `@pytest.mark.skipif`.
44
44
  - Bare scenario names that run unchanged across platforms claim more than they prove.
45
- - Adversarial probes: (a) does the production function's platform-specific branch get skipped on the CI runner's actual platform; (b) does the test pass against the platform fallback rather than the platform-specific code; (c) is the platform fixture installed and respected by the test runner.
45
+ - Adversarial probes: (a) does the production function's platform-specific branch get skipped on the CI runner's platform; (b) does the test pass against the platform fallback rather than the platform-specific code; (c) is the platform fixture installed and respected by the test runner.
46
46
 
47
47
  **N7. Time / clock scenario gating**
48
48
  - For every test named `_after_<duration>` / `_at_midnight` / `_during_business_hours`, verify the body injects a frozen clock (`freezegun.freeze_time`, `monkeypatch.setattr(time, "time", ...)`, `unittest.mock.patch("datetime.now")`).
@@ -62,8 +62,8 @@ ID prefix: `find`.
62
62
  - For every test, verify the fixture / path / import wiring resolves to the artifact the test name claims.
63
63
  - Path arithmetic: walk every `Path(__file__).parents[k]` chain symbolically and confirm it reaches the directory the assertion expects — a `parents[3]` that stops at `skills/` while the test expects the package root cannot fail for the right reason.
64
64
  - Same-symbol dual imports: `from module import helper` plus `from module import helper as helper_alias` bind two names to the same function object, so any parity assertion between the two bound names is true by construction and proves nothing.
65
- - Fixture file lookups: confirm every `open(Path(__file__).parent / "fixture.txt")` (or equivalent) reaches a file that actually exists in the repo.
66
- - Adversarial probes: (a) re-derive each `parents[k]` index against the real directory depth and flag any off-by-k; (b) check whether two imports in the test resolve to the same object before trusting a cross-name comparison; (c) confirm each referenced fixture path exists on disk at the depth the arithmetic produces.
65
+ - Fixture file lookups: confirm every `open(Path(__file__).parent / "fixture.txt")` (or equivalent) reaches a file that exists in the repo.
66
+ - Adversarial probes: (a) re-derive each `parents[k]` index against the directory depth and flag any off-by-k; (b) check whether two imports in the test resolve to the same object before trusting a cross-name comparison; (c) confirm each referenced fixture path exists on disk at the depth the arithmetic produces.
67
67
 
68
68
  ## Cross-bucket questions to answer at the end
69
69
 
@@ -87,7 +87,7 @@ PR: refactor(hooks): cross-platform path resolution for windows-rmtree-blocker
87
87
  Head SHA: (the commit that landed the platform-conditional logic)
88
88
  ID prefix: `find`.
89
89
 
90
- The PR adds platform-conditional path-resolution logic to `windows_rmtree_blocker.py` and ships 5 new tests named `test_*_on_windows` and `test_*_on_linux` across `test_windows_rmtree_blocker.py`. The audit goal: verify each scenario-named test sets up the named platform via monkeypatch or skipif gate so the production function's platform-specific branch actually runs during the act phase.
90
+ The PR adds platform-conditional path-resolution logic to `windows_rmtree_blocker.py` and ships 5 new tests named `test_*_on_windows` and `test_*_on_linux` across `test_windows_rmtree_blocker.py`. The audit goal: verify each scenario-named test sets up the named platform via monkeypatch or skipif gate so the production function's platform-specific branch runs during the act phase.
91
91
 
92
92
  ## Sub-buckets (each requires Shape A finding OR Shape B with ≥3 adversarial probes)
93
93
 
@@ -128,7 +128,7 @@ The PR adds platform-conditional path-resolution logic to `windows_rmtree_blocke
128
128
 
129
129
  ## Cross-bucket questions to answer at the end
130
130
 
131
- Q1: Five scenario-named tests (F5, F21, F23, F26, F27) do not gate on `sys.platform` and pass against the Linux-fallback branch on the CI runner. The Windows-specific code path has zero actual coverage despite the test names claiming it. Cite `test_windows_rmtree_blocker.py:42` (F5 first test) and `windows_rmtree_blocker.py:67` (the `if sys.platform == "win32":` branch) as the misclaim pair.
131
+ Q1: Five scenario-named tests (F5, F21, F23, F26, F27) do not gate on `sys.platform` and pass against the Linux-fallback branch on the CI runner. The Windows-specific code path has zero coverage despite the test names claiming it. Cite `test_windows_rmtree_blocker.py:42` (F5 first test) and `windows_rmtree_blocker.py:67` (the `if sys.platform == "win32":` branch) as the misclaim pair.
132
132
 
133
133
  Q2: Worst false-coverage signal: F5 — the test's name `test_resolves_path_on_windows` reads as Windows-branch coverage in the PR review, but the act phase exercises the Linux fallback. A reviewer reading the test name during PR review would assume Windows coverage exists; it does not.
134
134
 
@@ -57,7 +57,7 @@ ID prefix: `find`.
57
57
 
58
58
  **O6. Free-form `Args:`-adjacent claims**
59
59
  - Judgment: thick rubric O6 (unions, suppressors, exclusion axis, delegation summaries, Returns/Raises/Note claims, run-mode field meaning). Gate inventory and free-form checklist live in the rubric.
60
- - Adversarial probes: (a) check `Returns:` claims against every `return` statement in the body — is the documented return shape the actual return shape; (b) check `Raises:` claims against every `raise` and propagating callee — is every documented raise reachable; (c) check `Example:` snippets — does the snippet actually compile against the signature.
60
+ - Adversarial probes: (a) check `Returns:` claims against every `return` statement in the body — is the documented return shape the shape the body returns; (b) check `Raises:` claims against every `raise` and propagating callee — is every documented raise reachable; (c) check `Example:` snippets — does the snippet compile against the signature.
61
61
 
62
62
  **O7. Module-doc-vs-split-module after refactor**
63
63
  - Judgment: thick rubric O7.
@@ -89,7 +89,7 @@ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket O1-O9, produce Shape A
89
89
 
90
90
  Audit jl-cmd/claude-dev-env PR #522 for **Category O only** (docstring / fixture-prose vs implementation drift). Skip A-N, P. Sub-bucket forced-exhaustion mode: Category O is decomposed into 9 sub-buckets below.
91
91
 
92
- PR #522 split `pr_description_command_parser.py` into two modules — the original parser and a new `pr_description_pr_number.py` — but the originating module's docstring still claims the PR-number recovery responsibility. A sibling change to `pr_description_body_audit.py` introduced a module docstring whose verb (`detects vague language`) overstates the module's actual responsibility (it only exposes `_extract_vague_scan_text()`; detection runs elsewhere).
92
+ PR #522 split `pr_description_command_parser.py` into two modules — the original parser and a new `pr_description_pr_number.py` — but the originating module's docstring still claims the PR-number recovery responsibility. A sibling change to `pr_description_body_audit.py` introduced a module docstring whose verb (`detects vague language`) overstates the module's responsibility (it only exposes `_extract_vague_scan_text()`; detection runs elsewhere).
93
93
 
94
94
  Expected findings on PR #522:
95
95
  - **O1 finding:** `pr_description_body_audit.py:8` docstring uses verb `detects`, but the only exported symbol prepares input for a regex scan that fires in a different module. Body line(s) showing `_extract_vague_scan_text` returning normalized text without a detection call.
@@ -37,11 +37,11 @@ Two canonical failures to probe for:
37
37
 
38
38
  **P1. Boolean / flag names assert state the body keeps**
39
39
  - For every new `is_*` / `has_*` / `was_*` / `should_*` flag, trace the body. Every set site must be paired with a reset site that fires when the named condition becomes false.
40
- - Adversarial probes: (a) grep every assignment to the flag — count set-true vs set-false; (b) for AST-driven flags, walk the visit method and confirm scope-exit semantics (def visited → flag set; dedent / function-exit → flag reset); (c) construct an input where the flag should be false after a prior true region — does the code agree.
40
+ - Adversarial probes: (a) grep every assignment to the flag — count set-true vs set-false; (b) for AST-driven flags, walk the visit method and confirm scope-exit semantics (def visited → flag set; dedent / function-exit → flag reset); (c) construct an input where the flag should be false after a region that set it true — does the code agree.
41
41
 
42
42
  **P2. Predicate-name breadth matches body coverage**
43
43
  - For every new `_is_*` / `_has_*` / `_can_*` predicate function, list the body's `return True` branches. The named predicate must hold on the union of those branches and nothing else.
44
- - Adversarial probes: (a) construct an input that satisfies the name but returns False — that is a P2 narrowness finding; (b) construct an input that does not satisfy the name but returns True — that is a P2 breadth finding; (c) check neighbor predicates — is the body's actual contract the responsibility of a different helper.
44
+ - Adversarial probes: (a) construct an input that satisfies the name but returns False — that is a P2 narrowness finding; (b) construct an input that does not satisfy the name but returns True — that is a P2 breadth finding; (c) check neighbor predicates — is the body's contract the responsibility of a different helper.
45
45
 
46
46
  **P3. Regex name vs regex shape** ⭐ canonical P case
47
47
  - For every new `*_PATTERN` / `*_REGEX` constant, read the regex source. Confirm the anchors (`^`, `$`, `\b`, lookarounds) match the name's promised shape. `FILE_PATH_PATTERN` implies path shape; the regex must include path-shape anchors (slash count, segment shape, length bounds) enough to reject non-path inputs.
@@ -73,7 +73,7 @@ Q3: Which identifier most clearly shows the body should be renamed (the name is
73
73
 
74
74
  ## Output
75
75
 
76
- Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket P1-P7, produce Shape A or Shape B (with ≥3 probes). Each Shape A finding must cite (a) the identifier file:line, (b) one concrete input that shows the name-vs-body gap, and (c) the recommended fix direction (rename vs body-tighten). Cross-bucket Q1-Q3 answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 identifiers whose names overstate what the body actually does — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
76
+ Lead: `Total: N (P0=N, P1=N, P2=N)`. For each sub-bucket P1-P7, produce Shape A or Shape B (with ≥3 probes). Each Shape A finding must cite (a) the identifier file:line, (b) one concrete input that shows the name-vs-body gap, and (c) the recommended fix direction (rename vs body-tighten). Cross-bucket Q1-Q3 answers after the per-sub-bucket walk. Adversarial second pass: "assume your first pass missed at least 3 identifiers whose names overstate what the body does — find them." Open Questions section for ambiguities. Read-only. No edits, no commits.
77
77
 
78
78
  ---
79
79
 
@@ -30,13 +30,13 @@ ID prefix: `find`.
30
30
 
31
31
  **Q2. PR-description claim verification**
32
32
  - Read the PR body. List each falsifiable claim — a dead-code removal, a performance or ceiling number, a "last caller" assertion, a "no remaining X" cleanup claim.
33
- - Verify each claim two ways against the diff and the repo. A dead-code claim gets a reference search across the repo for the named symbol. A ceiling or performance claim gets checked against the actual config value the diff sets and the call sites it touches. Cite the PR-body line and the diff or repo line that confirms or contradicts it.
33
+ - Verify each claim two ways against the diff and the repo. A dead-code claim gets a reference search across the repo for the named symbol. A ceiling or performance claim gets checked against the config value the diff sets and the call sites it touches. Cite the PR-body line and the diff or repo line that confirms or contradicts it.
34
34
  - Adversarial probes when the body looks correct: (a) search the repo for a surviving caller of any symbol the body calls dead; (b) compare each stated number against the config constant the diff sets; (c) check whether a "this finishes the cleanup" claim leaves a matching pattern in an unchanged sibling file.
35
35
 
36
36
  **Q3. Message-vs-guard consistency**
37
37
  - For every log, error, and evidence string the diff adds or edits, read the branch condition that gates it. The message text matches what that guard guarantees: a string asserting an outcome fires only on a branch that reaches the outcome.
38
38
  - Check temporal wording against the lifetime of the state variable the message names: "this run" versus "this round", "is open" versus "was opened", "reached" versus "attempted". Cite the message line and the guard or state-variable line.
39
- - Adversarial probes when messages look aligned: (a) trace each success message back to the return value or flag it claims and confirm the branch cannot reach it with the opposite value; (b) check every "missing" / "not found" error against the exception class the branch actually catches; (c) check every counter-based message against where the counter resets.
39
+ - Adversarial probes when messages look aligned: (a) trace each success message back to the return value or flag it claims and confirm the branch cannot reach it with the opposite value; (b) check every "missing" / "not found" error against the exception class the branch catches; (c) check every counter-based message against where the counter resets.
40
40
 
41
41
  ## Cross-bucket questions to answer at the end
42
42
 
@@ -59,6 +59,7 @@ export const EVER_SHIPPED_SKILL_NAMES = new Set([
59
59
  'pmax',
60
60
  'pmid',
61
61
  'pmin',
62
+ 'plugin-eval-standalone-skill',
62
63
  'post-audit-findings',
63
64
  'pr-cleanup',
64
65
  'pr-consistency-audit',
@@ -41,6 +41,10 @@ test('EVER_SHIPPED_SKILL_NAMES includes the windows scheduled task skill', () =>
41
41
  assert.equal(EVER_SHIPPED_SKILL_NAMES.has('windows-scheduled-task'), true);
42
42
  });
43
43
 
44
+ test('EVER_SHIPPED_SKILL_NAMES includes the plugin eval standalone skill', () => {
45
+ assert.equal(EVER_SHIPPED_SKILL_NAMES.has('plugin-eval-standalone-skill'), true);
46
+ });
47
+
44
48
  test('EVER_SHIPPED_SKILL_NAMES includes the session continuity companion', () => {
45
49
  assert.equal(EVER_SHIPPED_SKILL_NAMES.has('session-continuity'), true);
46
50
  });
@@ -6,7 +6,7 @@ Source: Smart & Molak, *BDD in Action* 2e, Chapter 7.6 — scenario quality patt
6
6
 
7
7
  ## Declarative Focus
8
8
 
9
- Scenarios work best when they name what users want to achieve in the language of the business. Lead with goals and recognizable domain tasks so readers grasp intent at a glance. Reserve step-level or UI detail for places where it truly clarifies behavior.
9
+ Scenarios work best when they name what users want to achieve in the language of the business. Lead with goals and recognizable domain tasks so readers grasp intent at a glance. Reserve step-level or UI detail for places where it clarifies behavior.
10
10
 
11
11
  > "Good scenarios model business behavior, not system interactions." — Smart & Molak §7.6.3
12
12
 
@@ -42,7 +42,7 @@ Then I should be presented with the following hotels:
42
42
 
43
43
  Personas ground scenarios in realistic goals and context. Use light soap-opera personas when you need depth before full UX research: introduce names and roles as needed and deepen them across scenarios.
44
44
 
45
- > Smart & Molak §7.6.5 describe personas as rich, realistic descriptions: each persona captures goals, abilities, and background information that ground the test scenario in a real user context.
45
+ > Smart & Molak §7.6.5 describe personas as rich, realistic descriptions: each persona captures goals, abilities, and background information that ground the test scenario in a user context.
46
46
 
47
47
  Good scenarios name who acts and what they need in plain language.
48
48
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  The canonical review-criteria instruction set for every AI agent that audits pull requests in this repository, loaded on demand. [`.cursor/BUGBOT.md`](../../../.cursor/BUGBOT.md) is the checked-in pointer file Cursor BugBot reads; it points here.
4
4
 
5
- ⚡ marks rules enforced by hand-maintained `code_rules_enforcer.py` — the hook blocks the Write/Edit and returns the corrective detail at violation time, so this document lists those rules by name only. Session policy (question routing, task tracking) lives in `rules/*.md`; see [`code-standards.md`](../rules/code-standards.md).
5
+ ⚡ marks rules the hand-maintained `code_rules_enforcer.py` carries. The staged policy lint runs it over each changed file and returns the corrective detail, so this document lists those rules by name only. Session policy (question routing, task tracking) lives in `rules/*.md`; see [`code-standards.md`](../rules/code-standards.md).
6
6
 
7
7
  ---
8
8
 
@@ -25,9 +25,9 @@ Production and tests follow one rule. Changed directive, TODO, FIXME, HACK, XXX,
25
25
 
26
26
  ---
27
27
 
28
- ## ⚡ HOOK-ENFORCED RULES
28
+ ## ⚡ LINT-ENFORCED RULES
29
29
 
30
- `code_rules_enforcer.py` blocks each of these at Write/Edit and explains the specific violation when it fires; exact patterns and exemption lists live in the hook:
30
+ The staged policy lint reports each of these through `code_rules_enforcer.py` and names the specific breach; exact patterns and exemption lists live in that module:
31
31
 
32
32
  no new comments · imports at top · logging format args (`log_*("...", arg)`) · no `%s`/`%d` printf tokens in a `str.format`-logger message (`log_*` imported from `automation_logging`; `str.format` drops the args — use `{}`) · no magic values in production bodies (0, 1, -1 exempt) · UPPER_SNAKE constants only in `config/` (exempt: `config/*`; `/migrations/`; Workflow registries: path contains any of these substrings — `/workflow/`, `_tab.py`, `/states.py`, or `/modules.py`, each matching independently as a substring, so `pkg/states.py` qualifies while a top-level `states.py` follows the standard `config/` rule; test files — path or filename matches `test_`, `_test.`, `.spec.`, `conftest`, or `/tests/`) · no hardcoded user home paths · guarded `sys.path.insert` · banned identifiers (`ctx`, `cfg`, `msg`, `btn`, `idx`, `cnt`, `tmp`, `elem`, `val`) · banned function prefixes (`handle_`, `process_`, `manage_`, `do_`) · no type escape hatches (`Any` import, `cast()`, inline `Any`, a parameter typed bare `object` whose body reads `param.attribute`) outside boundary files · no bare/broad `except` · no `Any` in signatures or class attributes · no stub bodies (`pass`/`...`/`raise NotImplementedError`) outside abstract/Protocol · TypedDict `_encode_*`/`_decode_*` companions in the same module · no test-mode branching in production (use dependency injection) · no thin wrapper modules · Google-style docstrings on public functions with `Args:` matching the signature · boolean names prefixed `is_`/`has_`/`should_`/`can_`/`was_`/`did_` (assignments AND bool-typed parameters) · must-check returns (`find_and_click`, `write_outcome`) assigned and checked · known pytest fixture parameters in test files annotated with their single documented type (`tmp_path: Path`, `monkeypatch: pytest.MonkeyPatch`, `capsys`, `caplog`, `request`, …) · known pytest fixture parameters a test function declares but never references (drop the unused parameter — pytest still pays its setup cost) · JavaScript/TypeScript boolean declarations (`const`/`let`/`var` bound to a boolean literal or negation) and `@param {boolean}` JSDoc names prefixed `is`/`has`/`should`/`can`/`was`/`did` (camelCase forms) · banned identifiers as `.mjs`/`.js` declaration names (`result`, `data`, `ctx`, `msg`, …), scoped to changed lines · in test files, banned identifiers fire on changed lines, and pytest-collectable `test_*` functions need a return annotation · unused module-level imports and unsorted import blocks are ruff's job (F401, isort I001), not this hook's · a `hooks/blocking/` command classifier anchors its multi-word command regex to the command start (`^`/`\A`) or tokenizes the first word (`shlex.split`), never matching a command as a bare substring
33
33
 
@@ -53,7 +53,7 @@ The banned-noun check applies to public function definitions, parameters, and bo
53
53
 
54
54
  ## 6. COMPLETE TYPE HINTS
55
55
 
56
- ALL parameters typed, ALL returns typed. No `Any`. Avoid `# type: ignore`; remove it and use a typed boundary or real type. Prefer fixing the type over an ignore when a real annotation is available.
56
+ ALL parameters typed, ALL returns typed. No `Any`. Avoid `# type: ignore`; remove it and use a typed boundary or the type. Prefer fixing the type over an ignore when an annotation is available.
57
57
 
58
58
  ## 6.5 FILE LENGTH GUIDANCE
59
59
 
@@ -83,7 +83,7 @@ Components own their complete feature (state, modals, overlays, toasts). Parents
83
83
 
84
84
  ## 9.5 NO THIN WRAPPER MODULES
85
85
 
86
- A non-`__init__.py` module whose body is only imports (optionally `__all__`) is indirection without payload — callers import the real module. `__init__.py` is the canonical re-export surface and is exempt.
86
+ A non-`__init__.py` module whose body is only imports (optionally `__all__`) is indirection without payload — callers import the module. `__init__.py` is the canonical re-export surface and is exempt.
87
87
 
88
88
  ## 9.6 NO BACKWARDS-COMPATIBILITY SHIMS
89
89
 
@@ -111,7 +111,7 @@ If you already have the data, don't fetch it again.
111
111
 
112
112
  ## 11.5 VALIDATION-PHASE PRECEDENCE
113
113
 
114
- `code_rules_enforcer.py` decides what a run checks and reports along three independent axes. Each axis filters a narrower scope than the one before it; none widens what the axis before it already decided.
114
+ A staged policy lint run of `code_rules_enforcer.py` selects what it checks and reports along three independent axes. Each axis filters a narrower scope than the one before it; none widens what the axis before it already decided.
115
115
 
116
116
  1. **Phase selects the roster.** `EDIT_LANE_PHASE` or `FULL_GATE_PHASE` decides which checks exist in the lane at all. `validate_content_for_phase` takes `phase` keyword-only with no default, so every caller names its lane explicitly.
117
117
  2. **Target classification filters within a lane.** The hook-infrastructure patterns and the ephemeral-path check decide whether a target is validated, and with which subset. Classification narrows a lane; it never adds a check the phase already excluded.
@@ -127,6 +127,6 @@ Hook-infrastructure targets run three checks in the edit lane — `check_same_fi
127
127
 
128
128
  Three surfaces report on the roster, and each reports a specific thing:
129
129
 
130
- - `hooks/validators/hook_timing_harness.py` builds a `Write` payload against a target that already holds content. `_contents_for_validation` returns `None` for that payload, so the harness times interpreter start and hook dispatch. Time an `Edit` payload against a real file to measure the checks.
130
+ - `hooks/validators/hook_timing_harness.py` builds a `Write` payload against a target that already holds content. `_contents_for_validation` returns `None` for that payload, so the harness times interpreter start and hook dispatch. Time an `Edit` payload against a file to measure the checks.
131
131
  - `~/.claude/logs/hook-blocks.log` records the denials raised by fixtures in `test_code_rules_enforcer_*.py` and by the timing harness's default target.
132
132
  - `hooks/validators/run_all_validators.py` stages the target under a temporary root and rebuilds the shortest path tail that carries every exemption signal. The walk starts at the target's own project root and skips a directory pytest generated for its own scratch tree, so the staged path reads the same wherever `--basetemp` places that tree.
@@ -85,7 +85,7 @@
85
85
 
86
86
  **Mock too much** - Prefer integration tests
87
87
  - Bad: Mock every hook and function
88
- - Good: Render with real hooks, mock only API calls
88
+ - Good: Render with unmocked hooks, mock only API calls
89
89
 
90
90
  **Test implementation details** - Test what user experiences
91
91
  - Bad: Assert on state values, hook return values
@@ -66,7 +66,7 @@ running anything.
66
66
  The NAS is a Linux box and this machine is Windows, so two classes of difference show up only there:
67
67
  syntax newer than the NAS interpreter accepts, and tests that assert Windows paths.
68
68
 
69
- **Always separate a real break from a platform-only one.** Run the same test file at the branch head
69
+ **Always separate a break in the code from a platform-only one.** Run the same test file at the branch head
70
70
  and at the commit the work started from. Identical failures at both mean the environment, not the
71
71
  change. Report both numbers rather than the head alone.
72
72
 
@@ -6,7 +6,7 @@ Source bones: [Anthropic Advisor tool](https://platform.claude.com/docs/en/agent
6
6
 
7
7
  ## What it is
8
8
 
9
- `/team-advisor` is the repository's advisor implementation. It carries the full first packet explicitly, sends later deltas, and owns the warm Agent/SendMessage or read-only Sol CLI lifecycle.
9
+ `/team-advisor` is the repository's advisor implementation. On a Claude host with the built-in `advisor` tool on, it calls `advisor()`, which reads the whole transcript. Otherwise it carries the full first packet explicitly, sends later deltas, and owns the warm Agent/SendMessage or read-only Astra CLI lifecycle.
10
10
 
11
11
  See `team-advisor-skill.md` for the bind and lifecycle map.
12
12
 
@@ -57,7 +57,7 @@ When the consult path supports a free-text brief, append:
57
57
 
58
58
  `(Advisor: please keep your guidance under 80 words — I need a focused starting point, not a comprehensive plan.)`
59
59
 
60
- Size the ask at roughly 80 percent of the true ceiling; direct address to the advisor lands more reliably than a third-person description.
60
+ Size the ask at roughly 80 percent of the ceiling; direct address to the advisor lands more reliably than a third-person description.
61
61
 
62
62
  ## Related
63
63
 
@@ -10,7 +10,7 @@ The flag is `PROSE_STYLE_ENFORCEMENT_ENABLED` in `hooks/blocking/config/prose_st
10
10
 
11
11
  ## Always on
12
12
 
13
- Historical/comparative language detection in `state_description_blocker.py` runs unconditionally and does not read this flag. Apply `rules/asd-ste100-language.md` for user-facing word choice and sentence style. Code-rules and security blockers are out of scope for this switch.
13
+ Historical and comparative language detection lives in `state_description_blocker.py`, which the staged policy lint applies under its state-description rule whatever this flag holds. Apply `rules/asd-ste100-language.md` for user-facing word choice and sentence style. Code-rules and security blockers are out of scope for this switch.
14
14
 
15
15
  ## Hedging claims
16
16
 
@@ -1,6 +1,6 @@
1
1
  # Team-advisor skill
2
2
 
3
- `/team-advisor` binds one standing warm advisor for this session at the strongest reachable tier. It sends explicit packets to a warm Agent/SendMessage advisor or the read-only `codex_astra_advisor.py` helper.
3
+ `/team-advisor` binds one advisor for this session. On a Claude host with the built-in `advisor` tool on, it calls `advisor()`. Otherwise it binds a standing warm advisor at the strongest reachable tier and sends explicit packets to a warm Agent/SendMessage advisor or the read-only `codex_astra_advisor.py` helper.
4
4
 
5
5
  ## Refs
6
6
 
@@ -17,4 +17,4 @@ Follow the call rules in `advisor-tool.md` (orientation first, then consult befo
17
17
 
18
18
  ## Selected path
19
19
 
20
- `/team-advisor` is the repository's advisor implementation. It provides explicit first-consult packets, delta consults, a standing warm reviewer, an in-session Astra spawn on Codex, and a read-only Astra CLI option.
20
+ `/team-advisor` is the repository's advisor implementation. It uses the built-in advisor tool when the session has it. Without the tool, it provides explicit first-consult packets, delta consults, a standing warm reviewer, an in-session Astra spawn on Codex, and a read-only Astra CLI option.
@@ -30,7 +30,7 @@ carries its own packet:
30
30
 
31
31
  - **First consult** — use the complete packet in
32
32
  `~/.claude/_shared/advisor/reference/consult-format.md`: assignment,
33
- desired outcome, constraints and exclusions, actions taken in order, real
33
+ desired outcome, constraints and exclusions, actions taken in order,
34
34
  output and current state, decision or blocker, validation evidence,
35
35
  unresolved risks, and load-bearing paths or excerpts.
36
36
  - **Later consults** — the delta since the last consult.
@@ -1,4 +1,4 @@
1
- """Tests for magic-value allowlist alignment with CODE_RULES §HOOK-ENFORCED.
1
+ """Tests for magic-value allowlist alignment with CODE_RULES §LINT-ENFORCED.
2
2
 
3
3
  CODE_RULES.md states that only
4
4
  0, 1, and -1 (plus their float forms 0.0, 1.0) are exempt from the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.1.0",
3
+ "version": "8.2.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -45,18 +45,15 @@ to be reconstructed from the diff is not recoverable in any useful sense.
45
45
 
46
46
  ## Make the archive stick
47
47
 
48
- Three things do not follow the file on their own.
48
+ Two things do not follow the file on their own.
49
49
 
50
50
  **A skill's name stays in `bin/ever-shipped-skills.mjs`.** The prune computes
51
51
  the retired set as every name ever shipped minus the names installed now, and
52
52
  that is how a stale copy is moved out of a host's agents home. Delete the name
53
- and the copy is stranded on every machine that has it.
54
-
55
- **Add the archived name to the banned active-capability list.** The reference
56
- checker in `scripts/active_capability_references.py` does not resolve a named
57
- capability against the shipped inventory; it reports a name only when that name
58
- sits on `ALL_BANNED_ACTIVE_CAPABILITY_NAMES`. Until the archived name is on that
59
- list, every leftover mention of it passes.
53
+ and the copy is stranded on every machine that has it. The reference checker in
54
+ `scripts/active_capability_references.py` derives its retired set the same way,
55
+ so the registry entry is also what makes a leftover mention of the archived
56
+ name report. No second list needs the name.
60
57
 
61
58
  **A renamed skill directory takes its tests out of the run.** The node test
62
59
  command globs `.agents/skills/**/*.test.mjs`, so a suite under an archived skill
@@ -12,7 +12,7 @@ This is the autonomous-run partner to the ambiguous case: research and recommend
12
12
 
13
13
  ## Do not end a turn on a promise
14
14
 
15
- Pause for the user only when the work truly needs them: a destructive or irreversible action, a scope change, or input only they can give. When you hit one, ask through `AskUserQuestion` and end the turn. Do not end on a promise about work you have not done.
15
+ Pause for the user only when the work needs them: a destructive or irreversible action, a scope change, or input only they can give. When you hit one, ask through `AskUserQuestion` and end the turn. Do not end on a promise about work you have not done.
16
16
 
17
17
  A pause is a request you deliver, not a state you enter. Put it in the channel the user reads, name the one input you need and what resumes the moment it arrives, and shape it so a single word answers it. A decision recorded in a status line, a checklist, a working document, or a side thread is a note; the user never learns they are the bottleneck, and the work sits. Keep everything that does not depend on the answer moving while it is outstanding.
18
18