Defect
adopt's .claude/settings.json merge grant delegates to wiki/agents/claude-code/setup.sh --hook, which does two things:
- installs hook FILES under
.claude/hooks/ (ensure-wiki.py, session-start.sh);
- registers hook COMMANDS referencing those paths in
.claude/settings.json.
Whether each half is committed is decided by the HOST's .gitignore — which adopt, by design, only appends to (append-only grant) and never reads. When the host's rules ignore .claude/hooks/ but track settings.json, the registrations get committed while the files stay machine-local, and every other clone gets a settings.json referencing hooks that do not exist. Nothing warns; each grant behaves correctly in isolation and the interaction is validated nowhere.
Observed (crcresearch/paper-atomizer)
Host-authored ignore-then-whitelist stanza:
.claude/*
!.claude/commands/ # …wiki-*.md whitelisted
!.claude/skills/ # …wiki-*.md whitelisted
!.claude/settings.json # ← tracked
# ← .claude/hooks/ NOT whitelisted
Committed settings.json registers two SessionStart hooks under .claude/hooks/; the directory is absent from the repo. Consequence per fresh clone: the ensure-wiki.py line fails soft (guarded with command -v python3 … || true), but .claude/hooks/session-start.sh is registered unguarded — Claude Code reports a hook error at every session start and the wiki auto-pull never runs on collaborators' machines.
For contrast, the template's intended default is committed hooks: the shipped .gitignore carries .claude/hooks/ deliberately commented out ("uncomment if your team agrees not to share them"), and instantiated projects (e.g. sci-doc-understanding) track .claude/hooks/ alongside settings.json.
Proposed fix (detection, not modification)
Host .gitignore content stays sacred — no new writes. After setup.sh --hook installs and registers:
git check-ignore -q .claude/hooks/session-start.sh # hooks ignored on this host?
git ls-files --error-unmatch .claude/settings.json # settings tracked?
If ignored + tracked: print a warning naming the consequence ("collaborators' clones will reference missing hooks; either whitelist .claude/hooks/ or have each user run setup.sh --hook") and record it via the lw_record_* report — and, when reached through adopt, in .llm-wiki-adopt-log.md.
Optional hardening to discuss: register session-start.sh behind an existence guard the way ensure-wiki.py already is ([ -x .claude/hooks/session-start.sh ] && … || true). Trade-off: silences the dangling-reference error for collaborators, which also removes the signal that setup was never run — the warning above is the primary fix either way.
Acceptance
- Fixture: a host whose
.gitignore ignores .claude/hooks/ (tracked settings.json) → adopt/setup prints the warning and the adopt log records it; a host with the template-default gitignore does not warn. Red-first against current code (today both paths are silent).
- Real-world reference: the paper-atomizer state above, reported by a user hitting the session-start hook error.
Defect
adopt's
.claude/settings.jsonmerge grant delegates towiki/agents/claude-code/setup.sh --hook, which does two things:.claude/hooks/(ensure-wiki.py,session-start.sh);.claude/settings.json.Whether each half is committed is decided by the HOST's
.gitignore— which adopt, by design, only appends to (append-onlygrant) and never reads. When the host's rules ignore.claude/hooks/but tracksettings.json, the registrations get committed while the files stay machine-local, and every other clone gets a settings.json referencing hooks that do not exist. Nothing warns; each grant behaves correctly in isolation and the interaction is validated nowhere.Observed (crcresearch/paper-atomizer)
Host-authored ignore-then-whitelist stanza:
Committed
settings.jsonregisters two SessionStart hooks under.claude/hooks/; the directory is absent from the repo. Consequence per fresh clone: theensure-wiki.pyline fails soft (guarded withcommand -v python3 … || true), but.claude/hooks/session-start.shis registered unguarded — Claude Code reports a hook error at every session start and the wiki auto-pull never runs on collaborators' machines.For contrast, the template's intended default is committed hooks: the shipped
.gitignorecarries.claude/hooks/deliberately commented out ("uncomment if your team agrees not to share them"), and instantiated projects (e.g. sci-doc-understanding) track.claude/hooks/alongsidesettings.json.Proposed fix (detection, not modification)
Host
.gitignorecontent stays sacred — no new writes. Aftersetup.sh --hookinstalls and registers:If ignored + tracked: print a warning naming the consequence ("collaborators' clones will reference missing hooks; either whitelist .claude/hooks/ or have each user run setup.sh --hook") and record it via the
lw_record_*report — and, when reached through adopt, in.llm-wiki-adopt-log.md.Optional hardening to discuss: register
session-start.shbehind an existence guard the wayensure-wiki.pyalready is ([ -x .claude/hooks/session-start.sh ] && … || true). Trade-off: silences the dangling-reference error for collaborators, which also removes the signal that setup was never run — the warning above is the primary fix either way.Acceptance
.gitignoreignores.claude/hooks/(trackedsettings.json) → adopt/setup prints the warning and the adopt log records it; a host with the template-default gitignore does not warn. Red-first against current code (today both paths are silent).