Skip to content

test(self): gate hooks parity between settings.json and hooks.json (#140) - #196

Merged
robercano-ghbot merged 1 commit into
mainfrom
feat/issue-140-hooks-parity
Jul 29, 2026
Merged

test(self): gate hooks parity between settings.json and hooks.json (#140)#196
robercano-ghbot merged 1 commit into
mainfrom
feat/issue-140-hooks-parity

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

Closes #140.

Problem

Three hooks are hand-duplicated between reCode's repo-local .claude/settings.json (self-hosted sessions, $CLAUDE_PROJECT_DIR paths) and the shipped .claude/hooks/hooks.json (consumers, ${CLAUDE_PLUGIN_ROOT} paths). Nothing checked they stayed in sync — which is how a hook (the tool-mirror hook, #137) ended up reCode-only for weeks with an empty cockpit workers panel in every consumer.

Change

  • Add do_hooks_parity to .claude/self/checks.sh, wired into the build phase (and therefore test). It parses .hooks from both files, normalizes each hook into an (event, matcher, normalized-command) triple — stripping the two environment-specific path prefixes (quoted "$CLAUDE_PROJECT_DIR/.claude/ and unquoted ${CLAUDE_PLUGIN_ROOT}/) plus quotes so equal hooks compare equal — and diffs both directions. Settings-only triples must appear in an explicit documented ALLOWLIST; consumer-only triples always fail.
  • The allowlist has one entry: PostToolUse / Bash|Edit|Write / log-worker-tool.sh, commented as deliberately self-only (worker tool-mirror logging feeds a cockpit worker-session panel that only exists in this self-hosted repo). The gate is born green.
  • Add .claude/scripts/hooks-parity.test.sh (auto-discovered by do_test): hermetic/offline, uses temp copies via HOOKS_SETTINGS_FILE/HOOKS_PLUGIN_FILE env overrides. Proves the born-green path, both fail-on-divergence directions, and a synthetic matching pair.

Gates

GATES_FILE=.claude/self/gates.json build / lint / test all green, including hooks-parity.test.sh: PASS (4 checks).

Review

correctness (opus) + tests (sonnet) lenses — both APPROVE, consensus=all. Only non-blocking nits (narrow normalization regexes; allowlist covered indirectly by the born-green assertion).

🤖 Generated with Claude Code

…#140)

Three hooks are hand-duplicated between the repo-local .claude/settings.json
(self-hosted, $CLAUDE_PROJECT_DIR paths) and the shipped
.claude/hooks/hooks.json (consumers, ${CLAUDE_PLUGIN_ROOT} paths), with
nothing to catch drift — which is how a hook ended up reCode-only for weeks.

Add do_hooks_parity to .claude/self/checks.sh, wired into do_build (and
therefore gate.sh build/test): it normalizes each file's .hooks into
(event, matcher, command) triples, stripping the environment-specific path
prefix so both forms collapse to the same value, diffs both directions, and
fails on any divergence not covered by an explicit ALLOWLIST. The allowlist
currently covers log-worker-tool.sh's PostToolUse hook, which is deliberately
self-only (it mirrors worker tool calls into reCode's own cockpit
worker-session panel; consumers have no such panel to feed).

Add .claude/scripts/hooks-parity.test.sh (auto-discovered by do_test):
asserts the check passes born-green on the real repo files, and fails on
synthetic divergence in both directions, via the new HOOKS_SETTINGS_FILE /
HOOKS_PLUGIN_FILE env-var override seam.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@robercano-ghbot robercano-ghbot added the needs-human Loop escalated: attempt budget exhausted, manual triage required label Jul 28, 2026
@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

test(self): gate hooks parity between settings.json and hooks.json (#140) (not yet reviewed)

@robercano

Copy link
Copy Markdown
Owner

Is it necessary to maintain this copy of hooks? Can you inform me of a better way of keeping the hooks the same (same location) instead?

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

Re: is the duplicated hooks copy necessary? (answered by Claude on the owner's behalf)

Yes, two files are structurally required today — they feed two different Claude Code loaders, and neither can read the other's file:

  • .claude/settings.json → hooks for sessions in this repo (self-hosting), resolving scripts via $CLAUDE_PROJECT_DIR so a session runs the working tree's scripts (the branch being developed).
  • .claude/hooks/hooks.json → the shipped plugin artifact; Claude Code only loads a plugin's hooks/hooks.json from the installed plugin cache, resolving via ${CLAUDE_PLUGIN_ROOT} (only defined when running as an installed plugin).

There is no include/reference mechanism between them, and installing reCode as a plugin into itself would make self-sessions run the cached snapshot instead of the working tree — the exact stale-scripts drift this repo has already been bitten by downstream.

But the hand-maintenance isn't necessary: the right follow-up is to make hooks/hooks.json the single source of truth and generate settings.json's hooks block from it (rewrite ${CLAUDE_PLUGIN_ROOT}/scripts/X"$CLAUDE_PROJECT_DIR"/.claude/scripts/X, merge the documented self-only extras like log-worker-tool.sh, surgical JSON merge of only the hooks key). This PR's gate then verifies the generated output is current — same check, stronger guarantee, nothing here is throwaway.

Filed as a follow-up issue; recommendation is to merge this PR as-is.

@robercano-ghbot robercano-ghbot removed the needs-human Loop escalated: attempt budget exhausted, manual triage required label Jul 29, 2026
@robercano-ghbot
robercano-ghbot merged commit 015a3f2 into main Jul 29, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-140-hooks-parity branch July 29, 2026 06:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Self-gate: assert hooks parity between .claude/settings.json and hooks/hooks.json

2 participants