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
{{ message }}
Repository navigation
Commit 70984e4
Browse filesBrowse the repository at this point in the historyBrowse files
# Planning time: ~0s (Step 0 detects the template and skips Step 1)
322
345
```
323
346
324
347
See `orchestration_template.example.json` for the full field reference.
325
348
326
349
---
327
350
328
-
## Hook System
329
-
330
-
> Interactive version of this section + the pipeline diagram above: [Pipeline & Hook Architecture](https://claude.ai/code/artifact/6045b61f-00e5-411d-a2a6-d62b267ba29c)
351
+
## Hooks — what happened to them
331
352
332
-
Claude Code exposes 9 hook events. This project registers **4 of them** in `~/.claude/settings.json`. The table below was produced by tracing every registered entry point down to which internal policy modules actually execute, not by reading the prose docs — `docs/policies/hook-system-policy.md` has drifted from the real wiring (see Findings below).
353
+
**v2.0.0 deleted the enforcement hooks.**`PreToolUse`, `PostToolUse` and
354
+
`UserPromptSubmit` are no longer registered in the user-scope `settings.json`; the
355
+
`hooks` object holds exactly `Stop` and `Notification`, and those two were proved
356
+
unchanged by comparing canonical-JSON digests rather than merely checking they were
357
+
still present.
333
358
334
-
### Registration Status
359
+
If you are looking for the previous version of this section — a nine-row registration
360
+
table, a per-policy breakdown and four findings about dead abstractions — it described
361
+
that deleted architecture and has been removed rather than corrected. `git log` has it.
|`UserPromptSubmit` started the pipeline on every prompt | Nothing starts it implicitly. A caller declares one of six commands (see [Run](#run)) or it refuses |
368
+
|`PreToolUse` blocked unsafe pushes | An MCP-side push gate, plus ADR-020's three-layer control: prevention on the path the plugin owns, detection on the manual-edit path that has no interception point |
369
+
|`PostToolUse` tracked progress | MCP tools, called explicitly |
374
370
375
-
`pre_tool_enforcer/core.py` loads all 13 files under `hooks/pre_tool_enforcer/policies/`:
371
+
The ordering mattered: the replacement push gate was registered and proved reachable by
372
+
completing a real `tools/call`**before**`PreToolUse` was deleted. Removing the hook
373
+
while its replacement existed only in the repository would have left a machine with no
|`write_edit.py`|**Loaded, never called** — see Findings #2|
376
+
### The code is still here, and is not wired
382
377
383
-
### PostToolUse — full policy breakdown
378
+
`hooks/` still contains `pre_tool_enforcer/`, `post_tool_tracker/` and `stop_notifier/`.
379
+
None of it is registered. Treat it as reference material, not as behaviour you are
380
+
getting — nothing in the plugin or in `scripts/3-level-flow.py` reaches it.
384
381
385
-
All 6 files under `hooks/post_tool_tracker/policies/` are loaded and invoked: `uncommitted_push.py`, `post_merge_update.py`, `task_tracking.py`, `phase_complexity.py`, `task_breakdown_clear.py`, `skill_selection_clear.py`.
382
+
### The plugin ships zero hooks, deliberately
386
383
387
-
### Findings
384
+
Plugin hooks merge into a flat, session-wide pipeline with no per-plugin label, so a
385
+
user cannot disable one plugin's hook without disabling everyone's. Shipping one would
386
+
give you less control than you have now. This is ADR-010, and a CI job
387
+
(`Plugin conformance (ADR-010 / ADR-019 CRITICAL)`) fails the build if a `hooks/`
388
+
directory or a `hooks.json` ever appears under the plugin root.
388
389
389
-
1.**`PolicyRegistry` is a dead abstraction.**`hooks/pre_tool_enforcer/registry.py` defines a `PolicyRegistry` class meant to register and run policies generically. `core.py:182` imports it, but nothing in the repo ever instantiates it — `core.py` hand-builds its own `_BLOCKING_POLICIES` list instead. Either wire real policies through it or delete it.
390
-
2.**`write_edit.py` never executes.** Loaded and wrapped at `core.py:216` / `367-369`, but no call site in `main()` reaches it — and its own body is a stub that always returns `False`. A second, non-stub-looking copy also sits unused directly in the `pre-tool-enforcer.py` shim (lines 204-222), likewise never called. Two dead implementations of the same check.
391
-
3.**No dedicated `SessionEnd` hook.**`stop_notifier/core.py` runs session archiving, pruning, and preference tracking on every `Stop` event — work a real `SessionEnd` hook would own instead. Since `Stop` fires on every response (not just session close), this logic runs far more often than necessary.
392
-
4.**`docs/policies/hook-system-policy.md` no longer matches the implementation.** It describes a `script-chain-executor.py` dispatcher and a `hook-downloader.py` sync-from-repo model — neither file exists in the repo anymore. Its example `settings.json` snippets point at `~/.claude/scripts/...py`, while the real registration points directly into this repo's `hooks/...py`. Its policy counts ("8" PreToolUse checks, "6" PostToolUse functions) use a different taxonomy than the actual files and only coincidentally match on PostToolUse.
390
+
The consequence is worth stating plainly: **nothing is enforced in a session where you
391
+
do not invoke a command.** That is the intended trade-off of the hook-free design, not
392
+
a regression.
393
393
394
394
---
395
395
@@ -538,7 +538,7 @@ All options are set via environment variables. Copy `.env.example` and fill in:
0 commit comments