From a28124f2c262b7109975bd6e912c488322dda5c7 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:25:09 -0400 Subject: [PATCH 1/4] fix(disk-hygiene): retract false PowerShell non-firing claim, document the real bare-argv drop (#1195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0.8.2 shipped a safety-model claim that PreToolUse guards do not intercept PowerShell-tool commands, scoped behind a preview caveat. A fresh-session controlled test falsified it: a Bash|PowerShell matcher DOES fire for the PowerShell tool on 2.1.218, payload tool_name is literally "PowerShell", and a live Set-Content through that tool was blocked. No harness divergence, no preview limitation — 0.8.2 asserted an un-isolated inference as fact. Removed. The real defect, now documented: hooks/hooks.json passes a bare ${user_config.disk_hygiene_enabled}, and upstream never implemented the declared userConfig default, so an unset-but-defaulted token is neither substituted nor exported to CLAUDE_PLUGIN_OPTION_* and its presence drops the entire hook entry. The engine gate has therefore never run for any consumer who never set the key — on Bash and PowerShell alike, which is the real shape of the reported bypass. The skill-scoped belt carries no such token and is unaffected. Code fix tracked separately. disk-hygiene 0.8.3. Co-Authored-By: Claude Fable 5 --- .../disk-hygiene/.claude-plugin/plugin.json | 2 +- plugins/disk-hygiene/CHANGELOG.md | 21 +++++++++ plugins/disk-hygiene/skills/clean/SKILL.md | 44 +++++++++---------- .../skills/clean/reference/safety-model.md | 28 ++++++------ 4 files changed, 58 insertions(+), 37 deletions(-) diff --git a/plugins/disk-hygiene/.claude-plugin/plugin.json b/plugins/disk-hygiene/.claude-plugin/plugin.json index 3d289bac6a..16c0c67c78 100644 --- a/plugins/disk-hygiene/.claude-plugin/plugin.json +++ b/plugins/disk-hygiene/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", "name": "disk-hygiene", - "version": "0.8.2", + "version": "0.8.3", "description": "Context-aware disk hygiene for arbitrary directory trees: inventories orphaned and temporary artifacts, classifies evidence into review tiers, and offers exact-path cleanup only after a fresh safety preview and explicit per-tier approval. The target is read-only by default; OS-managed paths, links and mount points, VCS-tracked content, changed entries, and live-handle uncertainty fail closed.", "author": { "name": "Melodic Software", diff --git a/plugins/disk-hygiene/CHANGELOG.md b/plugins/disk-hygiene/CHANGELOG.md index 3852d08da4..a02a06fc78 100644 --- a/plugins/disk-hygiene/CHANGELOG.md +++ b/plugins/disk-hygiene/CHANGELOG.md @@ -3,6 +3,27 @@ All notable changes to the `disk-hygiene` plugin are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning. +## [0.8.3] + +### Fixed + +- **RETRACTS 0.8.2's PowerShell claim, which was wrong (#1195).** 0.8.2 documented that "PreToolUse guards + do not intercept PowerShell-tool commands" and scoped the PowerShell lane behind a preview caveat. A + fresh-session controlled test falsified that: a `Bash|PowerShell` PreToolUse matcher **does** fire for the + PowerShell tool on 2.1.218, the payload `tool_name` is literally `PowerShell`, and a live `Set-Content` + through that tool was blocked. There is no harness firing divergence and no preview limitation involved — + 0.8.2's caveat overstated an un-isolated inference and is removed. +- **The real defect, now documented accurately: the plugin-level engine gate is inert whenever + `disk_hygiene_enabled` is unconfigured.** `hooks/hooks.json` passes a bare + `${user_config.disk_hygiene_enabled}`; upstream never implemented the declared userConfig `default`, so an + unset-but-defaulted token is neither substituted nor exported as `CLAUDE_PLUGIN_OPTION_*` and its presence + **drops the entire hook entry** (proven: token-carrying hooks vanish while token-free controls fire, and + return once the key is configured). So the gate has never run for any consumer who never set the key — on + Bash and PowerShell alike, which is the real shape of the reported "PowerShell bypass". The skill-scoped + belt carries no such token and is unaffected. `SKILL.md` + `reference/safety-model.md` corrected; the code + fix (a delivery channel that does not depend on the unimplemented `default`) is tracked separately. + Recheck when the upstream gap closes (#46477 / #39455 / #39827). + ## [0.8.2] ### Fixed diff --git a/plugins/disk-hygiene/skills/clean/SKILL.md b/plugins/disk-hygiene/skills/clean/SKILL.md index ef273a3840..4c4cc61acc 100644 --- a/plugins/disk-hygiene/skills/clean/SKILL.md +++ b/plugins/disk-hygiene/skills/clean/SKILL.md @@ -261,15 +261,15 @@ handoff, not an engine plan: 4. Skip and report any path whose verdict is not `clear`; never substitute a sibling, retry around a lock, or delete under a stale verdict. -The PowerShell guard lane is *designed* to turn deletion spellings into a final human permission prompt -(the same bar as the engine apply prompt); confirm that prompt only when the command matches the exact -approved list. Engine invocations from PowerShell are *designed* to stay hard-denied (also subject to -the caveat below). **Caveat (Claude Code 2.1.218, Windows): none of this PowerShell interception fires -for the PowerShell tool** — the deletion-spelling prompt AND the engine-invocation deny are both inert; -the PowerShell tool is in preview and -PreToolUse hooks were reproduced not to intercept PowerShell-tool commands, so on Windows do not rely on -this prompt; the manual lane's per-path `handoff-verify` approval and the baseline permission policy are -the protections that actually hold. See the PowerShell-preview gotcha below and `reference/safety-model.md`. +The PowerShell guard lane turns deletion spellings into a final human permission prompt (the same +bar as the engine apply prompt); confirm that prompt only when the command matches the exact +approved list. Engine invocations from PowerShell stay hard-denied. **Caveat — the plugin-level engine +gate is inert until `disk_hygiene_enabled` is explicitly configured (verified on Claude Code 2.1.218):** +that gate (`hooks/hooks.json`) passes a bare `${user_config.disk_hygiene_enabled}`, and an +unset-but-defaulted userConfig value drops the whole hook entry, so for any consumer who never set the +key the gate never runs — on the Bash tool and the PowerShell tool alike. The skill-scoped belt (this +skill's frontmatter hook) carries no such token and is unaffected; `Bash|PowerShell` PreToolUse hooks do +fire for the PowerShell tool. See `reference/safety-model.md`. Summarize removed paths, logical bytes removed, observed free-space delta, and every skip grouped by `locked`, `changed-or-link`, `protected`, `needs-elevation`, `handle-state-unverified`, or @@ -316,21 +316,21 @@ sparse files, hard links, compression, and delayed allocation affect it. manual-handoff lane already requires and the consumer's baseline permission policy — defense-in-depth lost, not preserved. `/disk-hygiene:setup check` reports whether the interpreter resolves on this machine. -- **The PowerShell deletion belt does not fire for the PowerShell *tool* on current builds (Claude Code - 2.1.218, Windows) — distinct from the `python3`-resolution loss above.** Even with `python3` resolving, - a `Bash|PowerShell` PreToolUse hook was reproduced to fire for the Bash tool but NOT intercept - PowerShell-*tool* commands. The PowerShell tool is a documented *preview* feature - ([tools-reference](https://code.claude.com/docs/en/tools-reference)); PreToolUse interception of it is - not a listed preview limitation, so this is an observed docs-vs-behavior gap (mechanism not yet - isolated — matcher firing vs Windows payload delivery vs the tool's `tool_name`). Consequence on - Windows: the PowerShell deletion belt AND the `disk_hygiene_enabled` kill switch's reach into the - manual PowerShell lane are both inert; the protections that hold are the manual lane's per-path - `handoff-verify` approval and the consumer's baseline permission policy. Recheck when the PowerShell - tool exits preview or when interception is verified directly. +- **The plugin-level engine gate is dropped whenever `disk_hygiene_enabled` is unconfigured (Claude Code + 2.1.218) — distinct from the `python3`-resolution loss above.** `hooks/hooks.json` passes a bare + `${user_config.disk_hygiene_enabled}`; a declared userConfig `default` is not implemented upstream, so an + unset-but-defaulted token is neither substituted nor exported to `CLAUDE_PLUGIN_OPTION_*` and its presence + **drops the whole hook entry**. Fresh-session controlled test: token-carrying hooks vanish while token-free + controls fire, and return once the key is configured. Consequence: for any consumer who never set the key, + this gate has never run — on Bash and PowerShell alike. The skill-scoped belt carries no such token and is + unaffected. Recheck when the upstream `default` gap closes (#46477 / #39455 / #39827). +- **PreToolUse hooks DO fire for the PowerShell tool** (2.1.218; payload `tool_name` is literally + `PowerShell`, confirmed by a live block through that tool). A `Bash|PowerShell` matcher is correct and + there is no harness firing divergence — read `tool_name` from the stdin payload, not from an env var + (`CLAUDE_TOOL_NAME` does not exist). - The PowerShell lane is the inverse tradeoff: it stays open for read-only support work (git, gh, metadata probes) and instead hard-denies engine invocations and turns known deletion spellings - into a final human permission prompt (**subject to the preview caveat above — this does not fire for - the PowerShell tool on 2.1.218**). It is a raised bar, not a fail-closed lane; the engine's + into a final human permission prompt. It is a raised bar, not a fail-closed lane; the engine's own containment and the Bash lane remain the deletion authority. - The guard rejects `~` anywhere in a Bash command as a shell-expansion character, which includes Windows 8.3 short names (`SOMEUS~1`). Always pass long-form paths; the guard's own disclosures diff --git a/plugins/disk-hygiene/skills/clean/reference/safety-model.md b/plugins/disk-hygiene/skills/clean/reference/safety-model.md index a836501ac0..8197911560 100644 --- a/plugins/disk-hygiene/skills/clean/reference/safety-model.md +++ b/plugins/disk-hygiene/skills/clean/reference/safety-model.md @@ -144,20 +144,20 @@ switch. When the guard sees execution enabled they are downgraded to a final hum when it sees a configured `false` (audit-only mode) they are denied outright, so the kill switch would block deletions on the PowerShell lane too and not only the Bash engine apply. -**Preview caveat — PowerShell-tool interception does NOT fire on current builds (verified on Claude -Code 2.1.218, Windows).** The PowerShell tool is a documented *preview* feature -([tools-reference](https://code.claude.com/docs/en/tools-reference)); on 2.1.218 a `Bash|PowerShell` -PreToolUse hook was reproduced to fire for the Bash tool but **not** intercept PowerShell-*tool* -commands. So the entire PowerShell paragraph above — the deletion-spelling belt, the `ask` downgrade, -and the audit-only `deny` — **does not take effect for the PowerShell tool on this build**, and the -`disk_hygiene_enabled` kill switch does not reach the PowerShell manual-deletion lane. This is an -observed effect; the mechanism (matcher firing vs Windows payload delivery vs the tool's `tool_name`) -is not yet isolated. Treat every "prompt"/"deny" claim in this section as the guard's *intended* design, -**not a protection in force on Windows**: on Windows the only deletion protection that actually holds is -the manual lane's per-path human `handoff-verify` approval plus the consumer's baseline permission -policy. **Recheck** when the PowerShell tool exits preview, or verify firing directly: in a fresh session -add a logging `PreToolUse` hook with `matcher: "PowerShell"` and confirm it runs for a PowerShell-*tool* -command (or check whether a known deletion spelling is actually prompted), before relying on the lane. +**Caveat — the plugin-level engine gate is inert by default (verified on Claude Code 2.1.218).** The +gate (`hooks/hooks.json`, exec form) passes a bare `${user_config.disk_hygiene_enabled}`. A declared +userConfig `default` is **not implemented**: an unset-but-defaulted `${user_config.*}` is neither +substituted nor exported as `CLAUDE_PLUGIN_OPTION_*`, and its presence in an exec-form arg **drops the +entire hook entry**. (Fresh-session controlled test: the token-carrying hooks vanish while token-free +control hooks fire, and reappear unchanged once the key is configured.) So for every consumer who never +explicitly set `disk_hygiene_enabled`, this engine gate has never run — on the Bash tool and the +PowerShell tool alike, which is the real shape of the "PowerShell bypass" originally reported. + +The **skill-scoped belt is unaffected** — it carries no `${user_config.*}` token — and PreToolUse hooks +with a `Bash|PowerShell` matcher **do** fire for the PowerShell tool on 2.1.218 (payload `tool_name` is +literally `PowerShell`, confirmed by a live block through that tool). There is no harness firing +divergence. **Recheck** when the upstream `userConfig` `default` gap is fixed (#46477 / #39455 / #39827), +which would let the gate resolve its declared default instead of dropping. Kill-switch enforcement is only as reachable as the value is, and the guard now registers on two surfaces with different reach. The **plugin-level engine gate** (`hooks/hooks.json`, exec form, From 3d658d497aed5afd4321580ab494c5baa79fd77c Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:49:46 -0400 Subject: [PATCH 2/4] docs(disk-hygiene): correct README engine-gate claims to match the bare-argv drop (#1195) README still told consumers the engine gate "fires in every session" and that audit-only mode is "guard-enforced ... in every session". Per the proven bare-argv drop, the gate only registers once disk_hygiene_enabled is explicitly configured; on a default (unconfigured) install it does not run at all. Both claims scoped to the configured case with the caveat; the skill self-enforcement is named as the primary kill-switch honoring on a default install. Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/disk-hygiene/CHANGELOG.md | 6 ++++-- plugins/disk-hygiene/README.md | 27 ++++++++++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/plugins/disk-hygiene/CHANGELOG.md b/plugins/disk-hygiene/CHANGELOG.md index a02a06fc78..4173bf1cce 100644 --- a/plugins/disk-hygiene/CHANGELOG.md +++ b/plugins/disk-hygiene/CHANGELOG.md @@ -20,8 +20,10 @@ All notable changes to the `disk-hygiene` plugin are documented here. Format fol **drops the entire hook entry** (proven: token-carrying hooks vanish while token-free controls fire, and return once the key is configured). So the gate has never run for any consumer who never set the key — on Bash and PowerShell alike, which is the real shape of the reported "PowerShell bypass". The skill-scoped - belt carries no such token and is unaffected. `SKILL.md` + `reference/safety-model.md` corrected; the code - fix (a delivery channel that does not depend on the unimplemented `default`) is tracked separately. + belt carries no such token and is unaffected. `SKILL.md`, `reference/safety-model.md`, and the consumer + `README.md` (which still claimed the gate "fires in every session" and audit-only is "guard-enforced") + corrected; the code fix (a delivery channel that does not depend on the unimplemented `default`) is + tracked separately. Recheck when the upstream gap closes (#46477 / #39455 / #39827). ## [0.8.2] diff --git a/plugins/disk-hygiene/README.md b/plugins/disk-hygiene/README.md index 3ee06c7b01..e909b5b794 100644 --- a/plugins/disk-hygiene/README.md +++ b/plugins/disk-hygiene/README.md @@ -50,9 +50,14 @@ at preview. Backups remain the recovery boundary for user data. there, so treat the number printed here as a convenience copy). Claude Code launches the guard in shell-free exec form; guarded engine calls must use the same absolute interpreter reported by that guard, so Bash aliases and functions cannot replace it. The guard registers on two surfaces: a - plugin-level **engine gate** (`hooks/hooks.json`) that fires in every session but acts only on - commands referencing the engine — deferring everything else instantly — and enforces the - configured kill switch and data-root authority through plugin-hook substitution; and the + plugin-level **engine gate** (`hooks/hooks.json`) that acts only on commands referencing the + engine — deferring everything else instantly — and enforces the configured kill switch and + data-root authority through plugin-hook substitution. **Caveat (verified on Claude Code 2.1.218):** + that gate only registers once `disk_hygiene_enabled` is **explicitly configured**. Upstream never + implemented the declared userConfig `default`, so while the option is unset its + `${user_config.disk_hygiene_enabled}` argument is neither substituted nor exported, and its + presence **drops the whole hook entry** — the gate does not run at all, on either tool. The + skill-scoped belt below carries no such token and is unaffected. And the skill-scoped **belt** inside the `clean` skill's context, which adds the deny-by-default Bash and deletion-spelling PowerShell discipline during active cleanup work. Hook-lifetime caveat: docs scope a skill hook to the component's lifetime, but session-long firing of the belt has been @@ -148,12 +153,16 @@ hand-cleaning the zone. - **MCP / external trust:** no MCP server, agent, dependency, or third-party service is shipped. - **Configuration:** one non-sensitive `userConfig` boolean (`disk_hygiene_enabled`, default `true`) gating the execution tiers — setting it `false` puts `/disk-hygiene:clean` in audit-only - mode. That mode is now guard-enforced: the plugin-level engine gate receives the configured value - by exec-form substitution and denies engine invocations outright when it is `false`, in every - session. The skill self-enforcement (kill-switch probe + skill-content value) and the skill-scoped - belt remain as redundant layers; the belt still cannot receive the value (skill-frontmatter hooks - get neither `${user_config.*}` substitution nor `CLAUDE_PLUGIN_OPTION_*`), and still forces a - human prompt before every mutation. + mode. When the value is **explicitly configured `false`**, the plugin-level engine gate receives it + by exec-form substitution and denies engine invocations outright. **Caveat (verified on Claude Code + 2.1.218):** this holds only for a configured value — because upstream never implemented the declared + userConfig `default`, an *unset* `disk_hygiene_enabled` is neither substituted nor exported, and its + presence in the gate's args drops the whole hook, so on a default (unconfigured) install the engine + gate does not run at all. The skill self-enforcement (kill-switch probe + skill-content value) is + therefore the primary kill-switch honoring on a default install, not a redundant layer; the + skill-scoped belt cannot receive the value either (skill-frontmatter hooks get neither + `${user_config.*}` substitution nor `CLAUDE_PLUGIN_OPTION_*`) and still forces a human prompt before + every mutation. - **Trust-surface record (0.7.0):** the plugin-level `hooks/hooks.json` PreToolUse registration is a NEW trust surface (a hook that launches in every consumer session), added deliberately for guard-enforced audit-only mode and data-root authority (#1106 decision, Option E — split From b5c4a6617d6eeaa817e167afdd01a44ef339ac9d Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:59:48 -0400 Subject: [PATCH 3/4] docs(disk-hygiene): caveat the remaining "fires in every session" engine-gate claims (#1195) Codex review + a full sweep found three more spots that still told consumers the plugin-level engine gate fires in every session / audit-only is guard-enforced, contradicting this retraction: clean SKILL.md Gotchas, setup SKILL.md step 1 (direct hygiene.py invocations "meet the engine gate"), and safety-model.md's kill-switch-reach paragraph. All three scoped to the explicitly-configured case with the bare-argv-drop caveat, so a default-configured consumer no longer gets contradictory safety guidance. Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/disk-hygiene/CHANGELOG.md | 8 ++++---- plugins/disk-hygiene/skills/clean/SKILL.md | 11 +++++++---- .../skills/clean/reference/safety-model.md | 11 ++++++++--- plugins/disk-hygiene/skills/setup/SKILL.md | 10 ++++++---- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/plugins/disk-hygiene/CHANGELOG.md b/plugins/disk-hygiene/CHANGELOG.md index 4173bf1cce..50efc66a7d 100644 --- a/plugins/disk-hygiene/CHANGELOG.md +++ b/plugins/disk-hygiene/CHANGELOG.md @@ -20,10 +20,10 @@ All notable changes to the `disk-hygiene` plugin are documented here. Format fol **drops the entire hook entry** (proven: token-carrying hooks vanish while token-free controls fire, and return once the key is configured). So the gate has never run for any consumer who never set the key — on Bash and PowerShell alike, which is the real shape of the reported "PowerShell bypass". The skill-scoped - belt carries no such token and is unaffected. `SKILL.md`, `reference/safety-model.md`, and the consumer - `README.md` (which still claimed the gate "fires in every session" and audit-only is "guard-enforced") - corrected; the code fix (a delivery channel that does not depend on the unimplemented `default`) is - tracked separately. + belt carries no such token and is unaffected. Every doc that claimed the gate "fires in every session" + or that audit-only mode is "guard-enforced" corrected: the `clean` and `setup` `SKILL.md` files, + `reference/safety-model.md`, and the consumer `README.md`. The code fix (a delivery channel that does + not depend on the unimplemented `default`) is tracked separately. Recheck when the upstream gap closes (#46477 / #39455 / #39827). ## [0.8.2] diff --git a/plugins/disk-hygiene/skills/clean/SKILL.md b/plugins/disk-hygiene/skills/clean/SKILL.md index 4c4cc61acc..e7f3b5d057 100644 --- a/plugins/disk-hygiene/skills/clean/SKILL.md +++ b/plugins/disk-hygiene/skills/clean/SKILL.md @@ -299,10 +299,13 @@ sparse files, hard links, compression, and delayed allocation affect it. apply shapes using the hook runtime's same absolute executable pass. Shell expansions, globs, splitting/escape forms, operators, redirections, aliases, and exported functions fail closed. - The guard registers twice: a plugin-level engine gate (`hooks/hooks.json`, `--mode engine-gate`) - that fires in every session, receives the kill switch and data root by plugin-hook substitution, - and defers instantly on any command not referencing the engine; and this skill's frontmatter belt, - which adds the deny-by-default Bash and deletion-spelling PowerShell discipline while cleanup is - the active work. Verdicts are idempotent where both fire. + that receives the kill switch and data root by plugin-hook substitution and defers instantly on any + command not referencing the engine; and this skill's frontmatter belt, which adds the deny-by-default + Bash and deletion-spelling PowerShell discipline while cleanup is the active work. Verdicts are + idempotent where both fire. **Caveat (verified on Claude Code 2.1.218):** the engine gate only + registers once `disk_hygiene_enabled` is explicitly configured — its bare `${user_config.*}` argument + drops the whole hook while the option is unset (upstream never implemented the declared `default`), so + on a default install only the frontmatter belt runs, and only during `clean`. - The guard hook launches in exec form via `python3`, resolved on `PATH` with no shell (`python3`, not bare `python`, because stock macOS and many Linux distros ship only `python3` and a legacy `python` 2.x would crash the guard on modern syntax). Enforcement is therefore only as strong as diff --git a/plugins/disk-hygiene/skills/clean/reference/safety-model.md b/plugins/disk-hygiene/skills/clean/reference/safety-model.md index 8197911560..0d3180b73e 100644 --- a/plugins/disk-hygiene/skills/clean/reference/safety-model.md +++ b/plugins/disk-hygiene/skills/clean/reference/safety-model.md @@ -162,9 +162,14 @@ which would let the gate resolve its declared default instead of dropping. Kill-switch enforcement is only as reachable as the value is, and the guard now registers on two surfaces with different reach. The **plugin-level engine gate** (`hooks/hooks.json`, exec form, `--mode engine-gate`) receives `${user_config.disk_hygiene_enabled}` and `${CLAUDE_PLUGIN_DATA}` -by substitution — channels Claude Code documents for plugin hooks — so a configured `false` is -guard-enforced against every engine invocation in every session, whether or not the clean skill is -active. The gate defers instantly (no output) for any command that does not reference the engine, +by substitution — channels Claude Code documents for plugin hooks — so **when the value is explicitly +configured** `false` is guard-enforced against every engine invocation, whether or not the clean skill +is active. **Caveat (verified on Claude Code 2.1.218):** that reach exists only for a configured value. +Upstream never implemented the declared userConfig `default`, so while `disk_hygiene_enabled` is unset +its `${user_config.*}` argument is neither substituted nor exported and its presence **drops the whole +engine-gate hook** — on a default install the gate does not register at all, and this surface enforces +nothing (the skill's own kill-switch probe + skill-content value become the only honoring path). Recheck +when the upstream gap closes (#46477 / #39455 / #39827). The gate defers instantly (no output) for any command that does not reference the engine, so it never taxes unrelated work; its coverage marker is the engine script name, a belt against casual invocation, not an authority (renaming the script evades the gate but not the engine's own preview/approval-token containment). The **skill-scoped belt** (the clean skill's frontmatter diff --git a/plugins/disk-hygiene/skills/setup/SKILL.md b/plugins/disk-hygiene/skills/setup/SKILL.md index 0a615e5c13..0b5be46a14 100644 --- a/plugins/disk-hygiene/skills/setup/SKILL.md +++ b/plugins/disk-hygiene/skills/setup/SKILL.md @@ -29,10 +29,12 @@ note that re-enabling restores the FAIL semantics. 1. **Python floor on `PATH`** — the interpreter used by scanning, validation, the guard, and cleanup. (The guard registers on two surfaces: a plugin-level engine gate - that fires in every session but acts only on engine-referencing commands, and the - skill-scoped belt inside the `clean` skill's context. This skill's own probes — and - any direct `hygiene.py` invocation — meet the engine gate; the deny-by-default belt - applies only during `clean`.) The required version has one origin: the `MIN_PYTHON` + that acts only on engine-referencing commands, and the skill-scoped belt inside the + `clean` skill's context. Caveat, verified on Claude Code 2.1.218: the engine gate only + registers once `disk_hygiene_enabled` is explicitly configured — its bare + `${user_config.*}` argument drops the whole hook while the option is unset, so on a + default install direct `hygiene.py` invocations meet NO plugin-level hook; the + deny-by-default belt applies only during `clean`.) The required version has one origin: the `MIN_PYTHON` constant in `${CLAUDE_PLUGIN_ROOT}/skills/clean/scripts/hygiene.py` — parse it from there (`grep -m1 '^MIN_PYTHON' …`) and probe the interpreter against that value; do not recite a version number from this file or the README. FAIL if absent or older, naming From d58fd6046be7196b765c1df9c51473ca79c3f37e Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Thu, 23 Jul 2026 20:05:07 -0400 Subject: [PATCH 4/4] docs(disk-hygiene): condition the trust-surface record on a configured toggle (#1195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review P2: the 0.7.0 trust-surface record still said the plugin-level hook "launches in every consumer session" and charged one python3 launch per Bash/ PowerShell call — contradictory in the exact default configuration this retraction corrects. Conditioned on disk_hygiene_enabled being explicitly configured, with the bare-argv-drop caveat: on a default install the hook does not register or launch, so neither the trust surface nor its per-call cost applies until the option is set. Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/disk-hygiene/README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/disk-hygiene/README.md b/plugins/disk-hygiene/README.md index e909b5b794..96ac7e0567 100644 --- a/plugins/disk-hygiene/README.md +++ b/plugins/disk-hygiene/README.md @@ -164,14 +164,18 @@ hand-cleaning the zone. `${user_config.*}` substitution nor `CLAUDE_PLUGIN_OPTION_*`) and still forces a human prompt before every mutation. - **Trust-surface record (0.7.0):** the plugin-level `hooks/hooks.json` PreToolUse registration is a - NEW trust surface (a hook that launches in every consumer session), added deliberately for - guard-enforced audit-only mode and data-root authority (#1106 decision, Option E — split - registration). Its blast radius is bounded by design: exec form (no shell), bundled - standard-library script only, instant no-output deferral for any command not referencing the - engine, and no new capability beyond what the skill-scoped deployment already did during active - cleanup. Known costs, accepted: one `python3` launch per Bash/PowerShell call, and on a machine - where `python3` resolves to the Windows Store alias stub the launch fails on every call (tracked - with remediation detection in #1110). This entry is the plugin-acceptance review delta for the + NEW trust surface (a hook that launches in every consumer session **once `disk_hygiene_enabled` is + explicitly configured** — see the caveat below), added deliberately for guard-enforced audit-only + mode and data-root authority (#1106 decision, Option E — split registration). Its blast radius is + bounded by design: exec form (no shell), bundled standard-library script only, instant no-output + deferral for any command not referencing the engine, and no new capability beyond what the + skill-scoped deployment already did during active cleanup. Known costs, accepted: one `python3` + launch per Bash/PowerShell call **on a configured install**, and on a machine where `python3` + resolves to the Windows Store alias stub the launch fails on every call (tracked with remediation + detection in #1110). **Caveat (verified on Claude Code 2.1.218):** while `disk_hygiene_enabled` is + unset, the bare `${user_config.*}` argument drops the whole hook, so on a default install this hook + does not register or launch at all — neither the trust surface nor its per-call cost applies until + the option is configured. This entry is the plugin-acceptance review delta for the change. A direct `hygiene.py` invocation outside that skill does not read the toggle and answers only to the engine's own preview/approval-token gate. The toggle can only narrow the destructive surface, never widen it (see [the safety model](skills/clean/reference/safety-model.md)