fix: repair broken WorktreeCreate hook and add PR approval policy#441
Conversation
The .claude/settings.json WorktreeCreate hook only ran `bun install`, which is not enough to satisfy the harness's hook contract: once a WorktreeCreate hook is configured, the harness defers the entire worktree creation to it and expects only the resulting absolute path on stdout. The old hook never created a worktree or emitted a path, so EnterWorktree failed with errors like "path contains control characters" or an ENOENT chdir into stray bun install output. 21 stale empty directories under .claude/worktrees/ plus orphaned claude/* branches were leftover evidence of this. Fixed the hook to parse the requested name from stdin JSON, run `git worktree add`, install deps silently, and echo only the resulting path. Verified end-to-end via EnterWorktree/ExitWorktree. Documented the hook contract in CLAUDE.md so it doesn't regress. Also adds APPROVAL_POLICY.md, read by the repo's "Archgate CLI Approver" Cursor Automation. Per Cursor's Bugbot docs, a `success` check conclusion already means "no issues found, no unresolved comments" -- Bugbot does not always leave a comment. The automation's prior fallback logic treated a clean pass with no comment as an unverified signal and withheld approval (see PR #439). The policy clarifies that the check's own conclusion is sufficient on its own. Session findings captured in agent memory for future reference. Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
|
Warning Review limit reached
Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying archgate-cli with
|
| Latest commit: |
da69664
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b29a3177.archgate-cli.pages.dev |
| Branch Preview URL: | https://fix-worktree-hook-and-approv.archgate-cli.pages.dev |
There was a problem hiding this comment.
Stale comment
Not approving: Cursor Bugbot completed with a skipping status and reported 2 unresolved medium-severity issues in the WorktreeCreate hook (.claude/settings.json). Human review is needed to address the stale-directory and Windows CRLF findings before merge; no reviewers were assigned.
Sent by Cursor Approval Agent: Archgate CLI Approver
Code Coverage
Full HTML report available in workflow artifacts. Per-directory breakdown
|
Two issues flagged by Cursor Bugbot on this PR, both reproduced before fixing: - The hook only ran `git worktree add` when the target path was missing, so a pre-existing stale/empty directory under .claude/worktrees/ (exactly the kind of leftover this PR documents finding 21 of) would be skipped, cd'd into, and still reported as success with no real checkout. Now removes non-worktree leftovers (no .git) before the create-if-missing check. - `jq -r` can emit CRLF on Windows Git Bash; command substitution only strips the trailing \n, leaving a stray \r in the parsed name and therefore the final stdout path -- reproducing the exact "path contains control characters" failure this PR set out to fix. Now strips \r from the parsed name. Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
There was a problem hiding this comment.
Stale comment
Not approving: Cursor Bugbot completed with a skipping status and reported one unresolved medium-severity issue (silent
bun installfailure in the WorktreeCreate hook). Human review is needed before merge; recommend @rhuanbarreto address or resolve the Bugbot finding.Sent by Cursor Approval Agent: Archgate CLI Approver
…wallowing them Cursor Bugbot flagged that a failed `bun install --silent` in the WorktreeCreate hook went unchecked, so the hook always exited 0 and the harness treated setup as fully successful even with node_modules missing. Reproduced by corrupting package.json in a fresh worktree and confirming exit 0 despite bun's own parse error. Deliberately did not hard-fail here the way `git worktree add` does: by this point the worktree already exists and may be a *reused* existing one with real uncommitted work, not just a fresh checkout. Hard-failing without cleanup would orphan directories again (the bug fixed two commits ago); adding cleanup risks deleting real work on a transient install failure. Instead the hook now prints an explicit warning to stderr on failure, so the condition is visible rather than silently swallowed, while still completing successfully since a worktree without deps is still usable. Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c14e9d6. Configure here.
There was a problem hiding this comment.
Stale comment
Not approving: Cursor Bugbot completed with a skipping status and reported one unresolved medium-severity finding (missing
git worktree prunebefore add). This PR also introduces a newAPPROVAL_POLICY.mdwith no base-branch version, which requires human review. Recommend @rhuanbarreto address or resolve the Bugbot finding before merge.Sent by Cursor Approval Agent: Archgate CLI Approver
Cursor Bugbot flagged that WorktreeCreate never ran `git worktree prune` before `git worktree add`. If a worktree's directory was ever removed without going through `git worktree remove` (crash, manual rm -rf), git still has it registered internally and `add` fails outright with "fatal: '<branch>' is already used by worktree at '<path>'" -- even though the directory is gone, blocking EnterWorktree until manual cleanup. The round-2 stale-dir check doesn't catch this case either, since it only fires when the directory exists but lacks .git; here the directory doesn't exist at all, only git's internal registration does. Reproduced by creating a worktree, rm -rf'ing just the directory, and re-running the hook with the same name -- confirmed the exact fatal error. Fixed with `git worktree prune` immediately before `add`; cheap and safe to run unconditionally. Regression-tested against the earlier stale-dir fix and the plain happy path. Signed-off-by: Rhuan Barreto <rhuan@barreto.work>
There was a problem hiding this comment.
Not approving: Cursor Bugbot passed with all prior findings resolved, but this PR introduces a new APPROVAL_POLICY.md with no base-branch version, which requires human review before merge. Recommend @rhuanbarreto confirm the policy wording; no reviewers were assigned.
Sent by Cursor Approval Agent: Archgate CLI Approver
# archgate ## [0.46.0](v0.45.7...v0.46.0) (2026-07-02) ### ⚠ BREAKING CHANGES * **session-context:** per-editor list/show subcommands; remove --skip and its false premise (#446) ### Features * **session-context:** per-editor list/show subcommands; remove --skip and its false premise ([#446](#446)) ([dd96230](dd96230)) ### Bug Fixes * **ci:** read POSTHOG_PROJECT_ID from secrets, not vars, in release annotation ([#443](#443)) ([a53b305](a53b305)) * detect opencode Desktop app installs, not just the CLI ([#439](#439)) ([ed92b2b](ed92b2b)) * **hooks:** force bash shell for WorktreeCreate hook on Windows ([#442](#442)) ([9da86cf](9da86cf)) * **release:** cap breaking-change bumps to minor while pre-1.0 ([#447](#447)) ([3df6c91](3df6c91)), closes [#446](#446) [#440](#440) [#440](#440) [#440](#440) * repair broken WorktreeCreate hook and add PR approval policy ([#441](#441)) ([de5e97d](de5e97d)) * **session-context:** select top-level opencode sessions, add --root flag ([#445](#445)) ([29b13f4](29b13f4)) --- This PR was generated with [simple-release](https://github.com/TrigenSoftware/simple-release). <details> <summary>📄 Cheatsheet</summary> <br> You can configure the bot's behavior through a pull request comment using the `!simple-release/set-options` command. ### Command Format ````md !simple-release/set-options ```json { "bump": {}, "publish": {} } ``` ```` ### Useful Parameters #### Bump | Parameter | Type | Description | |-----------|------|-------------| | `version` | `string` | Force set specific version | | `as` | `'major' \| 'minor' \| 'patch' \| 'prerelease'` | Release type | | `prerelease` | `string` | Pre-release identifier (e.g., "alpha", "beta") | | `firstRelease` | `boolean` | Whether this is the first release | | `skip` | `boolean` | Skip version bump | | `byProject` | `Record<string, object>` | Per-project bump options for monorepos | #### Publish | Parameter | Type | Description | |-----------|------|-------------| | `skip` | `boolean` | Skip publishing | | `access` | `'public' \| 'restricted'` | Package access level | | `tag` | `string` | Tag for npm publication | ### Usage Examples #### Force specific version ````md !simple-release/set-options ```json { "bump": { "version": "2.0.0" } } ``` ```` #### Force major bump ````md !simple-release/set-options ```json { "bump": { "as": "major" } } ``` ```` #### Create alpha pre-release ````md !simple-release/set-options ```json { "bump": { "prerelease": "alpha" } } ``` ```` #### Publish with specific access and tag ````md !simple-release/set-options ```json { "bump": { "prerelease": "beta" }, "publish": { "access": "public", "tag": "beta" } } ``` ```` ### Access Restrictions The command can only be used by users with permissions: - repository owner - organization member - collaborator ### Notes - The last comment with `!simple-release/set-options` command takes priority - JSON must be valid, otherwise the command will be ignored - Parameters apply only to the current release execution - The command can be updated by editing the comment or adding a new one </details> <!-- Please do not edit this comment. simple-release-pull-request: true simple-release-branch-from: release simple-release-branch-to: main --> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>



Summary
hooks.WorktreeCreatecommand in.claude/settings.json, which only ranbun install. Once aWorktreeCreatehook is configured, the Claude Code harness defers the entire worktree creation to it and expects only the resulting absolute path on stdout — the old hook did neither, soEnterWorktreefailed with errors likepath contains control charactersor anENOENTchdir into straybun installoutput. (21 stale empty directories under.claude/worktrees/plus orphanedclaude/*branches were leftover evidence of this having been broken for a while.)git worktree add, installs deps silently, and echoes only the resulting path. Verified end-to-end viaEnterWorktree/ExitWorktree— worktree created with full content +node_modules, session cwd matched, clean removal.CLAUDE.mdso it doesn't regress back to a barebun install.APPROVAL_POLICY.md, read by this repo's "Archgate CLI Approver" Cursor Automation (confirmed via its own PR comments — it's custom logic in that automation's prompt, not a documented Cursor platform feature). Per Cursor's Bugbot docs, asuccesscheck conclusion already means "no issues found, and no unresolved comments" — Bugbot does not always leave a review comment. The automation's prior fallback logic treated a clean pass with no comment as an unverified signal and withheld approval (see PR #439). The policy clarifies that the check's own conclusion is sufficient on its own, while still requiring human review when the check is missing/pending/failed or Bugbot left unresolved comments..claude/agent-memory/archgate-developer/) for future reference.Test plan
bun run src/cli.ts check— 39/39 ADR rules passbun run validate— full pipeline (lint, typecheck, format, 1310 tests, ADR check, knip, build check) passesoxfmt --check .— clean@reviewerskill — APPROVED, 0 violations/warnings across General/Process, Distribution, and General domainsWorktreeCreatehook viaEnterWorktree/ExitWorktreeAPPROVAL_POLICY.md(unverified — the automation's policy-file lookup is self-reported, not independently confirmed)