Skip to content

chore(worktree): put worktrees under .claude and carry the overrides into them - #11

Merged
anilcancakir merged 3 commits into
mainfrom
chore/worktree-layout
Sep 3, 2026
Merged

chore(worktree): put worktrees under .claude and carry the overrides into them#11
anilcancakir merged 3 commits into
mainfrom
chore/worktree-layout

Conversation

@anilcancakir

Copy link
Copy Markdown
Contributor

What

  • .gitignore: ignore .claude/worktrees/ (only that subdirectory; the rules under .claude/ stay tracked)
  • .worktreeinclude: new, copies pubspec_overrides.yaml into every worktree Claude Code creates

Why

Worktrees were opened beside the checkout as <repo>-<slug>, so they landed in the workspace directory next to the real repositories, and Claude Code's EnterWorktree could not be used at all: it writes to a fixed .claude/worktrees/<slug>.

Moving them inside exposes a problem the old layout hid. pubspec_overrides.yaml is gitignored, so a worktree never receives it, and the absence is silent rather than loud: the siblings resolve from pub.dev, flutter pub get succeeds, and the suite passes against the PUBLISHED packages while the diff under review is of the local ones. An unreleased sibling API is where that bites.

For the copy to be correct the local file needs ABSOLUTE paths, since a relative ../magic resolves to .claude/worktrees/magic from inside a worktree. That file is machine-local and not committed, so this PR cannot carry it; the requirement is documented in .worktreeinclude.

Testing

The pattern was proven end to end on fluttersdk/magic_starter#124 first: probe worktree opened under the new path, pubspec_overrides.yaml confirmed absent, copied in, flutter pub get resolved and package_config.json pointed at the local checkouts. Here I verified that every rewritten absolute path resolves to a real package directory and that flutter pub get still succeeds.

No Dart, YAML or lib/ file is touched, so analyze, format and test have nothing to say about this diff.

…into them

Worktrees used to sit beside the checkout as <repo>-<slug>, which put them in the workspace directory next to the real repositories and left EnterWorktree unusable, since that tool writes to a fixed .claude/worktrees/<slug>. Pattern proven on fluttersdk/magic_starter#124.
@kodizm

kodizm Bot commented Sep 3, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Two config files, both correct in themselves, but the hole they close stays open for the worktree path the canonical docs actually prescribe.

Major

.worktreeinclude:21 — this closes the silent-resolution hole only for worktrees Claude Code creates. AGENTS.md:25 tells an agent to "work in a worktree under .claude/worktrees/<slug>" and says bin/check copies the three gitignored files it needs, naming pubspec_overrides.yaml first. It does not: bin/check:90 iterates backend/.env .artisan/plugins.json backend/public/build, even though its own comment at bin/check:78 claims the overrides are covered. So a plain git worktree add .claude/worktrees/x followed by bin/check gets the backend files, no overrides, and the suite passes against the published siblings - exactly the failure this PR sets out to kill. Either add pubspec_overrides.yaml to the bin/check loop or update AGENTS.md (and re-run bin/sync-instructions) to say the copy only happens on the Claude Code path. (correctness)

.worktreeinclude:13 — the absolute-path requirement is enforced by nothing and documented only inside an ignore file no reader of AGENTS.md is pointed at. Any developer whose local overrides use the conventional relative path: ../magic now gets a worktree where the first flutter pub get fails on a path that resolves to .claude/worktrees/magic, and AGENTS.md:25 tells them not to hand-author the file that needs rewriting. A relative-path check in bootstrap_ignored_files that prints the fix would turn a confusing solver error into an instruction. (maintainability)

Minor

.worktreeinclude:1 — neither AGENTS.md's worktree section nor its "Where the instructions live" table mentions this file, so an agent reading only the canonical instructions never learns the constraint. Adding a row there and regenerating the .github/ mirrors keeps the one-canonical-file rule intact.

The .gitignore rule itself is right and scoped as described: git check-ignore -v matches .claude/worktrees/x on the new line only, .claude/rules/design.md stays tracked, and nesting worktrees under a dot-directory does not leak into the suites - flutter analyze runs on the Dart package (the analyzer skips hidden directories), flutter test on test/, and pint from backend/ (bin/check:159-167).

Tests

Nothing covers this; it is two config files and there is no harness for worktree bootstrap in the repo. The end-to-end proof is the manual run described in the PR body, which I could not reproduce here.

Checks I ran

  • mcp__kodizm__read_pull_request_diff - 2 files, both read in full, nothing dropped.
  • git check-ignore -v pubspec_overrides.yaml .claude/worktrees/x - matched .gitignore:49 and .gitignore:70, so the .worktreeinclude entry does match a gitignored file as its comment requires.
  • Read bin/check (bootstrap_ignored_files, suite runners), AGENTS.md, DESIGN.md, .gitignore, bin/sync-instructions, analysis_options.yaml; grepped every worktree and .claude reference in bin/, lib/, *.md, .github/.
  • No suites run: the diff touches no Dart, YAML or PHP, and my attempt to probe analyzer descent into .claude/worktrees/ needed a file write, which is denied to me. That one claim is from documented analyzer behaviour, not observed here.

@anilcancakir

Copy link
Copy Markdown
Contributor Author

Both Majors were right, and the first one I checked against the code rather than the description: bin/check:90 iterated backend/.env .artisan/plugins.json backend/public/build, while AGENTS.md:25 claimed bin/check copies all three and bin/check:78 said the overrides matter. So a plain git worktree add plus bin/check really did produce a worktree resolving from pub.dev.

You offered two fixes; I took the sturdier one and did both halves:

  • bin/check now copies pubspec_overrides.yaml as well, with a comment saying why it is in both places: .worktreeinclude only runs on the Claude Code path, and the uncovered combination is the silent one.
  • AGENTS.md now says which mechanism covers which path instead of crediting bin/check with all three, and states the absolute-path requirement as its own bullet. Added a .worktreeinclude row to the instructions table and re-ran bin/sync-instructions, so .github/copilot-instructions.md is regenerated.

@kodizm

kodizm Bot commented Sep 3, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Both Majors and the Minor from my last pass are closed; the new commits leave one stray character in a .gitignore comment and nothing else.

Since 64b47ad: bin/check:96 now copies pubspec_overrides.yaml, AGENTS.md:25-26 splits the two mechanisms and states the absolute-path requirement as its own bullet, the instructions table gains a .worktreeinclude row, and the .github/ mirror is regenerated.

Minor

.gitignore:68# under it are part of the repository.# has the # of what was meant to be a blank separator line glued onto the end of the previous one, so the new paragraph runs straight into the old with no break. Harmless to git (still a comment), but it reads as a typo in a block that is otherwise carefully written. (maintainability)

Tests

Still nothing automated; there is no harness for worktree bootstrap in this repo, and the bin/check copy path only exercises inside a real secondary worktree. Same as last pass.

Checks I ran

  • mcp__kodizm__read_pull_request_diff with since_sha=64b47ad - scope: incremental, 4 files, all read in full, nothing dropped.
  • bin/sync-instructions --check - "mirrors are up to date", exit 0, so .github/copilot-instructions.md matches AGENTS.md and CI's mirror gate passes.
  • bash -n bin/check and sh -n bin/check - both clean; bootstrap_ignored_files is called at bin/check:130, ahead of the flutter pub get at bin/check:143, so the copied overrides are in place before resolution.
  • git check-ignore -v .claude/worktrees/x .claude/rules/design.md - matches .gitignore:74 for the worktree path only; .claude/rules/design.md stays tracked, so the reworded comment block did not disturb the rule.
  • No suites run: the incremental diff touches no Dart, YAML or PHP.

The comment claimed plain -xdf wipes a live worktree because its .git is a file rather than a directory. Measured on a scratch repo: the dry run prints "Skipping repository .claude/worktrees/slug", the worktree survives -xdf with its untracked files intact, and only -xdff removes it and leaves the registration prunable. The guard keys on the gitlink, not on the form of .git. Also splits the stray # that joined the two comment blocks.
@kodizm

kodizm Bot commented Sep 3, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

The only new commit rewrites a .gitignore comment: the stray # I flagged is gone and the git clean claim is now the correct one. No findings.

Since 7b00dc7: one file, .gitignore, comment text only. The paragraph break is fixed, and the previous wording ("git clean -xdf here wipes a live worktree's contents") is replaced by the accurate split - -xdf skips the nested worktree because git's nested-repository guard resolves a gitfile as well as a .git directory, and only -xdff removes it and leaves a stale .git/worktrees/<slug> registration for git worktree prune. That matches documented git clean behaviour; I could not execute a probe to confirm it (see below), so this one is read, not run. The ignore rule itself is unchanged on the last line.

Tests

Unchanged from my last pass: nothing automated covers worktree bootstrap, and this commit changes no executable text at all.

Checks I ran

  • mcp__kodizm__read_pull_request_diff with since_sha=7b00dc7 - scope: incremental, 1 file, read in full, nothing dropped or skipped.
  • Attempted an empirical git clean -xdf / -xdff probe against a throwaway repo with a nested worktree in /tmp; the command was denied to me, so the git clean claim is assessed against documented behaviour rather than observed here.
  • No suites run: the incremental diff is comment text in .gitignore and touches no Dart, YAML or PHP.

@anilcancakir
anilcancakir merged commit b81124b into main Sep 3, 2026
6 checks passed
@anilcancakir
anilcancakir deleted the chore/worktree-layout branch September 3, 2026 18:04
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.

1 participant