Summary
At project scope, hook declarations in .factory/hooks.json are silently never read. The documentation lists this as the primary project-scope hook location, but a PreToolUse hook registered there never fires. The same declaration works only when moved into the hooks key of .factory/settings.json. There is no warning or error in either case — the run proceeds unguarded at exit 0.
This is a security-relevant failure: a hook is a policy control (test-lock, path guard, etc.). An operator following the docs will believe a control is installed when it is not.
Environment
droid 0.186.0, macOS (darwin 24.6.0), droid exec mode
Reproduction
Register an identical matcher: "*" canary PreToolUse hook (writes one line per invocation) in each location, run one trivial tool call (droid exec 'run: ls'), count invocations:
| Config location |
Canary invocations |
.factory/hooks.json (project, documented primary) |
0 |
~/.factory/hooks.json (user scope) |
0 |
.factory/hooks/hooks.json (documented legacy) |
0 |
.factory/settings.json, hooks key |
1 |
All four tested with valid JSON (python3 -m json.tool exit 0), absolute script paths, and the documented PreToolUse event.
Expected
.factory/hooks.json is read and its hooks fire — or, at minimum, an unread/misconfigured hook file produces a diagnostic rather than silently doing nothing.
Actual
Only .factory/settings.json (hooks key) fires. The documented primary is silently ignored. The failure is indistinguishable from success at the exit code.
Note
Plugin-shipped hooks (hooks/hooks.json inside a plugin) DO fire — so the filename works in one context and is silently ignored in another, which compounds the confusion.
Summary
At project scope, hook declarations in
.factory/hooks.jsonare silently never read. The documentation lists this as the primary project-scope hook location, but aPreToolUsehook registered there never fires. The same declaration works only when moved into thehookskey of.factory/settings.json. There is no warning or error in either case — the run proceeds unguarded atexit 0.This is a security-relevant failure: a hook is a policy control (test-lock, path guard, etc.). An operator following the docs will believe a control is installed when it is not.
Environment
droid0.186.0, macOS (darwin 24.6.0),droid execmodeReproduction
Register an identical
matcher: "*"canaryPreToolUsehook (writes one line per invocation) in each location, run one trivial tool call (droid exec 'run: ls'), count invocations:.factory/hooks.json(project, documented primary)~/.factory/hooks.json(user scope).factory/hooks/hooks.json(documented legacy).factory/settings.json,hookskeyAll four tested with valid JSON (
python3 -m json.toolexit 0), absolute script paths, and the documentedPreToolUseevent.Expected
.factory/hooks.jsonis read and its hooks fire — or, at minimum, an unread/misconfigured hook file produces a diagnostic rather than silently doing nothing.Actual
Only
.factory/settings.json(hookskey) fires. The documented primary is silently ignored. The failure is indistinguishable from success at the exit code.Note
Plugin-shipped hooks (
hooks/hooks.jsoninside a plugin) DO fire — so the filename works in one context and is silently ignored in another, which compounds the confusion.