Skip to content

Honor chamber.config default_base + hooks for EnterWorktree-created worktrees#2

Draft
echochamber wants to merge 1 commit into
mainfrom
fix/enterworktree-honor-chamber-config
Draft

Honor chamber.config default_base + hooks for EnterWorktree-created worktrees#2
echochamber wants to merge 1 commit into
mainfrom
fix/enterworktree-honor-chamber-config

Conversation

@echochamber

Copy link
Copy Markdown
Owner

Summary

Fixes #1. Claude Code's built-in EnterWorktree tool (which ~/.claude/CLAUDE.md tells agents to prefer) creates worktrees from its own worktree.baseRef (default fresh = origin/<default-branch> = main) and ignores .claude/chamber.config — so it skips the per-repo default_base, post-create hooks, and symlink_files. In manabot-alpha (default_base: origin/dev, PRs target dev), every EnterWorktree branch was based on main, polluting PRs with ~25 dev→main promotion merges, and deps were never installed.

Root cause confirmed empirically: an existing EnterWorktree worktree (worktree-worker-egress-step1-vpc) was based exactly on origin/main's tip, 25 commits ahead of origin/dev.

Fix

New PostToolUse hook on EnterWorktree: wtutils/enterworktree-reconcile.sh. After the tool creates the worktree it:

  1. Re-bases the new branch onto chamber.config default_base (e.g. origin/dev) — via git reset --hardonly for a freshly-created empty worktree.
  2. Runs the chamber post-create hooks + applies symlink_files/symlink_dirs/beads redirect, reusing wtutils.sh's wt--setup-worktree (no config-parsing duplication).

EnterWorktree's baseRef can't target an arbitrary branch, so this is a post-create reconciliation, exactly as the issue suggested.

Safety (runs on every EnterWorktree create)

The rebase is gated hard — it proceeds only when all hold:

  • chamber.config declares a default_base
  • the worktree tree is clean (no staged/unstaged/untracked)
  • HEAD has no commits of its own beyond the base it was cut from (HEAD reachable from origin/<default-branch>)
  • default_base resolves to a real commit differing from HEAD

If any gate fails: no history touched — setup hooks still run (idempotent), and a skip reason is logged to ~/.claude/hooks/enterworktree-reconcile.log. The hook never blocks Claude (all paths exit 0). The wt-add shell path is untouched.

How it was tested

/tmp/claude-safe/test-reconcile.sh builds throwaway repos (main ahead of dev by promotion merges, mimicking manabot-alpha) and pipes a PostToolUse payload into the hook. 13 assertions across 6 scenarios, all pass:

  • (a) empty main-based worktree → rebased to origin/dev, 0 commits ahead of dev (clean PR)
  • (b) post-create hooks ran + symlink_files applied
  • (c) dirty worktree → HEAD untouched, refusal logged
  • (d) worktree with a committed change → HEAD untouched, work commit preserved
  • (e) no default_base → no rebase, setup still runs
  • idempotent when already on default_base; stable on re-run

shellcheck clean on the hook and install.sh.

Deploy / sync

The repo does not edit the user's settings.json. To deploy:

  1. cd ~/Code/chaimber-cliutils && ./install.sh — symlinks the hook into ~/.claude/hooks/ and prints the registration snippet.
  2. Register it in ~/.claude/settings.json under .hooks.PostToolUse. There is already an EnterWorktree|ExitWorktree matcher object running worktree-cwd-notify.sh; the simplest path is to append to that object's hooks array:
{ "type": "command", "command": "bash ~/.claude/hooks/enterworktree-reconcile.sh" }

(or add a standalone { "matcher": "EnterWorktree", "hooks": [...] } object — order doesn't matter).

Open questions / notes

  • I did not modify live ~/.claude/ files — review then run install.sh + the settings.json edit yourself.
  • Stale, unrelated finding: ~/.claude/hooks/worktree-create.sh / worktree-remove.sh reference wt--is-excluded + ~/.claude/chamber.user.config, which no longer exist in wtutils.sh. Those hooks are also not wired into settings.json and only run outside a git repo anyway, so they're effectively dead for git repos. Out of scope here; flagging for cleanup.

🤖 Generated with Claude Code

Claude Code's built-in EnterWorktree tool creates worktrees off its own
worktree.baseRef (default `fresh` = origin/<default-branch> = main) and
ignores the per-repo .claude/chamber.config default_base, post-create
hooks, and symlink_files. In repos whose integration branch is not the
default branch (e.g. manabot-alpha: default_base=origin/dev), every
EnterWorktree branch was based on main — polluting PRs with ~25 dev->main
promotion merges — and deps were never installed.

Add wtutils/enterworktree-reconcile.sh, a PostToolUse hook on EnterWorktree
that, for a freshly-created EMPTY worktree only (clean tree + no commits of
its own beyond the base it was cut from), re-points the branch onto
chamber.config default_base via `git reset --hard`, then runs the chamber
post-create hooks + symlinks by reusing wtutils.sh's wt--setup-worktree.

Safety: the rebase is gated hard. If the tree is dirty or HEAD carries any
unique commits, the hook refuses to touch history and logs a skip reason —
it can never clobber work. It also no-ops cleanly when default_base is
absent (setup still runs), when already on default_base, and on re-run.
All paths exit 0; the hook never blocks Claude. Logs to
~/.claude/hooks/enterworktree-reconcile.log.

install.sh now symlinks the hook into ~/.claude/hooks/ and prints the
settings.json PostToolUse registration snippet (the repo does not edit the
user's settings.json). The wt-add shell path is unchanged.

Refs #1

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

EnterWorktree ignores chamber.config default_base (branches off main, not dev)

1 participant