Skip to content

harness: hooks.json fix + git-add guard + sandbox git-config docs (v0.1.2) - #60

Merged
robercano-ghbot merged 5 commits into
mainfrom
fix/bump-plugin-version-for-hooks-fix
Jul 7, 2026
Merged

harness: hooks.json fix + git-add guard + sandbox git-config docs (v0.1.2)#60
robercano-ghbot merged 5 commits into
mainfrom
fix/bump-plugin-version-for-hooks-fix

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

Summary

Ships the plugin hooks.json fix and bumps the orchestrator plugin to 0.1.2, and adds a
harness-level guard plus documentation around a sandbox git-config gotcha discovered along the way.

What's included

1. Plugin hooks fix + version bump (0.1.0 → 0.1.2)

  • Wrap hooks.json events under a top-level "hooks" key so the plugin's hooks actually load.
  • Bump plugin.json + marketplace.json to 0.1.2 so downstream repos pick up the fixes.

2. Harness guard: block blanket git add -A / git commit -a

  • New PreToolUse Bash hook (.claude/scripts/guard-git-add.py) that stops blanket
    git add -A/./--all and git commit -a/-am and points the agent at explicit staging.
  • Under the hardened sandbox, sensitive config paths are bind-mounted to /dev/null (crw- device
    nodes); git can't index a device node, so a blanket add aborts the whole commit. Subagent prompts
    already forbade this, but the top-level agent (governed by user-owned CLAUDE.md) did not — and
    CLAUDE.md is never synced, so a text rule there can't reach consumers. A plugin-owned hook does.
  • The hook fires only when sandbox.enabled is set (a no-op for non-hardened repos). Note it runs
    outside the sandbox, so it can't see the masks directly (os.stat reports them absent) — it keys off
    settings instead. Verified live: blanket adds blocked with guidance; git status / git add -u /
    explicit paths pass through.
  • Rule also added to CLAUDE.md and the setup template so new repos scaffold with it.

3. Docs: the git-config sandbox mask is unfixable in-sandbox

  • docs/HARDENING.md: the strict sandbox masks .git/config.lock as /dev/null and denies
    .git/config + .git/hooks/ writes (a built-in git ACE protection). Verified that
    sandbox.filesystem.allowWrite cannot lift it (mount layer vs permission layer), and
    excludedCommands: ["git"] is rejected as a full-ACE escape. Correct workflow: run git config/hook
    writes in a real terminal. git commit/push/worktree are unaffected.

Testing

  • Guard unit-tested across 9 cases (blanket forms block; --amend, -u, explicit paths, non-git pass).
  • Guard verified live end-to-end in a hardened session after restart.
  • allowWrite non-fix and the git-config mask behavior verified empirically (process-start vs
    settings-mtime confirmed the restart actually took effect).

🤖 Generated with Claude Code

robercano and others added 5 commits July 7, 2026 11:45
… blanket git add

The strict sandbox bind-mounts /dev/null over .git/config.lock and denies
.git/config + .git/hooks writes as a built-in git ACE protection. Verified
2026-07-07 that sandbox.filesystem.allowWrite cannot lift it (mount layer vs
permission layer), and excludedCommands:[git] is rejected as a full-ACE escape
-> the correct workflow is to run git config/hook writes in a real terminal.
Commit/push/worktree are unaffected (they write refs/index/objects, allowed).

The recurring 'agent can't commit' symptom is git add -A / commit -a choking on
masked /dev/null device nodes in the worktree. The subagent prompts already
forbid this; the top-level agent (governed by CLAUDE.md) did not. Add the rule
to both this repo's CLAUDE.md and the setup template so new downstream repos
ship with it.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
Ships the git-add staging rule in the CLAUDE.md setup template so downstream
repos pick it up on the next /orchestrator:setup.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
…en sandbox masks present

CLAUDE.md is user-owned and never synced, so a text rule there can't reach
downstream consumers. Enforce it at the harness level instead: a plugin-owned
PreToolUse Bash hook (scripts/guard-git-add.py) blocks 'git add -A/./--all' and
'git commit -a/-am' and points the agent at explicit staging.

The guard fires only when the worktree actually contains /dev/null device-node
masks, so it is a no-op outside the sandbox and never obstructs consumers who
don't run hardened. Mask detection uses os.stat (S_ISCHR), not 'find -type c':
the bind-mount is invisible to readdir d_type, so find misses it. Ships with the
plugin, so a version bump delivers it to every downstream repo and to the
top-level agent, not just subagents.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
…etection

The PreToolUse hook runs OUTSIDE the sandbox, so os.stat cannot see the
/dev/null device-node masks (they exist only inside the per-command bwrap
namespace) — the original mask-detection gate never fired. Detect the cause
instead: read sandbox.enabled from settings.local.json/settings.json/user
settings. Verified live: blanket 'git add -A' is now blocked with guidance,
while git status / git add -u / explicit paths pass through untouched.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
@robercano-ghbot
robercano-ghbot merged commit a26be71 into main Jul 7, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants