Skip to content

Add a .worktreeinclude reader for seeding new worktrees - #15413

Merged
teamleaderleo merged 6 commits into
manaflow-ai:mainfrom
teamleaderleo:parity/worktree-include
Sep 29, 2026
Merged

teamleaderleo merged 6 commits into
manaflow-ai:mainfrom
teamleaderleo:parity/worktree-include

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A new agent worktree arrives without the files git does not track. AgentMoveScripts carries the working tree with git add -A, which respects .gitignore, so .env, local config and installed dependency directories are exactly what a fresh worktree lacks and exactly what the session then cannot run without. Right now the answer is hand-run cp after every worktree creation.

This is the portable half of the fix: a .worktreeinclude at the repository root naming what to carry, a planner that expands it against the tree, and an applier that copies or symlinks the result. Six types, all Foundation only, all injectable (the planner takes a directory listing closure, so plans are testable without a filesystem):

  • WorktreeSeedPattern / WorktreeSeedFile parse the file. A bad line becomes a reported problem, not a parse failure, so one typo does not discard the rest of the file.
  • WorktreeSeedPlanner expands patterns into a plan, and reports what it decided against: excluded, refused, shadowed, alreadyPresent, unmatched, ineffectiveNegations.
  • WorktreeSeedRepository reads a real tree. WorktreeSeedApplier performs the plan and reports per path; one failure never stops the rest.

Two departures from .gitignore syntax, both deliberate and documented at their definitions. Every pattern is a path from the repository root, because gitignore's basename-anywhere matching would force a walk through the very directories (node_modules) that make a walk expensive; ** is how a pattern opts into a walk. And a matched directory is taken whole, because a copy could filter its contents but a symlink cannot, so one rule serves both actions. The second departure silently disarms a ! aimed inside such a directory, which is why the planner detects that case and reports it as ineffectiveNegations instead of no-oping.

Two safety properties worth a reviewer's attention. A symlink is a leaf even when it points at a directory, the way git reads one, so the walk cannot loop through a link to its own ancestor and a build/ pattern does not match a symlink named build. And a path whose target resolves outside the repository is refused rather than followed, with the inside test comparing resolved paths plus a separator so /repo-backup does not read as inside /repo. The walk is also budgeted (maximumVisitedDirectories, default 20000) and says so in the plan (reachedWalkLimit) rather than silently truncating.

No CLI surface here, and nothing calls this yet. The file format is a product decision, so it goes to #13742 with a recommendation before a cmux worktree verb reads it.

Testing

70 swift-testing tests added in CMUXAgentLaunchTests: 17 on line parsing, 8 on file parsing, 25 on planning against an injected listing (including root anchoring, last-match-wins negation, the walk budget, and a pathological glob that must not hang), and 20 on a real filesystem in temp directories (escaping symlink refused, symlink loop not hanging the walk, dangling destination symlink counting as occupied, a link entry landing as an absolute symlink, one failure not stopping the rest).

Executed: every one of those 70, by swift test in a scratch SwiftPM package on Linux (Swift 6.1.3), since CMUXAgentLaunch as a whole needs Darwin and cannot build there. The eight files are byte-identical to what ran, apart from the test module import. Result: Test run with 70 tests passed.

Not executed by me: the same tests inside CMUXAgentLaunch on macOS. That is what CI's package lane establishes on this SHA, and it is the thing to check before merge. No app build, no tagged build, no dogfood: nothing in this PR is reachable from the UI yet.

New files land in an existing SwiftPM target with no explicit sources, so there are no project.pbxproj edits and scripts/sync-test-wiring does not apply (it covers cmuxTests/).

Changelog

none

Demo Video

Not applicable: no user-reachable surface in this PR.

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • UI, settings, menu, schema, help-text or user-facing docs change: none. No user-facing strings, so no localization audit. The WorktreeSeedProblem advice strings are developer-facing today; the PR that surfaces them through the CLI owns their audit.
  • New or changed v2 socket method allowlisted for cmux ssh: none.
  • iOS connectivity, auth, lifecycle, workspace action, terminal I/O or mobile RPC contract change: none.
  • User-facing docs updated if needed: none yet; the format gets documented with the CLI verb that reads it.
  • Reviewed with a subagent before merge

🤖 Generated with Claude Code


Summary by cubic

Adds a .worktreeinclude reader so new agent worktrees carry the files git doesn't track. Today a worktree arrives without .env, local config, and installed dependency directories, so the first command a session runs fails on missing configuration; the workaround is a hand-run cp after every creation.

The feature parses the file, expands its patterns into a plan against the repository tree, and copies or symlinks the result into the new worktree. Everything is Foundation-only and injectable, so the planner is testable without a filesystem. Patterns are paths from the repository root — ** is how a pattern opts into a walk, so a root pattern never descends into node_modules — and a matched directory is taken whole. A ! aimed inside a selected directory is reported as ineffectiveNegations naming the covering ancestor. A symlink is a leaf even when it points at a directory, so the walk cannot loop, and paths resolving outside the repository are refused.

Delivery never overwrites what git checked out: occupied destinations (including dangling symlinks) are skipped, a destination symlink ancestor that could redirect a write is refused, a dangling source symlink is delivered as a link node, and one failure never stops the rest.

Testing

  • 70 new tests cover parsing, planning against injected listings, and real-filesystem application; all pass on Linux, and the run inside CMUXAgentLaunch on macOS is what CI establishes.

No surface yet

Written for commit c783cce. Summary will update on new commits.

Review in cubic

A new agent worktree arrives without the files git does not track. Today
AgentMoveScripts carries the working tree with `git add -A`, which respects
.gitignore, so `.env`, local config and installed dependency directories are
exactly what a fresh worktree lacks and exactly what the session then cannot
run without.

This adds the portable half: a `.worktreeinclude` at the repository root naming
what to carry, a planner that expands it against the tree, and an applier that
copies or symlinks the result. The CLI surface comes separately, since the file
format is a product decision and is going to cmux#13742 first.

Two departures from .gitignore syntax, both deliberate and documented in the
sources. Every pattern is a path from the repository root, because gitignore's
basename-anywhere matching would force a walk through the very directories
(node_modules) that make a walk expensive; `**` is how a pattern opts into one.
And a matched directory is taken whole, because a copy could filter its contents
but a symlink cannot, so one rule serves both actions. The second departure
silently disarms a `!` aimed inside such a directory, so the planner reports
those as ineffectiveNegations rather than dropping them on the floor.

A symlink is a leaf even when it points at a directory, the way git reads one,
so the walk cannot loop through a link to its own ancestor. A pattern whose
target resolves outside the repository is refused rather than followed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 18 seconds.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 03592a4f-5dac-4d7d-96b8-cea824e0575b

📥 Commits

Reviewing files that changed from the base of the PR and between 5410299 and c783cce.

📒 Files selected for processing (10)
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/WorktreeSeed/WorktreeSeedApplier.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/WorktreeSeed/WorktreeSeedFile.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/WorktreeSeed/WorktreeSeedPattern.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/WorktreeSeed/WorktreeSeedPlan.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/WorktreeSeed/WorktreeSeedPlanner.swift
  • Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/WorktreeSeed/WorktreeSeedRepository.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/WorktreeSeedFileTests.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/WorktreeSeedPatternTests.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/WorktreeSeedPlannerTests.swift
  • Packages/macOS/CMUXAgentLaunch/Tests/CMUXAgentLaunchTests/WorktreeSeedRepositoryTests.swift

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

CI failure attribution

CI passes on c783cce2e3 (run 36474298417 attempt 1).

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Repaired the worktree seeding edge cases before merge: destination symlink ancestors cannot redirect delivery, source dangling symlinks use no-follow existence checks, repeated ** matching is memoized, leading glob escapes stay literal, and shadow/ineffective-negation diagnostics now name the operative retained root and its real link/copy action.

Validation after merging current main: focused WorktreeSeed coverage reached 76/76; full CMUXAgentLaunch passed 659/659; diff checks clean. Independent final review was clean.

— Mochi

@teamleaderleo
teamleaderleo enabled auto-merge (squash) September 28, 2026 19:44
@teamleaderleo
teamleaderleo merged commit 860619f into manaflow-ai:main Sep 29, 2026
68 of 69 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Merge receipt for c783cce2e3: every check was green at merge (21 verified; 18 skipped by policy). Full suite runs on main after merge.

rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 29, 2026
4e0f7d2 fix(bash): keep $? for PROMPT_COMMAND hooks after cmux's (manaflow-ai#15255)
ae49bf5 fix(examples): show custom description in Project Worktrees sidebar (manaflow-ai#15256)
a9a229d Add cross-provider token usage accounting for agent transcripts (manaflow-ai#15332)
860619f Add a .worktreeinclude reader for seeding new worktrees (manaflow-ai#15413)
3edbd83 Clear the stale Needs input badge when Claude's permission is decided in the terminal (manaflow-ai#15170)
9ed9294 CodeRouter: hold capacity errors on the same model instead of failing fast (manaflow-ai#15310)
56d4547 docs: add a front door for outside contributors (manaflow-ai#15263)
799f906 fix(ci): recognize GUI token acquisition failures (manaflow-ai#15449)
f118d43 ci: age parked builds by measured reuse distance (manaflow-ai#15616)
1f6744d ci: harden overflow switch recovery (manaflow-ai#15617)
9987778 Predicted echo: remote terminals only, withdraw on pasted and sent input (manaflow-ai#15211)
d9e199b Subtle selection follow-ups: group header hairline, no focus re-render for legacy rows, cmux.json test (manaflow-ai#15195)
c13afe1 test: cover UTF-8 workspace create commands (manaflow-ai#15622)
e76a660 fix: preserve Claude remote-control names on restore (manaflow-ai#15619)
900f248 feat: expose cmux-owned scratch metadata in session listing (manaflow-ai#15615)
b5604fa ci: say why compiled-product reuse refused an artifact (manaflow-ai#15553)

# Conflicts:
#	.github/workflows/ci-cloud-overflow-probe.yml
@github-actions

Copy link
Copy Markdown
Contributor

These app-host tests newly fail in main's full suite at 58505963a8, after this pull request merged. They did not fail in the previous full-suite run at 3edbd83801, and are not in scripts/ci/app-host-known-failures.json.

Commits in the range: 3edbd83...5850596

Pull requests run only the suites their diff reaches, so main's full suite is where this shows first. If this pull request is the cause, please fix forward or revert; if it is not, say so here. This is an automated attribution and can be wrong, most often for a flaky test.

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