feat(rig): provision .claude/worktrees ignore via global core.excludesfile (supersedes #23)#29
Draft
alex-mextner wants to merge 2 commits into
Draft
feat(rig): provision .claude/worktrees ignore via global core.excludesfile (supersedes #23)#29alex-mextner wants to merge 2 commits into
alex-mextner wants to merge 2 commits into
Conversation
…sfile Rebuild #23's gitignore feature under the CTO-chosen GLOBAL design: rig owns ONE marker-delimited block in git's global core.excludesfile so the harness's throwaway **/.claude/worktrees/ is ignored in EVERY repo on the machine, with zero per-repo commits and no per-repo `rig apply`. Wired like the git-hooks dispatcher (a `git config --global` setting + a managed file), in the GLOBAL rig layer. Target resolution at apply time: honor an existing core.excludesfile; when unset, set it to ~/.config/git/ignore AND write the block there (so a clean machine is fully provisioned by `rig init` alone). Reuses #23's marker reconcile machine (create/update/ok/conflict/ io_error), retargeted, plus: - strict idempotency: a re-apply is a byte-identical no-op; - dedup of the managed region: several duplicated blocks collapse to one, preserving user content BETWEEN blocks (splice per marker-pair, not first-begin..last-end); - drift in the GLOBAL section flags a missing/divergent/duplicated block and an unset core.excludesfile rig would set. Default ON at plan level (an absent `gitignore` key still provisions), so the GLOBAL block is NOT scaffolded into committed repo rig.yaml. The canonical block text (markers + fixed comment + entry) is byte-identical to what a provisioned machine already has, making re-apply zero-churn. Review: ran multi-model `review` on this diff; addressed findings (interleaved user-line preservation, XDG isolation in tests, scaffold cleanup, doc fixes). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- tests/test_global_excludes.py: target resolution both ways (core.excludesfile set vs unset, with injected git-config seams so no test runs real `git config --global`), every reconcile state, STRICT byte-identical re-apply, the dedup-of-managed-region collapse (incl. preserving a user line between blocks), CRLF/trailing-blank/no-final-newline preservation, drift parity, disabled-but- installed leftover scan, and a byte-stable canonical-block regression pin. - conftest: autouse fixtures isolate HOME + XDG_CONFIG_HOME and stub the git-config read/write seams suite-wide, so a full-plan e2e test can never touch the real ~/.gitignore or real global git config. - smoke.sh: HOME-isolated leg exercises the real init/apply flow — asserts core.excludesfile is set to the XDG default, the block is written, and the block is byte-stable (markers=2) across a re-apply. Also makes the mcp leg conditional on the carrier and prefers `uv run pytest`. - docs/config-schema.md: the `gitignore` (global core.excludesfile) section and validation note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
What & why
Rebuilds #23's gitignore feature under the CTO-chosen GLOBAL design.
#23 wrote a managed block into each repo's committed
.gitignore(per-repo, needs arig applyper repo). The CTO rejected that and chose a global git excludesfile: rig manages ONE marker block in git's globalcore.excludesfile, so**/.claude/worktrees/is ignored in every repo on the machine — zero per-repo commits, no per-reporig apply.This reuses #23's tested marker-reconcile machine (
create/update/ok/conflict/io_error, the offset-based splice, drift parity, thegitignore:schema), retargeted to the global file.Design
Global config, wired like the git-hooks
dispatcher— agit config --globalsetting plus a managed file, living in the GLOBAL rig layer. Default ON at plan level (an absentgitignorekey still provisions), so the global block is not scaffolded into committed reporig.yaml.Target resolution (at apply time):
core.excludesfilealready set (this machine:~/.gitignore) → manage the block in that file, leave git config alone (respect the user's choice).core.excludesfileunset → set it to the XDG default~/.config/git/ignoreand write the block there. So on a clean machinerig initdoes everything itself (set the config if absent + write the block).gitignore.excludesfile:override → force a specific file.Managed block — fenced by
# >>> rig-managed (do not edit) >>>/# <<< … <<<, a fixed explanatory comment, then the entries (default["**/.claude/worktrees/"], configurable). Only the bytes between the markers are ever touched; everything else (CRLF, trailing blanks, no-final-newline) is preserved verbatim.Strict idempotency — a re-apply is a byte-identical no-op. If a prior non-idempotent tool appended the block more than once, rig collapses the managed region to one correct block, preserving any user line that sits between duplicated blocks (splice per marker-pair, not first-begin..last-end).
.serena/is deliberately not ignored (committed shared memory).Drift —
rig statusflags a missing/divergent/duplicated block, and an unsetcore.excludesfilerig would set, in the global section.rig applyreconciles.Tests / smoke
uv run pytest tests/→ 509 passed.bash tests/smoke.sh→ exit 0 (HOME-isolated leg assertscore.excludesfileset to the XDG default, the block written, and markers=2 byte-stable across re-apply; pytest leg green).git config --globalor writes the real~/.gitignore: the git-config read/write seams are stubbed suite-wide and HOME/XDG are isolated.~/.gitignoreblock:resolve_global_excludes(...)→ok(no rewrite).reviewon the diff; addressed findings (interleaved-user-line preservation, XDG test isolation, removed the global block from the committed repo scaffold, doctor→status doc fix, CRLF create-path test, SYNC note on the duplicated path-expander).Supersedes #23
#23 should be closed as superseded — leaving that to the CTO (not closing it here).
Notes
origin/mainhas not moved since branch-off (error-system feat(errors): structured what/why/fix errors + stable exit codes (error-system v2) #27 and the tmux-v2 PR have not landed yet, so no rebase conflicts). Will re-run tests+smoke after those land if they touch these files.🤖 Generated with Claude Code