FE-755: Cook codebase-mode β brownfield resolver for brunch cook#155
FE-755: Cook codebase-mode β brownfield resolver for brunch cook#155kostandinang wants to merge 1 commit into
brunch cook#155Conversation
`brunch cook <dir>` now runs against an existing repo. When `<dir>/.brunch/cook/plan.yaml` exists and the working tree's tracked files are clean, cook initializes the sandbox via `git worktree add <sandboxDir> -b cook/<runId> HEAD` from the source repo, per-slice worktrees are seeded by file-copy from the parent (excluding `.git`, sibling slice dirs, and `__epic__/`), and pi-actions run unchanged against pre-existing code. The source branch in `<dir>` stays byte-identical. Also consolidates cook's filesystem footprint from `<cwd>/.cook/` to `<cwd>/.brunch/cook/` so all cook state lives under the existing `.brunch/` workspace convention. Acceptance criteria covered: - Resolver replaces the "not yet implemented" early-exit at cook-cli.ts:65-70 with a pure `resolveCookMode(dir)` discriminated union (fixture / codebase / error). - Clean-tree gate refuses brownfield runs with uncommitted tracked changes (`git status --porcelain --untracked-files=no`). - `createSandbox` gains a `CreateSandboxOptions` discriminated union; codebase mode invokes `git worktree add` on branch `cook/<runId>`. - `seedSliceFromParentWorktree` populates per-slice dirs from the parent worktree contents. - `OrchestratorInput.sandboxMode?: 'fixture' | 'codebase'` threads the mode through to net-compiler. - `pi-actions.ts` unchanged. Existing greenfield fixture-mode tests pass; new tests in cook-cli.test.ts (resolveCookMode), worktree.test.ts (codebase-mode createSandbox), epic-sandbox-merge.test.ts (seedSliceFromParentWorktree), plus a tmpdir+fake-actions brownfield-smoke.integration.test.ts that pins source-byte-identical isolation. Total: 1447/1447 tests pass. Known follow-ons (out of scope for this PR): - pi-actions evaluator currently gets full write tools, collapsing the TDD-shaped workflow when pi can satisfy the slice during evaluation. - No "promote cook artifact back" story β modification lives in untracked subdirs of the cook branch's worktree, not as commits. - Multi-slice brownfield over-copy (TODO in `seedSliceFromParentWorktree`) β slice 2's 1-slice fixture didn't exercise it. A 2026-05-26 spike evaluated `@ai-hero/sandcastle` for hybrid adoption. Technically viable (built-in pi provider, decoupled worktree primitives, noSandbox available) but deferred until sandcastle ships 1.0 or multi-slice over-copy becomes a measurable bottleneck. A 2026-05-26 outer-loop smoke against a tmpdir git repo with real pi confirmed in-place file modification and source-byte-identical isolation end-to-end. Updates SPEC Β§D50, Β§A49, Β§I123-K, Β§Lexicon (path consolidation) and PLAN.md `cook-codebase-mode` frontier definition (now Recently Completed with three follow-on findings). Co-Authored-By: Claude <noreply@anthropic.com>
brunch cook.brunch cook
PR SummaryMedium Risk Overview Sandbox layout moves under Per-slice brownfield seeding: Tests cover resolver gates, worktree branch/isolation, seed exclusions, and a tmpdir brownfield smoke integration run. Reviewed by Cursor Bugbot for commit fa4ff77. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
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. |
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 fa4ff77. Configure here.
|
|
||
| return sliceDir; | ||
| } | ||
|
|
There was a problem hiding this comment.
Dep seed wipes brownfield base
High Severity
In codebase mode, slices are pre-filled from the parent worktree, but every action still runs seedSliceSandboxFromDeps with preserveExisting. When a slice has depends_on and dependency worktrees exist, that helper deletes every file in the slice sandbox that is not also present in a dependency tree. Most of the brownfield checkout is removed before agents run, so dependent brownfield slices cannot modify the existing repo as intended.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit fa4ff77. Configure here.
π€ Augment PR SummarySummary: This PR implements βcodebase modeβ for Changes:
Technical notes: Codebase mode checks out a separate worktree branch ( π€ Was this summary useful? React with π or π |
| mkdirSync(sliceDir, { recursive: true }); | ||
|
|
||
| const excludedNames = new Set<string>(['.git', EPIC_MERGE_SEGMENT]); | ||
| for (const s of plan.slices) excludedNames.add(s.id); |
There was a problem hiding this comment.
seedSliceFromParentWorktree assumes slice IDs wonβt collide with real top-level repo entries, but in codebase mode the parent worktree is a full checkout β if a slice id matches an existing dir (e.g. src), the seed will skip that dir and/or copy repo-root files into it. Consider adding a guard (or reserving a dedicated slice root dir) to prevent slice-id/name collisions from corrupting the seeded worktree.
Severity: high
π€ Was this useful? React with π or π, or π if it prevented an incident/outage.
| for (const slice of plan.slices) { | ||
| mkdirSync(resolveSliceWorktreeDir(input.sandboxDir, slice.id), { recursive: true }); | ||
| if (input.sandboxMode === 'codebase') { | ||
| seedSliceFromParentWorktree(input.sandboxDir, slice.id, plan); |
There was a problem hiding this comment.
In codebase mode you seed the slice dirs up-front here, but later calls to seedSliceSandboxFromDeps(..., { preserveExisting: true }) still delete any files not present in dependency slices when depFiles.size > 0, which can wipe slice-local outputs between actions for slices with depends_on. That seems likely to break multi-step slices with dependencies (tests written in write-tests disappearing before write-code / run-tests).
Severity: medium
π€ Was this useful? React with π or π, or π if it prevented an incident/outage.



TL;DR.
brunch cook <dir>now runs against an existing repo. When<dir>/.brunch/cook/plan.yamlexists and the working tree's tracked files are clean, cook initializes the sandbox viagit worktree add <sandboxDir> -b cook/<runId> HEADfrom the source repo, per-slice worktrees are seeded by file-copy from the parent (excluding.git, sibling slice dirs,__epic__/), and pi-actions run unchanged against pre-existing code. Source branch stays byte-identical. Also consolidates cook's filesystem footprint from<cwd>/.cook/to<cwd>/.brunch/cook/.What changed
Codebase-mode resolver
resolveCookMode(dir): ResolvedCookModeβ pure discriminated union returning{mode: 'fixture' | 'codebase' | 'error'}. Replaces the "not yet implemented" early-exit atcook-cli.ts:65-70.git status --porcelain --untracked-files=no) refuses brownfield against an uncommitted working tree; untracked files (e.g., the user's freshly-authored.brunch/cook/plan.yaml) don't trip the gate.<dir>is not a git repo.Worktree lifecycle
createSandboxextended withCreateSandboxOptions = { mode: 'fixture' } | { mode: 'codebase'; sourceDir: string }.git worktree add <sandboxDir> -b cook/<runId> HEADfrom cwd of the source repo; fixture mode unchanged (empty mkdir).OrchestratorInputgains an optionalsandboxMode: 'fixture' | 'codebase'threaded through tonet-compiler.ts.Per-slice worktree population
seedSliceFromParentWorktree(parentSandboxDir, sliceId, plan): stringβ file-copies parent worktree contents into the slice dir, excluding.git, sibling slice subdirs, and__epic__/.net-compiler.tsbranches oninput.sandboxMode === 'codebase'at slice-dir creation.Path consolidation (
.cook/β.brunch/cook/).brunch/cook/runs/<runId>/worktree/(matching the existing.brunch/workspace convention).docs/design/orchestrator.md, and.gitignore(the.brunch/ignore already covered the new paths; redundant.cook/line removed).pi-actions.tsunchanged.Why now
Per SPEC Β§D50, codebase mode was reserved at the resolver level but not implemented. Without it,
brunch cookcould only run greenfield fixtures β agents generate code from scratch in fresh worktrees. The orchestrator stayed a fixture-only substrate even after Petri Phases 0β2 (FE-730, FE-738, FE-743, FE-745) and declarative routing (FE-747) landed. Codebase mode is the smallest step from "orchestrator-as-substrate" to "orchestrator-as-product."Verification
cook-cli.test.ts(resolveCookMode β 5 tests),worktree.test.ts(codebase-mode createSandbox β 3 tests),epic-sandbox-merge.test.ts(seedSliceFromParentWorktree β 5 tests). Existing greenfield-mode tests unchanged.brownfield-smoke.integration.test.tsβ tmpdir + real git + fake actions. Asserts source branch byte-identical, modification landed in slice worktree, parent worktree oncook/<runId>. (fixtures/brownfield-smoke/lives as a test-setup function rather than a committed directory because nested.git/inside the brunch repo creates submodule weirdness.)npm run verifygreen β 123 test files, 1447 tests pass.subtractbug confirmed in-place file modification (pi edited the existing line, did not overwrite) and source-byte-identical isolation. Took 1m 4s wall time.Out of scope
Three follow-ons surfaced during the outer-loop smoke and review, all explicitly deferred:
pi-actions.ts:70passes--tools read,write,edit,bashto every action includingevaluate-done. Real pi fixed the buggy file during evaluation and reporteddone: trueon the first call, collapsing the intended TDD-shaped flow (evaluate β write-tests β write-code β run-tests β evaluate). Affects both modes but more visible in brownfield. Worth its own frontier.cook/<runId>) has HEAD === source HEAD; the modification lives in two untracked subdirs of the cook branch's working tree (<runDir>/worktree/<sliceId>/and<runDir>/worktree/__epic__/<epicId>/). Nogit merge cook/<runId>story today. Pairs with worktree + branch GC. Worth acook-artifact-lifecyclefrontier.seedSliceFromParentWorktree. Mitigation: realgit worktree addper slice off the run-level cook branch, or diff-based epic merge.Sandcastle adoption deferred. A 2026-05-26 spike evaluated
@ai-hero/sandcastlefor hybrid adoption. Technically viable (built-inpiagent provider, decoupled worktree primitives,noSandbox()available without Docker requirement) but pre-1.0 with non-trivialeffectruntime deps. Revisit when sandcastle ships 1.0 or when multi-slice over-copy becomes a measurable bottleneck.Traceability
Requirements 46β50; SPEC Β§D50 (codebase-mode resolver), Β§A49 (worktree isolation), Β§I123-K (codebase mode preserves source-byte-identity); frontier
cook-codebase-modeinmemory/PLAN.md(nowRecently Completedwith the three follow-on findings captured). Predecessor: FE-747 (declarative routing) β base of this stack.