From a6e5570766c1937a4c883d8cda159fd2d8b4b730 Mon Sep 17 00:00:00 2001 From: dietrich ayala Date: Sun, 09 Aug 2026 18:14:05 +0000 Subject: [PATCH] link chook's config where chook actually looks for it chook.mjs resolves its rules to ~/.config/chook.toml and nowhere else — its DEFAULT_CONFIG constant — while server/lib/22-claude-config.sh renders and delivers the file as part of the ~/.claude payload. On the live box the two never met: ~/.claude/chook.toml existed, ~/.config/chook.toml did not, and chook had been running there with no rules at all. The failure mode is the one worth naming. The hooks stay wired in settings.json and still run on every tool call, so a guard that loads nothing returns nothing and reads exactly like a guard that passed. Nothing about the box looks wrong; the metachar guard and every other rule are simply absent. A symlink rather than a copy, so the delivered file stays the single source and a later delivery cannot leave the two disagreeing. A real file already sitting at that path defers rather than being clobbered, as everything else here does. verify.sh asserts the link resolves to the delivered file, because a silent absence is precisely what drift detection is for. --- verify.sh | 9 +++++++++ server/lib/22-claude-config.sh | 20 ++++++++++++++++++++ 2 file(s) changed, 29 insertion(s)(+), 0 deletion(s)(-) diff --git a/verify.sh b/verify.sh --- a/verify.sh +++ b/verify.sh @@ -98,6 +98,15 @@ check_true "present: $c" command -v "$c" done +echo "=== claude config ===" +# chook.mjs resolves its rules to ~/.config/chook.toml and nowhere else, while +# the file is delivered as part of the ~/.claude payload. When the link is +# missing chook loads no rules, and the failure is silent in the way that costs +# most: the hooks stay wired in settings.json and still run on every tool call, +# so the guards return nothing and read as passing rather than as absent. +check "chook config resolves to the delivered file" "$HOME/.claude/chook.toml" \ + readlink -f "$HOME/.config/chook.toml" + echo "=== locale (§4.3) ===" check_true "UTF-8 locale active" bash -c '[[ "$(locale charmap)" == "UTF-8" ]]' diff --git a/server/lib/22-claude-config.sh b/server/lib/22-claude-config.sh --- a/server/lib/22-claude-config.sh +++ b/server/lib/22-claude-config.sh @@ -134,6 +134,26 @@ "existing chook.toml differs from the rendered delivery; chook.toml has no box-local override file (unlike settings.json/settings.local.json), so reconcile by hand before re-running" \ text +# chook.mjs resolves its config to ~/.config/chook.toml and nowhere else — see +# its DEFAULT_CONFIG constant — while the file is delivered here as part of the +# ~/.claude payload. Without this link chook loads no rules at all, which fails +# in the worst possible way: the hooks stay wired in settings.json and every +# tool call still runs them, so the guards report nothing and read as passing +# rather than as absent. A link rather than a copy, so the delivered file stays +# the single source and a later delivery cannot leave the two disagreeing. +CHOOK_LINK="$H/.config/chook.toml" +if [[ -L "$CHOOK_LINK" && "$(readlink "$CHOOK_LINK")" == "$CHOOK_DST" ]]; then + info "chook config link already points at $CHOOK_DST" +elif [[ -e "$CHOOK_LINK" && ! -L "$CHOOK_LINK" ]]; then + defer "$CHOOK_LINK" \ + "a real file sits where chook's config link belongs; chook reads only this path, so move it aside and link it to $CHOOK_DST" \ + "ln -sfn '$CHOOK_DST' '$CHOOK_LINK'" +else + install -d -m 0755 "$H/.config" || die "could not create $H/.config" + ln -sfn "$CHOOK_DST" "$CHOOK_LINK" || die "could not link $CHOOK_LINK" + info "linked $CHOOK_LINK -> $CHOOK_DST" +fi + # ---- settings.json ----------------------------------------------------------- SETTINGS_SRC="$SRC/settings.json" SETTINGS_DST="$DST/settings.json" -- tangled.sh