local-mount: git-list population, warm reattach, and teardown safety#349
Conversation
…afety Mount population can enumerate via git (population: 'git' | 'auto') so gitignored trees never enter the mount, seeds autosync state during the copy (priming pass eliminated), preserves mtimes for rsync-style quick comparison, and bulk-clones .git copy-on-write. attachMount() + AutoSyncHandle.exportState() support reusing a kept mount across sessions with correct delete propagation. Autosync no longer mirrors an externally torn-down mount root as a mass delete of the project. On a 1.5k-file repo whose directory holds 38k+ non-ignored files, mount setup drops from ~16s to ~1.5s and session teardown from ~11s to <1s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 2 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
📝 WalkthroughWalkthroughLocal mounts now support walk, git, and automatic population strategies, resumable autosync state, mtime-preserving copies, faster content comparisons, population-aware sync-back behavior, and safeguards against root teardown deletion cascades. ChangesLocal mount population and synchronization
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Project
participant createMount
participant Git
participant Mount
participant AutoSync
participant syncBack
Project->>createMount: population options and ignore patterns
createMount->>Git: resolve tracked files and ignore rules
Git-->>createMount: eligible file list
createMount->>Mount: populate selected files and optional .git
createMount->>AutoSync: seed file sync state
Project->>AutoSync: report edits or deletes
AutoSync->>Mount: reconcile project changes
Mount->>syncBack: provide mount changes
syncBack->>Project: apply allowed changes
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a new population option ('walk' | 'git' | 'auto') to optimize mount creation by leveraging git ls-files to exclude gitignored files. It also adds attachMount and exportState to support warm session reuse, preserves source mtimes to enable a fast-path content comparison, and implements safety guards against accidental mass deletions when roots vanish. The review feedback highlights two key issues: first, the check for patterns containing .git is overly broad and will trigger false positives on common patterns like .gitignore or .github; second, fs.cpSync does not support a mode option in Node.js, rendering the COPYFILE_FICLONE flag a silent no-op.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 295483e1b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/local-mount/src/auto-sync.ts`:
- Around line 160-162: Update the state snapshot creation around the auto-sync
initialization and the other applicable state-copy site so it clones each
FileState record rather than only creating a new Map. Ensure both supplied
initialState and exported snapshots contain detached FileState objects,
preserving the readonly/snapshot contract while keeping existing map contents
and reconciliation behavior unchanged.
In `@packages/local-mount/src/mount.ts`:
- Around line 199-217: Update the git population flow around cloneGitInto and
initialState so bulk-cloned .git/** entries are recorded in the autosync
FileState map before priming is skipped. Pass initialState into cloneGitInto if
supported, or seed equivalent .git/** state immediately afterward, while
preserving the existing file-count behavior.
- Around line 777-793: Update cloneGitInto so sourceStat.isFile() does not copy
a worktree .git pointer verbatim; resolve the referenced gitdir and rewrite the
pointer for the mounted repository, or reject this case by disabling includeGit.
Ensure the mount cannot retain a pointer to the original checkout’s gitdir while
preserving normal directory-based .git cloning.
In `@packages/local-mount/src/stat-compare.ts`:
- Around line 35-40: Update statsImplySameContent so matching size and mtime are
not treated as proof of equal content; use a cached checksum when available or
make this predicate only a preliminary hint that callers must confirm with byte
comparison before skipping reconciliation or sync-back. Remove the five-second
recency guard as the correctness mechanism, while preserving the existing
metadata checks as an optimization signal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2a749a94-5926-49b2-9970-7ea3a64e52e8
📒 Files selected for processing (7)
packages/local-mount/CHANGELOG.mdpackages/local-mount/src/auto-sync.test.tspackages/local-mount/src/auto-sync.tspackages/local-mount/src/index.tspackages/local-mount/src/mount.test.tspackages/local-mount/src/mount.tspackages/local-mount/src/stat-compare.ts
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- Probe the real matchers for '.git' coverage instead of substring matching, so '.github'/'.gitignore' patterns no longer force the walk and glob spellings no longer bypass the guard - Honor tracked nested .gitignore files (directory-scoped) and the user's global excludes in the sync predicate - Seed autosync state for the bulk-cloned .git tree: project-side .git deletions propagate and pre-autosync mount .git edits survive - Never copy a worktree-style .git pointer file into the mount: git inside the mount would mutate the host checkout's worktree metadata - Skip state seeding for dereferenced symlink copies — auto-sync's symlink-rejecting stat would read the seeded entry as a project-side delete and remove the mount copy on first reconcile - attachMount: run createMount's overlap validation on realpaths and throw for explicit population 'git' when preconditions fail - Detach FileState snapshots on initialState import and exportState Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v0.10.22 release commit bumped the platform-package versions inside package-lock.json without regenerating resolution entries, dropping the linux-x64 entry entirely; every npm ci on a branch cut from that tip fails with "Missing: @relayfile/mount-linux-x64@0.10.22 from lock file". Entry restored to mirror its platform siblings, verified with npm ci --dry-run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Relayfile Eval ReviewRun: Passed: 4 | Needs human: 0 | Reviewable: 0 | Missing output: 0 | Failed: 0 | Skipped: 0 Human Review CasesNo reviewable human-review cases captured Relayfile output. |
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…oject dir The ls-files listing runs with `git -C projectDir`, so a (misconfigured) relative excludes path resolves there — mirror that instead of letting readIgnoreRuleLines fall back to process.cwd(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What this is
@relayfile/local-mountmounts are now fast enough to sit on an interactive launch path, and safe against external teardown.Population —
population: 'git' | 'auto'enumerates the mount set viagit ls-files --cached --others --exclude-standardinstead of walking the directory tree. Gitignored trees (nested caches, stale worktrees, build outputs at any depth) never enter the mount. The repo's root.gitignore+.git/info/excluderules join the mount's ignore set so reconcile/syncBack agree with the populated set; tracked-but-gitignored files are excepted and sync normally. Non-git projects, submodules, and pattern negations fall back to the walk. WithincludeGit,.gitis one timestamp-preserving copy-on-write bulk clone.Autosync priming — population records both sides' mtimes per copied file and seeds
startAutoSync, which previously byte-compared every file pair in a full-tree priming walk. This also fixes a latent first-reconcile bug: project edits made betweencreateMountandstartAutoSyncnow win over the stale mount copy, and mount-side.gitsetup writes survive reconciles.Quick comparison — copies preserve source mtimes everywhere, and content equality takes an rsync-style quick path (equal size + mtimes within 2ms), guarded by a 5s recency window so fresh writes always get byte-compared.
syncBackskips no-sync-back subtrees (.git/**) instead of walking them.Warm reuse —
attachMount()reattaches to a kept mount directory without wiping or re-copying;AutoSyncHandle.exportState()persists the per-file sync state so the next attach distinguishes deletions from creations (without it, files deleted from the project while a mount sat idle would be resurrected).Teardown safety — autosync no longer mirrors an externally torn-down tree as a mass delete: if the mount root's marker vanishes while autosync is alive, mount-side "deletions" stop propagating to the project, and a vanished project root stops emptying the mount. Previously, removing a live session's mount directory caused autosync to delete every mounted file from the user's project — this is reachable in production today by any crash-cleanup or manual
rmthat races a live session.Numbers (relay repo: 1,477 mount-relevant files inside a 75GB working directory)
.gitcloneConsumers
@agentworkforce/cliopts in withpopulation: 'auto'and builds a warm-launch fast path onattachMount/exportState(companion PR in AgentWorkforce/workforce). Walk mode remains the default; existing callers see only the mtime-preservation and safety changes.Semver: minor (new options/exports, no breaking API changes).
🤖 Generated with Claude Code