D1198 — A derived binding registers Claude Code hooks in settings.local.json¶
Decision. A derived workspace binding (D1019) registers its Claude Code hooks in
.claude/settings.local.json, the settings layer Claude Code reads beside .claude/settings.json
and that a repository does not version, and excludes it from git like CLAUDE.local.md. It never
writes .claude/settings.json. When the repository tracks settings.local.json too, the hooks are
not projected (D1181's rule for a tracked registration file). Wherever a Claude Code settings file
is still rewritten, its key order, indent and final newline are kept.
Why. A derived binding writes into repositories with no command run there, so it may not edit a
tracked file (D1019). A team's tracked settings.json showed up modified in git status with the
KB's hooks in it, its keys reordered and its final newline gone. Registering the hooks in an
untracked file keeps them working, the same answer CLAUDE.local.md gives for the instructions.
Alternatives rejected. Skipping hooks whenever settings.json is tracked: most Claude Code
repositories track it, so derived bindings would lose their hooks almost everywhere. Moving the
registration only when settings.json is tracked, like CLAUDE.local.md: a repository that starts
tracking the file later would commit Cartographer's entries with it. Stripping the entries an
earlier version left in a tracked settings.json during sync: that edits a tracked file too.
doctor reports them and the user removes them.
Consequences. An entry an earlier version left in a settings.json git does not track is
Cartographer's to move: the derived projection strips it there and registers in
settings.local.json. A hook still registered in a tracked settings.json is not registered again
(it would fire twice): sync warns and doctor reports it. Once the entry is removed, the next
sync registers the hook in settings.local.json, because Apply rewrites a derived Claude Code hook
that is not registered there. A prune that does not know which file holds a registration
(disconnect, an orphan projection) strips the hook from settings.json and from a
settings.local.json git does not track, and only rewrites a file that names it. The registration
readers (doctor, on-disk verification) read both files. The ordered decoder runs only on what
json.Valid accepts, so a repository file cannot nest past its depth limit.