Skip to content

disk-hygiene: destructive-operation guard silently unenforced for the entire measured window #1346

Description

@kyle-sexton

Summary

disk-hygiene's destructive-operation guard has been silently unenforced for the entire measured
window
. Both registered variants fail before any guard logic runs, and both fail non-blockingly
so the tool call proceeds and nothing surfaces to the operator.

This was found incidentally while diagnosing guardrails PreToolUse latency: the disk-hygiene guard
was being used as the fast "control" in that comparison, and it turned out never to have executed.

Evidence

Measured from hook attachments in ~/.claude/projects/*/*.jsonl.

Variant 1 — destructive_guard.py --mode engine-gate. All 16 recorded runs are
hook_non_blocking_error with exitCode: 1:

Failed to run: Plugin option "disk_hygiene_enabled" isn't set. Open /plugin manage to configure it, or check that the plugin's userConfig schema declares "disk_hygiene_enabled".

The recorded durationMs is 2 ms — that is Claude Code refusing to launch the hook during config
validation, before any process is spawned. python3 -c 'pass' costs ~396 ms on this host, so 2 ms
was never physically achievable for a real run.

An earlier variant of the same registration shows a second, distinct registration bug:

Failed to run: Hook command references ${CLAUDE_PLUGIN_DATA} but only ${CLAUDE_PLUGIN_ROOT} is available for skill hooks (${CLAUDE_PLUGIN_DATA} is plugin-only).

Variant 2 — destructive-guard.sh. All 57 recorded runs are hook_non_blocking_error with
exitCode: 1:

Failed with non-blocking status code: <3>WSL (2444932 - Relay) ERROR: CreateProcessCommon:818: execvpe(/bin/bash) failed: No such file or directory

The command resolved to the Windows WSL bash.exe relay, which has no /bin/bash. Its 878 ms is the
cost of failing, not of guarding.

Impact

  • The destructive-operation guard provided zero protection over the whole measured window.
  • Both failure modes are non-blocking, so there is no operator-visible signal — the guard's absence
    is indistinguishable from the guard allowing the operation.
  • Invoked directly, bypassing the broken registration, the guard does run and is genuinely fast:
    destructive_guard.py --mode engine-gate against a real payload measured 181 ms, 1 process, 0
    external spawns
    . The logic is fine; only the registration is broken.

Sequencing note

The registration carries a Bash|PowerShell matcher, so repairing it will add this guard to every
Bash tool call. On Windows that adds load to an already-contended process-spawn path. Land the
guardrails spawn-count reduction (see the linked issue) before or alongside the repair.

Not fixed here

Deliberately out of scope for the guardrails latency work — filed so it is not lost. Needs an owner
for the disk_hygiene_enabled userConfig declaration and the bash-resolution path.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions