You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Produced by /plugin-quality:audit after a real --execute run against a user profile: 17 paths, 8,529,560 bytes removed on a machine with 3.71 TB free. Every path was verified clear immediately before deletion; nothing was skipped or wrongly deleted. The safety machinery worked correctly throughout.
It cost roughly 50 tool calls, ~34 permission prompts, and about an hour. The operator's verbatim assessment: "Good god this is all performing so poorly."
Correct behavior, unacceptable experience. That gap is what this tracks.
handoff-verify lacks the container semantics Linux apply has
this issue
F6
low-medium
Managed-state exclusion contradicts the plugin's own baseline policy
this issue
F11
low-medium
Residual fail-open when no Python resolves (disclosed)
this issue
F12
low-medium
Telemetry no-op unless HOOK_TELEMETRY_SINK set; no auditable decisions
this issue
F4
informational
Most per-path model work is already engine work
this issue
Withdrawn during the audit: F9.1 and F9.2, plus Blindspot 5's attribution. A two-cell control falsified the original single-observation inference. They are listed here so the count is not read as 14 live findings.
F14 — the headline
shutil.disk_usage appears exactly twice in the 149 KB engine (hygiene.py:3201, :3314), both inside apply, read after every decision purely to populate observed_free_space_delta_bytes. It gates nothing, ranks nothing, skips nothing. And apply is Linux-only, so on Windows the engine never reads free space at all — the operator's 3.71 TB is invisible to the component on their platform.
The gates that exist (large-target-confirmation-required, --max-depth) scale with scan cost; safety-model.md:445-451 says so explicitly. Three rules then make proportionality unreachable by design:
SKILL.md:33-35 — "reclaimed bytes are secondary"
SKILL.md:208-209 — "byte size is never a ranking key"
SKILL.md:264-265 — "Never treat a low or zero reclaimable-byte figure as a reason to skip a finding"
Each is individually defensible and each earned its keep in this run. Jointly they mean the component cannot distinguish a 3.7-TB-free tidiness pass from a disk-full emergency, and is instructed not to try.
Per-path ceremony is a constant, so cost scales with finding COUNT, not finding VALUE. That makes a 17-small-item tidiness pass the worst case the design produces — and the most common one.
This is not a neutral tax; it directly causes residue. Two empty directory trees were knowingly left on disk rather than pay the two-scan cost again. The skill's stated primary objective is tidiness, and its cost model defeated that objective.
A related gap: the skill has no notion of regenerable at a cost, so a 2.78 GB rebuildable build cache and 2.78 GB of irreplaceable data rank identically.
Remaining findings tracked here
F3 — execution_blockers gates on os_key() != "linux" whole-lane rather than per-primitive, excluding macOS despite it having dirfd + lsof. Decline is dated and affirmed (#1116) with a stated reversal trigger.
F9.3 / F9.4 — No escape hatch for the belt; the kill switch makes it stricter, not looser. Bash deny-by-default exceeds its threat model and inverts on Windows, where the hard-denied lane cannot delete anyway. Disclosure alone is not a remedy for a session-long lockout with no lever. Note the belt does not reach subagents — an earlier finding to the contrary was withdrawn — and that non-inheritance is undocumented and build-specific, so documenting it as a recovery lane needs that caveat.
F5 — Provenance and ranking are mandated with no mechanism. Hint coverage was 1 of 76 entries. What actually worked was ad-hoc, reproducible, and computable from data the snapshot already holds:
a PID embedded in a staging filename, cross-checked against the live process list
gaps in a numbering sequence marking an already-abandoned set
identical byte lengths across four files revealing one repeated HTTP 429 body
a shared write-second between a leaked write-probe and its sibling config
a log line naming the writer (python/3.14.6; huggingface_hub/1.27.0; hf_xet/1.6.0)
Sibling-mtime clustering and size-collision detection are computable today.
F8 — handoff-verify lacks the bottom-up container semantics Linux apply already implements. A directory cannot be named inside its own snapshot and is truncated-not-inventoried in its parent's until a rescan walks it empty, so removing one empty directory took two full scan rounds.
F6 — The managed-state exclusion contradicts the plugin's own baseline policy, which ships a .pulumi-write-test-* hint it then makes engine-ineligible. Two pieces of genuine residue found inside managed directories will never be reclaimed by their owners. --vcs-evidence is the in-tree precedent for a narrow evidence-gated exception.
F11 — Residual fail-open: if no Python resolves, the guard exits 0 silently; only a Stop-hook detector reports it, after the fact. Disclosed by the plugin.
F12 — Telemetry is a no-op unless HOOK_TELEMETRY_SINK is set, and the data root holds only runs/, so no guard decision is auditable after the fact.
F4 — Most per-path model work is already engine work; genuine per-OS divergence is small. One script with per-OS branches, which the engine already is.
Remediations not covered by a scoped issue
Append a durable guard-decision line to CLAUDE_PLUGIN_DATA (F12).
Document the belt's recovery lanes, including the subagent one, and add an escape hatch (F9).
Write down the five provenance techniques above; emit sibling-mtime clusters and size collisions from existing snapshot data (F5).
Give handoff-verify container semantics mirroring Linux apply (F8).
A stated batch posture for low-stakes targets (F14) — relaxes orchestration, not checks.
Narrow the Bash belt from deny-all to deny-mutation (F9.4); split the platform gate so macOS fails on mountinfo specifically (F3).
Reopen chore(disk-hygiene): affirm recorded decline of Windows engine-execution lane (F12) #1116 with F1 as new input. An interleaved verify-and-delete subcommand closes F2, F3 and most of F14 together and is stricter than today's model-mediated gap — but it is a Windows deletion lane and belongs to the maintainer under the full security review the existing reversal trigger specifies.
What the component gets right
Most of the sharpest findings above were possible because of this: the documentation is unusually honest. It names its own fail-open, states that coverage is enumerated rather than complete, dates its declines, and pre-warns that observed free-space deltas are inexact.
Its byte-accounting discipline prevented a false claim during this run — an unqualified recursive size sum reported a Docker directory at 54.84 GB, and the insistence on size_qualifiers forced a sparse check before that number could be used. Its name-is-not-a-verdict rule prevented a real mistake: .codex/.tmp (6,270 files, 134 MB) reads as scratch and is live plugin state.
Where it goes wrong is asserting a human where the docs establish a hook (F1), and describing one guard with the other's message (F13).
Limits of this audit
Single platform. The Linux apply lane was never exercised.
Single run, single target.
One permission mode. F1's experiment measured the session's own mode only.
test_hygiene.py (356 KB) was not read. Some remediation proposed here may already be tested-as-unsupported.
No 0.20.20 → 0.20.21 diff, so nothing is characterized as a regression versus long-standing behavior.
Not audited: the sibling setup skill's workflow, evals/, killswitch_config.py scope resolution.
F10's number is n=5 on one machine, Git Bash on Windows, range 817–2,657 ms (3.2x spread), no plugin-disabled control.
Methodological note: two findings were single-observation inferences that a later control falsified. They were withdrawn rather than shipped.
Findings came from a fresh-context auditor that never saw the reasoning of the session under audit, then were independently re-validated by two further fresh-context validators that never saw each other's verdicts.
plugins/disk-hygiene, skillclean, v0.20.21. Windows 11, PowerShell 7 + Git Bash.Produced by
/plugin-quality:auditafter a real--executerun against a user profile: 17 paths, 8,529,560 bytes removed on a machine with 3.71 TB free. Every path was verifiedclearimmediately before deletion; nothing was skipped or wrongly deleted. The safety machinery worked correctly throughout.It cost roughly 50 tool calls, ~34 permission prompts, and about an hour. The operator's verbatim assessment: "Good god this is all performing so poorly."
Correct behavior, unacceptable experience. That gap is what this tracks.
Sealed evidence packet (findings, controls, withdrawals, validation):
~/.claude/plugins/data/plugin-quality-melodic-software/evidence/<session>/disk-hygiene-clean/20260824T165142Z/Severity table
Ships
evidence-3.md's revised ordering plus F14, notaudit-notes.md's original table, which the audit itself superseded.ask; docs do not guarantee a humanhandoff-verifylacks the container semantics LinuxapplyhasHOOK_TELEMETRY_SINKset; no auditable decisionsWithdrawn during the audit: F9.1 and F9.2, plus Blindspot 5's attribution. A two-cell control falsified the original single-observation inference. They are listed here so the count is not read as 14 live findings.
F14 — the headline
shutil.disk_usageappears exactly twice in the 149 KB engine (hygiene.py:3201,:3314), both insideapply, read after every decision purely to populateobserved_free_space_delta_bytes. It gates nothing, ranks nothing, skips nothing. Andapplyis Linux-only, so on Windows the engine never reads free space at all — the operator's 3.71 TB is invisible to the component on their platform.The gates that exist (
large-target-confirmation-required,--max-depth) scale with scan cost;safety-model.md:445-451says so explicitly. Three rules then make proportionality unreachable by design:SKILL.md:33-35— "reclaimed bytes are secondary"SKILL.md:208-209— "byte size is never a ranking key"SKILL.md:264-265— "Never treat a low or zero reclaimable-byte figure as a reason to skip a finding"Each is individually defensible and each earned its keep in this run. Jointly they mean the component cannot distinguish a 3.7-TB-free tidiness pass from a disk-full emergency, and is instructed not to try.
Per-path ceremony is a constant, so cost scales with finding COUNT, not finding VALUE. That makes a 17-small-item tidiness pass the worst case the design produces — and the most common one.
This is not a neutral tax; it directly causes residue. Two empty directory trees were knowingly left on disk rather than pay the two-scan cost again. The skill's stated primary objective is tidiness, and its cost model defeated that objective.
A related gap: the skill has no notion of regenerable at a cost, so a 2.78 GB rebuildable build cache and 2.78 GB of irreplaceable data rank identically.
Remaining findings tracked here
F3 —
execution_blockersgates onos_key() != "linux"whole-lane rather than per-primitive, excluding macOS despite it having dirfd + lsof. Decline is dated and affirmed (#1116) with a stated reversal trigger.F9.3 / F9.4 — No escape hatch for the belt; the kill switch makes it stricter, not looser. Bash deny-by-default exceeds its threat model and inverts on Windows, where the hard-denied lane cannot delete anyway. Disclosure alone is not a remedy for a session-long lockout with no lever. Note the belt does not reach subagents — an earlier finding to the contrary was withdrawn — and that non-inheritance is undocumented and build-specific, so documenting it as a recovery lane needs that caveat.
F5 — Provenance and ranking are mandated with no mechanism. Hint coverage was 1 of 76 entries. What actually worked was ad-hoc, reproducible, and computable from data the snapshot already holds:
python/3.14.6; huggingface_hub/1.27.0; hf_xet/1.6.0)Sibling-mtime clustering and size-collision detection are computable today.
F8 —
handoff-verifylacks the bottom-up container semantics Linuxapplyalready implements. A directory cannot be named inside its own snapshot and istruncated-not-inventoriedin its parent's until a rescan walks it empty, so removing one empty directory took two full scan rounds.F6 — The managed-state exclusion contradicts the plugin's own baseline policy, which ships a
.pulumi-write-test-*hint it then makes engine-ineligible. Two pieces of genuine residue found inside managed directories will never be reclaimed by their owners.--vcs-evidenceis the in-tree precedent for a narrow evidence-gated exception.F11 — Residual fail-open: if no Python resolves, the guard exits 0 silently; only a Stop-hook detector reports it, after the fact. Disclosed by the plugin.
F12 — Telemetry is a no-op unless
HOOK_TELEMETRY_SINKis set, and the data root holds onlyruns/, so no guard decision is auditable after the fact.F4 — Most per-path model work is already engine work; genuine per-OS divergence is small. One script with per-OS branches, which the engine already is.
Remediations not covered by a scoped issue
CLAUDE_PLUGIN_DATA(F12).handoff-verifycontainer semantics mirroring Linuxapply(F8).What the component gets right
Most of the sharpest findings above were possible because of this: the documentation is unusually honest. It names its own fail-open, states that coverage is enumerated rather than complete, dates its declines, and pre-warns that observed free-space deltas are inexact.
Its byte-accounting discipline prevented a false claim during this run — an unqualified recursive size sum reported a Docker directory at 54.84 GB, and the insistence on
size_qualifiersforced a sparse check before that number could be used. Its name-is-not-a-verdict rule prevented a real mistake:.codex/.tmp(6,270 files, 134 MB) reads as scratch and is live plugin state.Where it goes wrong is asserting a human where the docs establish a hook (F1), and describing one guard with the other's message (F13).
Limits of this audit
applylane was never exercised.test_hygiene.py(356 KB) was not read. Some remediation proposed here may already be tested-as-unsupported.setupskill's workflow,evals/,killswitch_config.pyscope resolution.Findings came from a fresh-context auditor that never saw the reasoning of the session under audit, then were independently re-validated by two further fresh-context validators that never saw each other's verdicts.
🤖 Generated with Claude Code