Skip to content

FE-879: Lazy per-slice cook worktrees and shared node_modules for brownfield#223

Open
kostandinang wants to merge 2 commits into
graphite-base/223from
ka/fe-879-lazy-cook-worktrees
Open

FE-879: Lazy per-slice cook worktrees and shared node_modules for brownfield#223
kostandinang wants to merge 2 commits into
graphite-base/223from
ka/fe-879-lazy-cook-worktrees

Conversation

@kostandinang

@kostandinang kostandinang commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Stack Context

Stacks on FE-864. Refines the brownfield per-slice worktree provisioning FE-755 introduced -- makes it lazy and shares node_modules -- without changing what cook produces. (brunch serve / FE-878 is what surfaced the startup cost.)

What?

  • Lazy provisioning -- slice-worktree creation moves out of the eager wireHandlers loop into resolveSliceCwd, via a new idempotent ensureSliceWorktree. A slice's worktree is materialized on first fire, so a run touching 2 of 8 slices pays for 2 worktrees, not 8. Rework re-fires are no-ops; provisioning is synchronous (execFileSync), so concurrent fires of distinct slices serialize on the JS thread -- no two git worktree add calls overlap under the parallel policy.
  • Shared node_modules -- each slice symlinks node_modules/ to the parent worktree's single copy instead of CoW-copying it per slice (SHAREABLE_TOP_LEVEL_ENTRIES). walkFiles already skips symlinks, so the shared tree is never re-walked during dependency seeding, merge, or promotion. Other gitignored dirs (dist/) still copy per slice.

Why?

In codebase mode the slice layout is always per-slice, and every slice's worktree was provisioned up front -- N git worktree add + N recursive node_modules CoW copies, paid synchronously before any slice fired (9 of each for an 8-slice plan). The per-slice node_modules copy dominates wall-clock.

Behavior / risk

Correctness-neutral: same worktrees on the same branches, just lazy; deps resolve through the symlink. Trade-off: build caches under node_modules (.cache, .vite) become shared across parallel slices -- fine for cook's transient runs; documented at the call site.

Tests

npm run verify green. New unit tests: slice node_modules is a symlink (not a copy), other gitignored content still copies, ensureSliceWorktree idempotent. Brownfield integration smoke unchanged.

kostandinang commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@kostandinang kostandinang changed the base branch from ka/fe-878-brunch-serve to graphite-base/223 June 16, 2026 14:17
@kostandinang kostandinang changed the base branch from graphite-base/223 to ka/fe-878-brunch-serve June 16, 2026 14:19
@kostandinang kostandinang marked this pull request as ready for review June 16, 2026 15:00
@cursor

cursor Bot commented Jun 16, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches brownfield sandbox layout (symlinked node_modules and shared build caches under parallel slices); behavior is intended to be correctness-neutral but parallel cook runs could see toolchain cache contention.

Overview
Brownfield codebase-mode cook no longer provisions every slice worktree at wiring time. wireHandlers drops the eager loop over all slices; resolveSliceCwd now calls idempotent ensureSliceWorktree on first use so only slices that actually fire pay for git worktree add and seeding (rework repeats are no-ops).

Slice seeding symlinks node_modules to the parent worktree via SHAREABLE_TOP_LEVEL_ENTRIES and an optional symlink set on copyMissingTopLevelEntries; other gitignored top-level dirs (e.g. dist/) still CoW-copy per slice.

memory/PLAN.md marks cook-worktree-laziness (FE-879) done and ties it to closing the over-copy follow-on from cook-codebase-mode. Tests cover symlink behavior, dist still copied, and ensureSliceWorktree idempotency.

Reviewed by Cursor Bugbot for commit e35fa92. Bugbot is set up for automated code reviews on this repo. Configure here.

kostandinang and others added 2 commits June 16, 2026 16:30
Brownfield cook provisioned every slice's git worktree eagerly in
wireHandlers — N `git worktree add` + N recursive node_modules CoW copies
paid synchronously at startup before any slice fired.

- Move slice-worktree creation into resolveSliceCwd via idempotent
  ensureSliceWorktree, so a slice's worktree is materialized on first fire.
  A run touching 2 of 8 slices pays for 2 worktrees, not 8. Synchronous
  provisioning serializes concurrent fires on the JS thread, so parallel-policy
  worktree adds never overlap.
- Symlink each slice's node_modules to the parent worktree's single copy
  instead of CoW-copying per slice (SHAREABLE_TOP_LEVEL_ENTRIES). walkFiles
  already skips symlinks, so the shared tree is never re-walked during dep
  seeding, merge, or promotion. Other gitignored dirs still copy per slice.

Correctness-neutral: same worktrees/branches, just lazy; deps resolve through
the symlink. npm run verify green; adds symlink + idempotency unit tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add the cook-worktree-laziness frontier definition (FE-879, done) and mark
the per-slice over-copy / eager-seeding optimization resolved — closing
cook-codebase-mode acceptance (8) and the sandcastle over-copy trigger.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@kostandinang kostandinang changed the base branch from ka/fe-878-brunch-serve to graphite-base/223 June 16, 2026 15:31
@kostandinang kostandinang force-pushed the ka/fe-879-lazy-cook-worktrees branch from 3c0fa64 to e35fa92 Compare June 16, 2026 15:31
@kostandinang kostandinang changed the base branch from graphite-base/223 to ka/fe-864-orchestrator-enhancements June 16, 2026 15:31

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e35fa92. Configure here.

): string {
const sliceDir = resolveSliceWorktreeDir(parentSandboxDir, sliceId);
if (existsSync(sliceDir)) return sliceDir;
return seedSliceFromParentWorktree(parentSandboxDir, sliceId, plan, runId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Slice id collision silently ignored

High Severity

ensureSliceWorktree returns immediately when the slice path already exists, without checking that it is a provisioned git worktree. If a slice id matches a top-level tracked entry in the parent sandbox (e.g. slice src when the repo has src/), cook uses that project directory as the sandbox instead of failing, breaking per-slice isolation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e35fa92. Configure here.

@kostandinang kostandinang changed the base branch from ka/fe-864-orchestrator-enhancements to graphite-base/223 June 16, 2026 23:44
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