FE-878: Brunch serve — one-shot plan-then-cook capstone#222
FE-878: Brunch serve — one-shot plan-then-cook capstone#222kostandinang wants to merge 13 commits into
Conversation
e9a6f5e to
cd5ef22
Compare
593bf7f to
c4101d7
Compare
cd5ef22 to
e7a2007
Compare
dabcac5 to
d8f61cd
Compare
e7a2007 to
b59e71f
Compare
d8f61cd to
7c7bfdf
Compare
b59e71f to
58d7775
Compare
PR SummaryMedium Risk Overview Presentation seam (I136-K): plan, cook, and Adds Reviewed by Cursor Bugbot for commit 89e7850. Bugbot is set up for automated code reviews on this repo. Configure here. |
58d7775 to
d8ee639
Compare
90bb5ef to
40a9d88
Compare
Dependency ReviewThe following issues were found:
Vulnerabilitiespackage-lock.json
License Issuespackage-lock.json
OpenSSF ScorecardScorecard details
Scanned Files
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 40a9d88. Configure here.
d8ee639 to
a0ebbda
Compare
40a9d88 to
05b471a
Compare
Amp-Thread-ID: https://ampcode.com/threads/T-019ecb9a-9a08-733b-833d-76885fc8243a Co-authored-by: Amp <amp@ampcode.com>
runCook reads opts.dir raw — the launch-cwd default lives only in parseCookArgs, which serve bypasses. With dir:'' cook resolved the just-emitted plan path against process.cwd() and would clone '' for brownfield, so serve only worked when greenfield and process.cwd()===launchCwd===project root. serveCookOptions/runServe now take the resolved cook dir; cli passes launchCwd (the same dir plan writes the plan to). Corrects the stale test that asserted cook.dir===''. Amp-Thread-ID: https://ampcode.com/threads/T-019ecb9a-9a08-733b-833d-76885fc8243a Co-authored-by: Amp <amp@ampcode.com>
- I126-K: name the shared runVerification seam (FE-872); evaluateVerificationTargets is deleted - decision 166: brownfield promotion is no longer a follow-on — landed as decision 168 (FE-877) - decision 168 (new): brownfield auto-promotion (plumbing-only) + brunch serve capstone (FE-877/878) - I128-K: --out is greenfield-only (brownfield auto-promotes) - I135-K (new): brownfield promotion never touches the user's checkout Amp-Thread-ID: https://ampcode.com/threads/T-019ecb9a-9a08-733b-833d-76885fc8243a Co-authored-by: Amp <amp@ampcode.com>
The serve and plan branches duplicated the spec gate verbatim (resolveBrunchProject -> createDb -> existence check -> snapshot -> completeness assert -> db close + uniform error). withCompletedSpec now owns it; parsing is a thunk so parse errors report through the same 'Failed to run brunch <command>' channel. Pure refactor — cli.test.ts green. Amp-Thread-ID: https://ampcode.com/threads/T-019ecb9a-9a08-733b-833d-76885fc8243a Co-authored-by: Amp <amp@ampcode.com>
Introduces a single emit(CookEvent) presentation boundary so terminal
output stops being smeared across console.error/log() in the orchestrator
CLI. Foundation: presenter.ts root + presenter/{events,bus,select,plain,
silent}.ts.
- selectPresenter(command,isTTY,ci,reporterFlag): pure decision table →
plain (CI/non-TTY/default) | silent (agent, keeps stdout JSONL-clean) |
ink (interactive TTY; falls back to plain until slice 2).
- CookBus: synchronous fan-out; a thrown presenter is downgraded to a
process warning so presentation can never abort a run.
- PlainPresenter: CookEvent → stderr, byte-exact for the plan arms; sink
injectable for the golden differential.
- plan-runner migrated to emit CookEvents; cli.ts plan/serve wired through
createCookBus. cook left untouched (still behavior-preserving).
Oracle per SPEC I136-K: plan-runner.test.ts now drives a capturing bus and
asserts the same stderr; npm run verify green.
Slice 1b (cook surface + injected-clock elapsed timer) queued in
memory/CARDS.md.
Co-Authored-By: Claude <noreply@anthropic.com>
Routes cook/serve terminal output through the emit(CookEvent) boundary,
completing the seam across all three commands.
- cook-cli: banner / completion summary / promotion / petrinaut blocks and
the early-exit diagnostics now emit {kind:'line'} through the bus; the
petrinaut-setup log is bus-backed. runCook takes a bus (defaults to
createCookBus('cook')); serve shares one bus across plan+cook.
- pi-actions: per-action log()/logVerbose() become structured action/
verbose CookEvents; the module is now console-free. The module-level
Date.now() elapsed timer is gone — the presenter owns it.
- PlainPresenter: gains an injected clock (I136-K). A cook-start event
seeds runStart; the elapsed prefix is computed at render time, so the
cook surface now has a deterministic byte-exact golden.
Verified: presenter goldens (plan + cook arms incl. fake clock),
brownfield-smoke runs cook end-to-end through the bus, npm run verify green.
ink still falls back to plain — that's slice 2.
Co-Authored-By: Claude <noreply@anthropic.com>
Makes the `ink` backend real (it no longer falls back to plain on a TTY).
- format.ts + clock.ts: line formatting + the elapsed clock extracted from
PlainPresenter so the plain and Ink backends share one formatter and can't
drift. PlainPresenter is now a thin sink over formatCookEvent.
- phase.ts: nextPhase — a pure, monotonic brigade tracker
(prep→recipe→cook→taste→plate→serve) projected from the event stream.
Coarse for now (post-hoc events); precise in-flight transitions are 2b.
- run-store.ts: folds CookEvents into { phase, lines } with a stable
snapshot for useSyncExternalStore.
- ink/: egg-logo.ts (ANSI mark), app.tsx (egg header + brigade strip +
bounded activity log), ink-presenter.tsx (renders to STDERR; stdout stays
reserved). makePresenter('ink') now returns InkPresenter.
Adds ink@^7 + ink-testing-library@^4 (React 19.2 satisfies the peer dep).
Verified: phase/run-store units, ink-testing-library frame (egg + active
phase + activity line), non-TTY path still plain (brownfield-smoke), full
build bundles the tsx. Real-terminal walkthrough is outer-loop debt; the
dead-air waiting fix is slice 2b.
Co-Authored-By: Claude <noreply@anthropic.com>
Closes the dead-air problem: long waits now show what brunch is doing. - events: activity-start / activity-progress / activity-end. - pi-actions: runPi self-brackets every agent session (start → finally end) with a throttled KB heartbeat off its token stream; the test-run and probe waits bracket via a small withActivity helper. All close in finally, so a spinner can't hang — covered by a test that fails the session mid-wait. - cook-cli: promotion brackets via a `promoting` helper. - run-store: a pending map (start adds, progress updates detail, end removes); activity events stay out of the scrolling log. - ink: PendingPanel renders a live spinner + label + elapsed + detail, with a tick interval that runs only while something is pending. Plain/CI prints one `⋯` start line per wait. Known limit: test-runner uses blocking spawnSync, so the spinner freezes (but stays labeled) during a test run; the async pi session animates. Real-terminal walkthrough is outer-loop debt. Verified: run-store pending units, ink frame (panel shows/clears), balanced brackets incl. on session failure, npm run verify green. Co-Authored-By: Claude <noreply@anthropic.com>
ln-review caught that nothing ever called bus.dispose() — harmless for plain/silent, but on a real TTY the Ink app was never unmounted, so `brunch cook`/`serve` would hang after the run. - withCookBus(command, fn): builds the bus, runs the work, and disposes it (→ unmounts Ink) in finally. One owner, no split ownership. - runCook takes a required bus (drops the in-cook createCookBus default). - cli.ts cook/plan/serve paths run through withCookBus; serve's single shared bus is disposed once after the cook stage. Verified: withCookBus disposes on success and on throw (spy); CookBus.dispose fan-out test stands; npm run verify green. Remaining real-terminal debt is now purely visual. Co-Authored-By: Claude <noreply@anthropic.com>
) The cook banner/summary text had no oracle (the migration preserved it verbatim but nothing guarded against drift). Extract cookBannerLines / cookSummaryLines as pure functions and golden-test them; runCook feeds their output to the bus. Covers completed + halted runs incl. the epic/slice tree. npm run verify green. Co-Authored-By: Claude <noreply@anthropic.com>
…arks Per feedback: drop the egg, use the "brunch" wordmark tinted with the brunch.ai brand gradient (HASH blue→indigo→violet, one hex per letter), and keep the brigade/status glyphs as the original monochrome marks (✓ ◐ ○) rather than emoji. egg-logo.ts → wordmark.ts. Plain/CI backend stays untinted. Ink frame tests updated. Co-Authored-By: Claude <noreply@anthropic.com>
The panel re-rendered every 120ms recomputing toFixed(1) elapsed, so the number jittered at the decimal. Add formatElapsed (whole seconds under a minute, m:ss above), use it in the panel, and slow the spinner tick to 250ms. The static action-log prefix (a fixed record) keeps its one-decimal form. Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
05b471a to
89e7850
Compare
a0ebbda to
17ae900
Compare


Stacks on FE-877. The Arc-1 capstone -- one shot from a completed spec to a promoted cook result, no manual steps (closes Arc 1) -- plus the CLI presentation seam the cook/plan/serve surfaces now render through.
What?
Serve capstone.
brunch serve <specId>=brunch plan <specId>thenbrunch cook --spec=<specId>.runServe,serve-runner.ts): emit the plan, then cook it. Cook reads the plan just emitted (--spec=<id>), not an auto-picked older one. A failed plan short-circuits -- nothing is cooked.parseServeArgs/serveCookOptions): serve's--outis the greenfield promote target -> cook (brownfield auto-promotes via FE-877 regardless);--profilestamps the plan; petrinaut /--policy/--max-retriesforward to cook;--verboseto both. Launch cwd is threaded into cook's stage.cli.tsserve branch reuses the existing plan db/snapshot wiring (spec existence + completeness), then hands the two stages torunServe. A shared completed-spec gate is extracted for plan + serve.Presentation seam. Plan and cook output migrate off ad-hoc
console.erroronto an event-driven presenter (presenter/).selectPresenter(command, isTTY, ci, --reporter)picks the renderer:ink(interactive full-screen TUI),plain(line-oriented for CI / non-TTY / piped),silent(keeps stdout clean for theagentJSONL protocol).Why?
The Arc-1 chain (detect -> plan -> cook -> verify -> promote) was already built across FE-867..877; serve is the no-manual-steps entry point -- pure glue, the heavy side effects already live in
runPlan/runCook, and the two stages are injected so sequencing + flag mapping stay unit-testable. The presenter seam replaces scattered stdout writes with one testable decision (renderer choice is pure) so plan/cook/serve share consistent output and CI vs TTY vs agent each get the right surface.Command surface
serveis one of the three real commands --brunch plan,brunch cook,brunch serve. The kitchen-brigade names (prep / recipe / taste / plate) are phase labels, not commands: detect runs insideplan; verify (probe + oracle) and promotion run insidecook.servechains those phases end to end -- no new verbs beyond itself.Tests
parseServeArgs/serveCookOptions/runServe(plans-then-cooks, fresh-plan read,--outmapping, failing plan never cooks);selectPresenterenv matrix; plain + Ink presenter rendering; bus lifecycle teardown; golden tests pinning the cook banner + summary strings. Includes a SPEC reconcile of Arc-1 invariant drift.Co-authored-by: Amp amp@ampcode.com