chore: enforce isolated worktrees for feature work - #980
Merged
Conversation
The main checkout kept accumulating in-flight work from concurrent sessions. Three defects made that unavoidable: - `.worktrees/` was never gitignored, even though validate-docs.py already excluded it. A nested .git directory stages as a gitlink and silently corrupts history. - `install-hooks.sh` resolved hooks via `--git-dir`, which points at the per-worktree directory with no `hooks/`, so it failed outright inside a worktree. - A global `core.hooksPath` silently overrode the repo-local hook, so the origin guard never actually ran. Add `worktree.sh` to create, list, retire, and prune worktrees branched from a freshly fetched projectbluefin/testing. Branch retirement is resolved through the forge because squash merges leave no ancestry for `git branch --merged` to find. Extend the pre-push hook to refuse feature-branch pushes from the main checkout, overridable with SKIP_WORKTREE_GUARD=1. Install hooks into the common git dir and, when core.hooksPath is overridden, redirect it locally while shimming every unmanaged global hook so it still runs. Assisted-by: Claude Opus 5 via GitHub Copilot CLI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Blocking the remote named `origin` rejected every push in a clone made directly from projectbluefin/bluefin, where `origin` is the correct target. Match on the push URL instead, which enforces the documented "no writes outside the projectbluefin org" boundary in both setups. Also: - restore the executable bit on install-hooks.sh, lost in the previous commit - parse `git worktree list --porcelain` paths with substr instead of a whitespace-split field, so paths containing spaces are not truncated - fold the worktree mandate into AGENTS.md "Change flow" so it no longer contradicts the plain `git checkout -b` instructions added by #979 - narrow the worktree boundary rule to another task's worktree Tested: shellcheck on both scripts and the generated hooks; installer run twice against a scratch clone with an existing hooksPath, with no hooksPath, and with a nonexistent hooksPath; pre-push exercised for feature branch from main checkout, from a linked worktree, on testing, with SKIP_WORKTREE_GUARD=1, and against ublue-os/fork/projectbluefin URLs; worktree.sh new/list/done/prune end to end. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
castrojo
force-pushed
the
chore/worktree-workflow
branch
from
August 7, 2026 03:19
679e206 to
45000d7
Compare
This was referenced Aug 7, 2026
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The main checkout kept accumulating in-flight work from concurrent sessions. Three defects made that unavoidable:
.worktrees/was never gitignored — even thoughvalidate-docs.pyalready excluded it from scanning. A nested.gitdirectory stages as a gitlink and silently corrupts history, which is exactly whatAGENTS.mdwarns about.install-hooks.shresolved hooks via--git-dir— inside a worktree that points at the per-worktree directory, which has nohooks/. The script failed outright.core.hooksPathsilently overrode the repo-local hook — so theoriginpush guard has never actually run for anyone who sets one.Changes
.github/scripts/worktree.sh— create, list, retire, and prune worktrees:Branch retirement is resolved through the forge via
gh, because squash merges leave no ancestry forgit branch --mergedto find.install-hooks.shnow:SKIP_WORKTREE_GUARD=1to override);core.hooksPathis overridden, redirects it repo-locally and shims every unmanaged global hook so they keep running. The original path is saved tobluefin.chainedHooksPathso re-runs stay idempotent.Verification
just check,pre-commit run --all-files, andvalidate-docs.pyall pass. Behavior tested end-to-end:SKIP_WORKTREE_GUARD=1from main checkoutoriginnew→doneround-tripinstall-hooks.shrun twiceA
--show-toplevelbug was caught during testing: it returns the current worktree's root, so it misidentified the main checkout. Now derived from--git-common-dir.Notes
docs/skills/worktrees/SKILL.md, plus index andAGENTS.mdentries..github/scripts/, not.github/workflows/, so no CODEOWNERS gate.~/.git-hooks/pre-pushon at least one maintainer machine is mode644, so it is silently skipped. The new hook now warns about this instead of failing quietly.Assisted-by: Claude Opus 5 via GitHub Copilot CLI