Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0070

ADR-0070: Hooks live at the user level only and scope themselves to the project in cwd, so they apply to every project

field value
Status Accepted — Accepted (ruling, Mohamed, 2026-10-06: "hooks at user level only but apply to the project in cwd,soo they apply to all projects, add it to the rules")
Date 2026-10-06
Register factory (docs/adr, 4-digit ids)
Source docs/adr/0070-hooks-live-at-user-level-only-and-scope-to-the-project-in-cwd.md — permalink at 9e3535d35 (unchanged on master since that commit)
Wiki register entry ADR-0070 (wiki status: Accepted)

← ADR-0069 · ADR-0071 →

The record

Status: Accepted (ruling, Mohamed, 2026-10-06: "hooks at user level only but apply to the project in cwd,soo they apply to all projects, add it to the rules") · Date: 2026-10-06

Context

Claude Code loads hook registrations from two places: the user level (~/.claude/settings.json, ~/.claude/hooks/, and every installed plugin's hooks/hooks.json) and the project level (<repo>/.claude/settings.json). The project file is loaded only when the session's own project directory IS that repo. Measured 2026-08-23 and recorded in plugins/orchestrata/project-hooks/_dispatch.sh: a session launched from $HOME never read <repo>/.claude/settings.json, so all 25 guards then registered there (AST mandate, no-xdist, no-doubles, banned-deps, the gate battery, design-before-build, every Stop guard) were dark while looking installed. Delivery moved to the orchestrata plugin, which loads in every session; _dispatch.sh scopes each guard to the repo that owns the current working directory and exits in any other tree.

Measured 2026-10-06 in wt-SPEC-110:

home registrations
<repo>/.claude/settings.json (project level) 31 (UserPromptSubmit 1, SessionStart 8, PreToolUse 15, Stop 5, PostToolUse 2)
plugins/orchestrata/hooks/hooks.json (user level, via plugin, commit 83c604886 installed) 42 (SessionStart 11, UserPromptSubmit 11, PreToolUse 13, PostToolUse 3, Stop 2, SessionEnd 1, PermissionRequest 1)
~/.claude/hooks/ + ~/.claude/settings.json (user level) 46 hook files; settings registers Stop 1

Two delivery paths therefore coexist for the same guards. _dispatch.sh lines 134-142 STAND DOWN when CLAUDE_PROJECT_DIR equals the repo and the project file exists, to avoid double firing. The result is the defect the P4 reviewer hit on 2026-10-06: the AST-daemon hook is registered at project level (.claude/settings.json:26) AND shipped in the plugin as a byte-identical copy, and which copy runs depends on where the session was started. A probe that would have exercised the plugin path was refused by a guard carrying the text "all hooks and tools must be at the user level, not project level, but they apply to the project in cwd" — a rule that existed as guard prose and nowhere in the rulebook.

Decision (Mohamed, ruling)

Hooks live at the USER level only: ~/.claude/settings.json, ~/.claude/hooks/, and the installed orchestrata plugin's hooks/hooks.json. Each hook scopes itself to the project that owns the current working directory (the _dispatch.sh pattern: resolve the repo from cwd, exit 0 in any other tree), so the same registration applies to every project a session is opened in. No project registers hooks in its own <repo>/.claude/settings.json.

Alternatives rejected

Consequences

Provenance

Rulings cited

Specs cited