Skip to content

fix(worktree): initialize submodules and preserve their local work - #445

Merged
kixelated merged 13 commits into
mainfrom
fix/worktree-submodules
Nov 3, 2026
Merged

kixelated merged 13 commits into
mainfrom
fix/worktree-submodules

Conversation

@kixelated

@kixelated kixelated commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Initialize submodules before returning a new worktree. Clone and remote verification run outside the registry lock while the reservation remains charged; a creator record distinguishes live creation from interrupted recovery.
  • Keep remotely verified module tips under refs/remotes/quest-pins/, outside origin's branch-prune namespace. Fetch cloned tags by name, then batch any remaining SHA-only tips. This preserves fetched pins and tags without treating a pushed superproject gitlink as an uploaded module object.
  • Check module files and every module store, including deinitialized ones, before removal. Nested scratch uses its module's ignore rules. Protect leftover deinitialized files and local commits made after creation. Refuse removal while a module store has linked worktrees of its own, which deleting the store would break.
  • Charge each checkout's module stores, cloned into its own Git directory outside its path, in the disk budget; the derived reserve includes the main checkout's module stores.
  • Route nested hook cwds through the selected project while preserving direct vendored-project sessions and unrelated-cwd behavior.
  • Run every Git subprocess in worktree.ts and worktree-hook.ts without the repository-local variables (git rev-parse --local-env-vars, keeping -c config as Git does for other repositories). A Git hook or git submodule foreach exports GIT_DIR/GIT_WORK_TREE, which override -C discovery: with them naming a submodule, the hook created the worktree inside the submodule and removal checks inspected the wrong repository.

Claude Code's standard WorktreeCreate hook is the immediate consumer. This adds module support without a new post-create configuration API.

Merged with main after moq-dev/quest#425 landed, keeping both behaviors: a subagent hook walks up from a submodule cwd to the first registered checkout, then applies #425's agent-<id> match or workflow-agent binding; WorktreeCreate fetches a pr-<number> head through the project-routed checkout, falling back to the default base. Main's new gate-scope tests now await this PR's async run fixture, and a regression test binds a workflow agent that starts inside a submodule of its checkout.

Checks

  • just check and just test pass on adb28aa66f4b8cbf9a8a5087d930795b7b4b0a48: 1001 CLI tests, 2 skipped; 508 Worker tests; bundled/native lifecycle and shell suites.
  • New regression tests fail on the head before the module fixes: the hook under inherited GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE created repo/sub/.worktrees/agent-located; a 1 MiB module store went uncharged (allocated 1073152); removal deleted a module store serving a linked worktree.
  • Real isolated public-remote fixture passes create, first devshell, owner/non-owner stop, restore, and removal, preserving parent scratch (on c30efe23dd17bada5561d7c6a0d850548ee9a5c6; not rerun).
  • Fixture temporary directories stay under the checkout, with a Git discovery ceiling for outside-repository cases.

Review

(Written by GPT-6.1 Sol and Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 5 commits November 2, 2026 22:59
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
@kixelated
kixelated marked this pull request as ready for review November 3, 2026 07:30
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

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

Next included review available in 46 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 30d3c64c-2fe9-414e-b35d-59d5c7608daa
Commits

Reviewing files that changed from the base of the PR and between 5e6b104 and adb28aa.

Files selected for processing: 7
  • README.md
  • src/cli/main.ts
  • src/cli/worktree-hook.ts
  • src/cli/worktree.ts
  • test/agent.test.ts
  • test/worktree.run.test.ts
  • test/worktree.test.ts

Walkthrough

Worktree creation is now asynchronous and initializes submodules recursively. Removal checks root and submodule files, ignored paths, and commits in module stores before deleting a checkout. Hook checkout resolution now walks enclosing superprojects and can use an explicit project root. CLI creation uses shared promise handling. Tests were updated for asynchronous operations and expanded to cover nested submodule creation, preservation, and removal checks.

Merge Risk: Moderate · previous review 5e6b1

A hook launched with inherited Git location variables can create a worktree in the wrong repository. Clear those variables for Git subprocesses before merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title "fix(worktree): initialize submodules and preserve their local work" matches the main change. The raw summary and objectives show recursive submodule initialization during worktree creation,…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description is clearly related to the changeset. It describes submodule initialization during worktree creation, submodule-aware removal checks, module-store disk budget accounting, hook project r…
Full details: Title check

Explanation

The title "fix(worktree): initialize submodules and preserve their local work" matches the main change. The raw summary and objectives show recursive submodule initialization during worktree creation, plus recursive checks before removal so local submodule work is preserved. The title is concise, specific, and follows a conventional commit style.

Full details: Description check

Explanation

The description is clearly related to the changeset. It describes submodule initialization during worktree creation, submodule-aware removal checks, module-store disk budget accounting, hook project routing, and the Git environment-variable fix. It also lists the test results and review rounds, so it is specific rather than vague.

Full details: Docstring Coverage

Explanation

Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 8 functions across 6 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 5c097bf4-b7a6-4187-bb25-88e4ce30ce21
Commits

Reviewing files that changed from the base of the PR and between acb8682 and 5e6b104.

Files selected for processing: 7
  • README.md
  • src/cli/main.ts
  • src/cli/worktree-hook.ts
  • src/cli/worktree.ts
  • test/agent.test.ts
  • test/worktree.run.test.ts
  • test/worktree.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Fix comments: Fix CodeRabbit comments on this PR

Comment thread src/cli/worktree-hook.ts Resolved
@kixelated

kixelated commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator Author

Design change: A real submodule fixture reached its first devshell and stop/restore, but clean removal refused an object reachable through a cloned tag but no remote-tracking branch. Initialization fetched that tag from the remote without a separate remote-tracking ref. Preserve that signal at creation: when a new module has a tip not remotely tracked, explicitly verify that tip by fetching it to a namespaced remote-tracking ref outside origin's branch-prune refspec. Continue refusing later local commits. This verifies the object at its source; it does not trust a superproject gitlink as preservation. Add a fixture for remote-fetched refs absent from branches, then repeat both gates and a new local review.

(Written by GPT-6.1 Sol)

@kixelated
kixelated marked this pull request as draft November 3, 2026 07:40
kixelated and others added 2 commits November 2, 2026 23:41
… tips

Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
@kixelated
kixelated marked this pull request as ready for review November 3, 2026 08:03
kixelated and others added 2 commits November 3, 2026 07:02
A Git hook or `git submodule foreach` exports GIT_DIR, GIT_WORK_TREE, and
the other repository-local variables, which override discovery from `-C`.
With them naming a submodule, the WorktreeCreate hook resolved the project
to that submodule and created the checkout there, and removal checks
inspected the wrong repository. Drop Git's own `--local-env-vars` list,
keeping `-c` config as Git does for other repositories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Submodule initialization clones module stores into the checkout's own
Git directory, outside its path, so the disk budget never charged them,
and the derived reserve skipped the main checkout's stores. Count them
in each checkout's footprint and in the reserve.

Removal deletes those stores, breaking any linked worktree a store
serves outside the checkout. Refuse removal while one has any.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 194a0a2 Nov 3, 2026
21 of 22 checks passed
@kixelated
kixelated deleted the fix/worktree-submodules branch November 3, 2026 17:50
kixelated added a commit that referenced this pull request Nov 4, 2026
…uite

`git submodule` is a shell script that spawns sh and five more Git
processes per call, which costs seconds on Windows runners. Since #445,
every create, release, and removal ran it even without submodules, pushing
worktree tests past their timeout on windows-11-arm. List initialized
submodules from the index's gitlinks instead, and run
`submodule update --init` only when the checkout has any.

Fix the suite's other Windows failures where each lies: compare text that
Git checks out with core.autocrlf with line endings normalized, expect
PATH joined with path.delimiter, and await main for `worktree create`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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