Skip to content

Commit 977ea9c

Browse files
kostandinangclaudecursoragent
authored
FE-841: Pi agent foundations β€” in-process SDK, toolchain profiles (#194)
* FE-841: Embed pi as an in-process SDK to drop the external pi CLI dependency Replace the spawn('pi', …) subprocess in pi-actions.ts with an in-process createAgentSession over @earendil-works/pi-coding-agent (pinned 0.79.1), so a fresh checkout no longer needs an external pi binary on $PATH β€” only ANTHROPIC_API_KEY. - buildSessionOptions maps the old CLI flags to SDK config: tools allowlist, model-registry lookup, system-prompt override, in-memory session + empty resource overrides (no context files). - Auth fed from brunch's own ANTHROPIC_API_KEY (no pi login / ~/.pi/auth.json); one reused per-process agent dir, no per-call temp leak. - Output captured off text_delta events (never brunch's stdout, keeps the cook SSE stream clean); cooperative timeout + 10MB output cap. - Read-only evaluator scoping (I126-K) preserved via the SDK tools allowlist; injectable SessionFactory keeps the drive loop offline-testable. - Drops the `which pi` prereq from the cook guide (npx pi for the interim helper). - Override transitive hono to 4.12.25 (patches the moderate advisories the dependency-review gate flags). - PI_REAL_LLM-gated smoke proves a real in-process run with no pi on $PATH. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * FE-841: Harden in-process pi session guards Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: Clean up late pi setup timeouts Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: Tighten pi timeout and output caps Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: pin pi SDK nested vulnerable patches Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: pin root ws to patched release Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: require the pi SDK node floor Co-authored-by: Cursor <cursoragent@cursor.com> * FE-843: Frontier setup β€” toolchain-profile-expansion plan entry + 3-card queue Co-authored-by: Claude <noreply@anthropic.com> * FE-843: Data-driven profile registry + node-vitest/node-test/node-jest/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> * FE-843: Live + strict profile selection β€” flag ≫ spec ≫ bun, persisted 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> * FE-843: Architect classifies the toolchain profile from spec prose 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> * FE-843: reject inherited profile names Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: bump pi SDK to 0.79.8 for patched undici pi-coding-agent 0.79.1 pinned undici 8.3.0, which trips three advisories flagged by the dependency-review check (GHSA-38rv-x7px-6hhq, GHSA-vmh5-mc38-953g, GHSA-pr7r-676h-xcf6). 0.79.8 is the first patch release that depends on the fixed undici 8.5.0. A root override can't reach the SDK's exact-pinned nested undici, so bump the SDK itself. * FE-841: reject failed embedded pi turns Detect SDK-level assistant failures after prompt resolution so cook does not advance past a failed in-process agent run. Co-authored-by: Cursor <cursoragent@cursor.com> * FE-841: tolerate empty plan profiles Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b4312b3 commit 977ea9c

15 files changed

Lines changed: 2780 additions & 161 deletions

β€Ždocs/praxis/orchestration-guide.mdβ€Ž

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ Run `brunch cook` against the brunch repo in codebase (brownfield) mode.
55
## Pre-flight
66

77
```sh
8-
which pi && pi --version # pi >= 0.74
9-
npm run build # dist/ fresh
8+
npm run build # dist/ fresh (bundles the in-process pi SDK)
9+
grep -q '^ANTHROPIC_API_KEY=' .env # cook feeds this to the embedded agent
1010
git status --porcelain --untracked-files=no # must be empty
1111
```
1212

13+
`brunch cook` embeds `@earendil-works/pi-coding-agent` in-process (FE-841), so no external `pi` binary on `$PATH` is required β€” only `ANTHROPIC_API_KEY` in `.env`, which the cook command loads via `--env-file=.env`.
14+
1315
`.brunch/` is already gitignored, so cook artifacts won't appear in `git status`.
1416

1517
## Author the plan
@@ -28,10 +30,12 @@ Run `/ln-scope` on a `memory/PLAN.md` frontier to produce a scope card (Target B
2830

2931
Extract the frontier section and ask pi for YAML:
3032

33+
The `pi` CLI is no longer a global prerequisite, but it still ships inside the bundled dep β€” invoke it with `npx pi` (resolves to `node_modules/.bin/pi`):
34+
3135
```sh
3236
FRONTIER="<id>"
3337
awk "/^### $FRONTIER\$/,/^### /" memory/PLAN.md | head -n -1 > /tmp/f.md
34-
pi -p --no-session --provider anthropic --model claude-haiku-4-5 \
38+
npx pi -p --no-session --provider anthropic --model claude-haiku-4-5 \
3539
--tools "read,write" \
3640
"Translate /tmp/f.md into .brunch/cook/plan.yaml. One epic, one slice per
3741
Acceptance line (max 2). Each slice needs a verification.target pointing
@@ -118,6 +122,6 @@ Periodic stragglers: `git worktree prune` + `git branch --list 'cook*' | xargs -
118122

119123
## Known limitations
120124

121-
- **Pi evaluator may short-circuit.** Pi has `read,write,edit,bash` even during `evaluate-done` and may fix the file during evaluation rather than going through write-tests β†’ write-code β†’ run-tests. Non-deterministic.
125+
- **Agent runs in-process.** Cook drives the agent via the embedded `@earendil-works/pi-coding-agent` SDK (FE-841), not a spawned `pi` CLI. `evaluate-done` is scoped to a read-only tool allowlist (`toolsForAction('evaluate-done')`), so the evaluator cannot fix the file during evaluation (I126-K); `done` comes from executing the verification targets, not an LLM verdict.
122126
- **No commit on the cook branch.** Modification is in untracked subdirs of the cook branch's worktree, not committed. Promotion is manual `cp -R`.
123127
- **Plan vs frontier mismatch.** `.brunch/cook/plan.yaml` is orchestrator runtime, not planning vocabulary. `/ln-scope` or pi-assisted translation is the bridge.

β€Žmemory/PLAN.mdβ€Ž

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ The May 2026 intent-spec, multi-chat, changeset-ledger, prompt/context, and agen
3333
6. `cook-mode-from-spec` β€” **FE-826**; **branch-complete on `ka/fe-826-cook-mode-from-spec`** (stacked on FE-819). Greenfield/brownfield is now spec-derived plan truth: `brunch plan <specId>` writes `specification.mode` into `plan.yaml` (`Plan.mode`); `brunch cook` reads `plan.mode` to pick the worktree strategy β€” greenfield β†’ empty worktree (generate from scratch), brownfield β†’ cwd clone + clean-tree gate. Resolver split into `resolveCookPlan` (path-only) + `resolveSandboxPlan` (mode-driven). Cook `dir` arg is now optional (defaults to cwd). Reverses the location-keyed reading of Requirement 50, refines I123-K, adds D164-K. Refinement on `cook-codebase-mode` (done) + `spec-to-cook-plan` (FE-800).
3434
7. `cook-greenfield-single-tree` β€” **FE-827**; **branch-complete on `ka/fe-827-greenfield-single-tree`** (stacked on FE-826). Cook slice layout is now policy-selected (D165-K): serial greenfield runs one shared accreted tree (verify in place, no `__epic__`); parallel greenfield isolates per-slice + `__epic__` merge (race-safe); brownfield unchanged. Adds greenfield promotion-back (D166-K, I128-K): `--out`/`--force`, completed-gated, commit-on-branch, never silent β€” serial copy-commits the run tree, parallel commits a whole-plan merge (order-wins, collisions reported). Closes the cook output-promotion gap for greenfield; brownfield promotion stays a follow-on. New seams: `promote-run.ts`, `mergeCompletedSlicesIntoTree`, `promotionSourceDir`. LLM conflict reconciliation β†’ Horizon `parallel-merge-conflict-reconciliation`.
3535
8. `plan-build-architect` β€” **FE-829**; **active, building on `ka/fe-829-build-architect`** (stacked on FE-827, draft PR #185). Evolves `brunch plan` from a sequencer into a build-architect emitting fixture-quality, cook-executable `plan.yaml` (target shape = the three reference fixtures). Slice 1 (**done** 2026-06-09): self-contained `PlanContract` (`checkPlan` base/emitted profiles + `repairPlan`) + shared `plan-graph` Kahn helper + `Toolchain` descriptor in the emitter β€” synthesizes the multi-slice-epic integration seam, closing FE-800's integration-blind / "green checks, no artifact" gap (D167-K, I129-K; no LLM, no D160-K change). Slice 2 (**done** 2026-06-09): toolchain-agnostic cook prompts + runner β€” `ToolchainTestRunner` + toolchain-driven cook task builders + de-hardcoded `test-writer.md`, resolved from `plan.profile` (I130-K). Slice 3 (**done** 2026-06-09): build-architect LLM stage β€” the planner prompt now carries each slice's acceptance criteria, spec relation hints projected into slice-id space (`projectPlanningContext`), and the three reference fixtures inlined as comment-stripped few-shot exemplars; output schema unchanged (classify/group/order the existing `req-*` slices only). Slice 4A (**done** 2026-06-09): file-ownership contract β€” `Slice.writes?: string[]` + single-writer-per-file `checkPlan` finding (`file-write-conflict`, design-class warning, never auto-repaired) + the D160-K coordination-file-layout-namespace amendment (I132-K). Slice 4B (**done** 2026-06-09): the architect now AUTHORS the slice set β€” `architectPlan` (new schema: authored slices + `writes` + `derivedFrom`) β†’ `materializeArchitectedPlan` (deterministic normalize + requirement-provenance coverage sidecar) β†’ `repairPlan` + `checkPlan` (emitted + generalized coverage), with a deterministic projection fallback when authoring throws/parses-malformed/leaves a requirement uncovered (I133-K; supersedes the slice-3 `planExecutionOrdering` enrichment stage on the mainline). Slice 5 (**done** 2026-06-09): eval harness β€” `evaluatePlanShape` (`plan-eval.ts`), a deterministic outer-loop acceptance oracle (narrow `verdict` gate + graded structural-feature metrics scored against the abstract fixture-design principles, no id/path/count overfit); the 3 reference fixtures are its self-test and were refreshed (`writes` on every slice + the missing integration seam on the `core` / `pipeline` epics of two of them) to score `overall === 1` (I134-K). Post-slice-5 cleanup (**done** 2026-06-09): retired the dormant `planExecutionOrdering` β€” deleted `plan-llm-planning.ts` (+ test), relocated the surviving `PlanningEnrichment` type into `plan-reconciliation.ts`, consolidated the duplicate `RunModel` onto `plan-architect.ts` (I131-K retired). Opt-in real-LLM eval smoke (**done** 2026-06-09): `plan-emitter.test.ts` env-gated block (`PLANNING_REAL_LLM=1` + `ANTHROPIC_API_KEY`, `it.skip` otherwise) runs the production architect end-to-end on the `brunch-graphs-snapshot.json` fixture and asserts `evaluatePlanShape(plan).verdict === 'accept'` β€” restoring the opt-in real-LLM coverage lost with `plan-llm-planning.test.ts` at the I134-K gate. **Branch-complete** (slices 1–5 + post-slice-5 cleanup + opt-in smoke). Refinement on `spec-to-cook-plan` (FE-800).
36+
9. `toolchain-profile-expansion` β€” **FE-843**; **branch-complete on `ka/fe-843-toolchain-profiles`** (stacked on FE-841). Expands the FE-829 toolchain contract so cook builds on more technologies. Card 1 (**done** 2026-06-10): data-driven profile registry β€” profiles are data literals (path templates + argv template + conventions prose) compiled into the unchanged `Toolchain` interface; added `node-vitest`, `node-test`, `node-jest`, `deno`; registry invariants pinned enumerably. Card 2 (**done** 2026-06-10): live + strict selection β€” `brunch plan --profile=<id>` (validated via `parseProfileId`), emitter resolves the chain once and stamps the id on both authored and fallback plans, `resolveToolchain` throws `UnknownProfileError` on unknown ids (absent stays lenient for fixtures). Card 3 (**done** 2026-06-10): architect-schema profile enum β€” chain is flag ≫ `snapshot.profile` ≫ architect-classified-from-spec-prose ≫ `bun`; a hallucinated profile fails schema parse β†’ deterministic fallback (D160-K intact). No harness install verb (the cook agent scaffolds per A98) β€” assumption pending outer-loop validation (greenfield cook smoke `--profile=node-vitest`). I130-K refinement + agent-install assumption β†’ SPEC at ln-sync tie-off. Deferred follow-ons: fail/infra test-outcome split, brownfield drift-check at cook open, `brunch detect`. Refinement on `plan-build-architect` (FE-829).
3637

3738
### Recently Completed
3839

@@ -349,6 +350,20 @@ The May 2026 intent-spec, multi-chat, changeset-ledger, prompt/context, and agen
349350
- **Traceability:** Requirements 46–50; A97, A100-K; D158-K, D160-K (amended in slice 3), D161-K, D167-K; I129-K, I130-K; Future Direction Β§Cook plan generation. Refinement on `spec-to-cook-plan` (FE-800) β€” resolves its integration-blind follow-on. Builds on FE-826, FE-827.
350351
- **Design docs:** `docs/design/orchestrator.md`; `docs/design/orchestrator-demo-fixtures.md` (reference fixtures); `docs/next/architecture/plan-graph-petri-orchestration.md`.
351352

353+
### toolchain-profile-expansion
354+
355+
- **Name:** Toolchain profile expansion β€” more TS runtimes + live/strict profile selection
356+
- **Linear:** FE-843 Β· branch `ka/fe-843-toolchain-profiles` (stacked on FE-841)
357+
- **Kind:** structural (selection decision + I130-K refinement); cards 1/3 bounded
358+
- **Status:** branch-complete (2026-06-10) β€” all 3 cards landed (1: data-driven registry + 4 TS profiles; 2: selection live + strict; 3: architect classifies profile from spec prose); card queue retired. SPEC promotion (I130-K refinement + agent-install assumption) rides with ln-sync at PR tie-off. Outer-loop validation outstanding: greenfield cook smoke `--profile=node-vitest` (conventions-prose oracle + A98 install assumption).
359+
- **Objective:** Expand the FE-829 `Toolchain` contract (`project-profile.ts`) so cook builds on more technologies: profiles as data literals compiled into the existing `Toolchain` interface (consumers untouched); profile resolved once at plan time (`--profile` flag ≫ `snapshot.profile` ≫ architect-classified ≫ `bun`), persisted into `plan.yaml`; cook errors on unknown ids (lenient on absent, mirroring the `checkPlan` base/emitted split). The architect's existing schema-constrained call gains an optional profile enum β€” classification reads projected spec prose only (D160-K intact, no host introspection).
360+
- **Why now / unlocks:** Today both implemented profiles are TypeScript and the selection path is dead (`snapshot.profile` never populated) β€” every cook run silently resolves to bun, and a typo'd profile id silently defaults rather than erroring. Without live selection, adding profiles changes nothing at runtime; without more profiles, "brunch builds on different technologies" is false in practice.
361+
- **Design (ln-design 2026-06-10):** flat data registry (A) over composed axes (B β€” deferred until real combo demand; retreat is cheap since the consumer surface never changes); no harness install verb β€” the cook agent scaffolds + installs per A98 (pressure trigger: first profile where the runner fails for missing install). Deferred follow-ons: fail/infra test-outcome split in `ToolchainTestRunner`, brownfield drift-check at cook open (path-neutral by type), `brunch detect` CLI convenience. Durable end-state for selection: the spec interview captures stack as a structured field (the D164-K `mode` pattern); the chain already reserves that rung.
362+
- **Acceptance:** per cards in `memory/CARDS.md`.
363+
- **Verification:** inner β€” `project-profile.test.ts` (registry invariants + command shapes), `plan-runner.test.ts` (flag), `plan-emitter.test.ts` (stamping + chain precedence + fallback), `cook-cli.test.ts` (strict/lenient); middle β€” contract/eval suites unchanged; outer (follow-on, non-gating) β€” greenfield cook smoke `--profile=node-vitest` as the conventions-prose oracle.
364+
- **Traceability:** Requirements 46–50; A98, D160-K, D164-K (pattern), D167-K; refines I130-K (resolved profile persisted; strict-on-unknown). New assumption on build: agent-side install suffices for node profiles. Refinement on `plan-build-architect` (FE-829).
365+
- **Design docs:** `docs/design/orchestrator.md`; SPEC Β§Future Direction Cook plan generation.
366+
352367
### petrinaut-colour-fold
353368

354369
- **Name:** Petrinaut export β€” colour-fold per-slice subnet

0 commit comments

Comments
Β (0)