FE-879: Lazy per-slice cook worktrees and shared node_modules for brownfield#223
FE-879: Lazy per-slice cook worktrees and shared node_modules for brownfield#223kostandinang wants to merge 12 commits into
Conversation
…ard queue Co-authored-by: Claude <noreply@anthropic.com>
…t/deno profiles Profiles are now data literals (path templates + argv template + conventions prose) compiled into the unchanged Toolchain interface — adding a runtime is one data entry. Registry invariants pinned enumerably across all profiles. Co-authored-by: Claude <noreply@anthropic.com>
…d into plan.yaml brunch plan gains --profile=<id> (validated via parseProfileId); the emitter resolves the chain once and stamps the id on both the authored and fallback plans, so cook always reads the profile the emitter used. resolveToolchain now throws UnknownProfileError on a typo'd id instead of silently running bun; absent stays lenient for hand-authored fixtures. Co-authored-by: Claude <noreply@anthropic.com>
architectPlan's schema gains an optional profile enum (registry ids, null when the spec is silent); the emitter chain becomes flag ≫ spec profile ≫ architect-classified ≫ bun. A hallucinated profile fails the schema parse and rides the existing deterministic fallback. D160-K intact — classification reads projected spec prose only. Queue exhausted: CARDS.md retired; PLAN frontier marked branch-complete. Co-authored-by: Claude <noreply@anthropic.com>
Add the full-cook-orchestrator frontiers to memory/PLAN.md as a planning baseline. Arc 1 (brunch-detect, harness-dep-install, app-runtime-probe, integration-oracle, brownfield-promotion, brunch-ship + dogfood-spike) and Arc 2 (interactive-recovery, intent-conformance-oracle, adaptive-replan), plus the separate transformation-orchestrator line. Records the cook-time grounding decision, the greenfield-protecting invariant, the kitchen-brigade CLI surface, and the unpublished pi-thread coordination note. Docs-only; downstream feature branches stack on this. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Categorize the pi harness as a dual-mode (elicit/execute) agent-extension host: mode-neutral core + per-mode plugins. Cook capabilities are execute-mode plugins; the existing interview is the elicit witness. Adds the agent-extension-host frontier with the four abstracted-enough acceptance criteria (mode-neutral core, two-consumer proof, open plugin seam, no gold-plating), the execute-mode-plugin hooks on the dispatch-seam frontiers (integration-oracle, interactive-recovery, adaptive-replan), and the Context coordination note. It gates only the dispatch-seam frontiers; the seam-independent infra is unaffected. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Move agent-extension-host from a parallel prerequisite track to the base of the Arc-1 cook stack (2026-06-15 decision): every Arc-1 frontier now stacks on it. Updated the arc-framing paragraph, the Next sequencing list (promoted to step 1, renumbered 2-8), the frontier definition, and brunch-detect's stacking note. Kept the honest framing that the logical gate is still only integration-oracle, so basing the whole arc on it is a deliberate stack-order coupling to the pi-harness-thread coordination. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Consultation against the cook harness surfaced two corrections, now folded into the frontier definitions: - brunch-detect: slice 1 (detectProfile / project-detect.ts) already landed as FE-871 — status updated from not-started; only slice 2 (wire `detected` into the emitter chain + greenfield-protection) remains. Restated as a plan-time profile-id resolver (not a language-detection engine) and added the D160-K boundary note: profile-id resolution is an input to authoring, not architect host-introspection, so no D160-K amendment is needed. - harness-dep-install: the install *action* is agent-native (cook actions carry bash; FE-843 testConventions inject install prose per A98). Reframed from an "install verb" to dependency-delta capture + install/infra-vs-test failure classification — the two things bash install doesn't give for free. Dropped the now-redundant install criteria + install-verb unit tests. - agent-extension-host: status updated to contract-landed (FE-867). - interactive-recovery: noted the asking reuses the elicit/secondary-chat substrate (FE-716); the load-bearing work is resume from durable markings. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ames are phases The kitchen-brigade naming (prep/recipe/taste/plate/serve) was an aspirational command surface, but implementation revealed 4 of the 6 are internal pipeline stages, not CLI verbs: detect runs inside `plan`; probe+oracle and promotion run inside `cook`/`serve`. Only `serve` (FE-878) and the pre-existing `cook` adopted brigade names; `plan` never became `recipe`. Reword the surface to: real commands = plan/cook/serve; prep/recipe/taste/plate are phase labels. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
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.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
PR SummaryLow Risk Overview Shared Docs: Tests: Coverage for the Reviewed by Cursor Bugbot for commit 8c8b78b. Bugbot is set up for automated code reviews on this repo. Configure here. |
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>
90bb5ef to
95ac829
Compare
3c0fa64 to
e35fa92
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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.
ensureSliceWorktree early-returned on existsSync, bypassing the collision guard in seedSliceFromParentWorktree. A slice id matching a tracked parent path (e.g. `src`) resolved to the project source dir, which cook then adopted as the slice sandbox — silently breaking per-slice isolation. Only treat an existing path as provisioned when it is a real git worktree (own `.git`). 🍳 Built with brunch Co-Authored-By: Opus 4.8 <noreply@anthropic.com>


Stack Context
Stacks on FE-864 (#212). 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?
wireHandlersloop intoresolveSliceCwd, via a new idempotentensureSliceWorktree. 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 twogit worktree addcalls overlap under the parallel policy.node_modules/to the parent worktree's single copy instead of CoW-copying it per slice (SHAREABLE_TOP_LEVEL_ENTRIES).walkFilesalready 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 verifygreen. New unit tests: slicenode_modulesis a symlink (not a copy), other gitignored content still copies,ensureSliceWorktreeidempotent. Brownfield integration smoke unchanged.