Restricted, and still all 34 hooks fired
CC 2.1.248 added --restricted: no Bash, no WebFetch, no user or project settings, file tools fenced to the working directory. The open question for a plugin with 154 hook entries was which of them break in that mode. Measured on 2.1.251: none. This page shows the lane that keeps it that way on every PR, at zero model spend.
1. What the process was offered, by mode
| tools in the request CC sent | |
|---|---|
| Bash / WebFetch present | |
| distinct plugin hooks that fired | |
| hooks reporting ok: false | |
| model spend | $0 (local stub of the Messages API answered all 7 requests) |
2. The hooks that fired under --restricted
One-turn session ("reply ok"). PreToolUse and PostToolUse hooks are absent because no tool ran, which is correct for a one-turn prompt; Stop and SessionEnd hooks are included because the runner waits three seconds for them to flush.
3. How the lane asserts it
| # | Assertion | What proves it | Negative control |
|---|---|---|---|
| 1 | the run completed and answered | result: ok, is_error: false | stub down: run fails |
| 2 | the process really was restricted | the stub logged the tool list; no Bash, no WebFetch | same run without the flag: FAIL, 38 tools including Bash and WebFetch |
| 3 | plugin hooks fired | hook-timing.jsonl in the run's private HOME has entries | plugin not loaded: file absent, FAIL |
| 4 | the failing set is the accepted set | hooks with ok:false equal expected-hook-failures.txt (empty) | list edited to name one hook: FAIL, [] != ["lifecycle/nonexistent-hook"] |
The runner gives claude a fresh HOME, so the hook log it reads can only have been written by this run (twelve other sessions were live on the machine that produced these numbers). The stub is 60 lines of node: every POST /v1/messages gets an SSE stream that says "ok"; everything else is logged and 404s. The policy that real model calls never run in CI holds.