Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions memory/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,22 @@ Brunch-next has delivered the original composition spine: the host, sealed Pi pr

### orchestrator-cutover — ◐ active

- **Goal:** re-grow the old `main` cook orchestrator natively on alpha's CODE/executor substrate (D101-L), layer by layer: projection seam → descriptive lifecycle shape → real runnable sandbox → real change-producing agent → real promotion/land. Split by capability layer + risk + reversibility so each layer is independently reviewable and the hard-to-reverse git seam lands last.
- **Goal:** re-grow the old `main` cook orchestrator natively on alpha's CODE/executor substrate (D101-L), layer by layer: projection seam → descriptive lifecycle shape → real runnable sandbox → real change-producing agent → real promotion/land → driven end-to-end (orchestrate loop). Split by capability layer + risk + reversibility so each layer is independently reviewable and the hard-to-reverse git seam lands last.
- **Members:**
- `orchestrator-alpha-cutover` (FE-1089) ✓ done — `ExecutionSpecSnapshot` projection seam + the descriptive `fs`-only cook lifecycle scaffold (`execute_plan_file` → … → `execute_promotion_prepare`). Proved the lifecycle shape + thin-adapter/one-side-effect-per-tool pattern with zero real execution.
- `executor-sandbox` (FE-1109) ✓ built — `GitWorktreePort` + `TestRunnerPort`: a run becomes a real, runnable, verifiable git workspace (no LLM, subprocess only).
- `executor-agent-runner` (FE-1111) → active — `AgentRunnerPort` reusing the D90-L–D93-L sealed subagent substrate: a run actually produces real changes via a code-owned write-capable CODE worker.
- `executor-promotion` → last — `GitLandPort`: a run's real changes get promoted (run-local promotion first, host promotion later); the only externally-visible, hard-to-reverse seam.
- **Done-definition:** a selected-spec cook run can be planned, executed against a real git worktree by a real CODE worker that produces real diffs, verified by real tests, and promoted — each layer behind the established injected-capability-port seam (SPEC D101-L executor cutover), no faked side effects, topology immutable in execution, and `execute_status` `pendingTools` empty. Open follow-ups (adaptive replan, real Petri-net execution) ride their own horizon items, not arc blockers.
- `executor-orchestrate-loop` (FE-1125) ✓ built — a single `execute_orchestrate` driver composes the lifecycle rungs behind a pure, Petri-ready `RunScheduler` seam (ready-set return; readiness from slice-completion facts, not the global status enum); drives created → run-local `promotion_prepared`. Real Petri scheduler deferred to `geolog-and-petri-execution`.
- **Done-definition:** a selected-spec cook run can be planned, executed against a real git worktree by a real CODE worker that produces real diffs, verified by real tests, and promoted — each layer behind the established injected-capability-port seam (SPEC D101-L executor cutover), no faked side effects, topology immutable in execution, `execute_status` `pendingTools` empty, and the run driven end-to-end by a single `execute_orchestrate` entry behind a swappable scheduler seam (FE-1125). Open follow-ups (adaptive replan, real Petri-net execution) ride their own horizon items, not arc blockers.
- **Anchors:** D39-L, D40-L, D52-L, D90-L–D93-L, D98-L, D101-L / I49-L, I56-L.

## Sequencing

### Active

- `executor-host-promotion` (FE-1118, `orchestrator-cutover` arc) — **built; ready for tie-off.** Apply a verified run-local executor promotion back to the host project branch through an explicit host-promotion path. Preflight validates the promoted SHA and computes/reports the host diff without mutation; accepted apply mutates host files only after accepted SHA confirmation; CODE-mode Pi tools expose both surfaces with side-effect details. Review hardening added real git apply/conflict oracles and tightened the apply result shape. Stacks on `ka/fe-1112-executor-promotion`.
- `executor-orchestrate-loop` (FE-1125, `orchestrator-cutover` arc) — **built; ready to tie off.** `execute_orchestrate` drives a run end-to-end (created → run-local `promotion_prepared`) behind a pure `RunScheduler` seam (ready-set return; readiness from slice-completion facts; scheduler in core), instead of cranking tool-by-tool. Petri stays an export artifact; the real Petri scheduler is the parked `geolog-and-petri-execution` successor. Host promotion stays off the driven chain. Stacks on `ka/fe-1118-executor-host-promotion`.
- `elicitation-gap-guidance` — **proving frontier.** Generate "what next?" gap guidance from graph shape/readiness, distinct from ranking already-registered gaps.

### Recently Completed
Expand Down Expand Up @@ -206,6 +208,26 @@ Brunch-next has delivered the original composition spine: the host, sealed Pi pr
- Done: Review hardening proves the real `git diff --binary` / `git apply --check` / `git apply` path against temp repos, narrows apply result states, and removes the dead report-path alias.
- **Traceability:** D52-L, D99-L / I52-L; depends on `executor-promotion`; `src/executor/TOPOLOGY.md`.

### executor-orchestrate-loop

- **Name:** Executor orchestrate loop — driven run over a scheduler seam
- **Linear:** [FE-1125](https://linear.app/hash/issue/FE-1125/reconcile-executor-run-driver) — reconcile executor run driver
- **Branch:** `ka/fe-1125-executor-orchestrate-loop` (stacked on `ka/fe-1118-executor-host-promotion`)
- **Kind:** structural / execute-mode run driver (`orchestrator-cutover` arc)
- **Status:** built — `execute_orchestrate` drives a run end-to-end (created → run-local `promotion_prepared`) behind the pure `RunScheduler` seam; host promotion stays off-chain. Real-provider end-to-end (real LLM + git) is unexercised — unit slices use fake ports. Ready to tie off.
- **Certainty:** proving.
- **Why now / unlocks:** the cutover arc built every lifecycle rung (real worktree, agent, test, promotion, host-files apply) but a run is still cranked one `execute_*` tool-call at a time; nothing drives the composition end-to-end. This is the `orchestrate` tool D101-L names but that does not yet exist, and the first unattended end-to-end run — it converts the parked follow-ups (recovery, replan, containment) from speculation into observed pressure.
- **Design verdict (chosen, via `ln-design`):** a generic `drive()` loop parameterized by a pure `RunScheduler.ready(state, plan): ReadyStep[]`. `ready()` returns a **set** (length-1 under today's `LinearScheduler`; a future `PetriScheduler` returns N for parallel epics); readiness is derived from per-slice completion facts (`run.json` completedSlices + `reports.jsonl`), not the global `status` enum; the scheduler is pure and lives in executor core, not in the `ExecutionPorts` capability bag. Petri stays an export artifact — real Petri-net execution remains the parked `geolog-and-petri-execution` item. See SPEC D102-L.
- **Objective:** Compose the real `execute_*` lifecycle steps into a single `execute_orchestrate`-driven run behind the scheduler seam, so a run advances itself instead of being cranked tool-by-tool.
- **Acceptance (slice 1; refine via `ln-scope`):**
- `execute_orchestrate` drives a created run + multi-slice plan to `run_completed` in one call, with each slice completed.
- `RunScheduler.ready()` returns a length-1 array each turn and `[]` at completion (locks the set-return contract).
- Readiness selects the pending slice's next step from completion facts, not the global `status` string.
- A failed injected-port step halts the run, leaves status unadvanced, and returns a halt outcome.
- Each `execute_*` step fn is invoked exactly once per run step; driven-run terminal metadata equals the hand-cranked sequence's (no new side effects).
- Promotion/land out of scope this slice (stops at `run_completed`).
- **Traceability:** D101-L, D102-L, D52-L / I56-L; depends on `executor-host-promotion`; optional successor `geolog-and-petri-execution` (real Petri scheduler); `src/executor/TOPOLOGY.md`.

### elicitor-project

- **Name:** Elicitor `project` capability — cross-plane derivation
Expand Down Expand Up @@ -273,6 +295,13 @@ frontiers:
depends_on: executor-promotion, D101-L executor cutover, D52-L, I56-L
stacks_on: ka/fe-1112-executor-promotion

executor-orchestrate-loop (FE-1125, orchestrator-cutover arc)
status: built; drives created -> run-local promotion_prepared; host promotion off-chain
depends_on: executor-host-promotion (FE-1118), D101-L executor cutover, D102-L scheduler seam, D52-L, I56-L
provides: RunScheduler seam + execute_orchestrate driver
optional_successor: geolog-and-petri-execution (PetriScheduler, real Petri-net execution)
stacks_on: ka/fe-1118-executor-host-promotion

executor-agent-runner (FE-1111, orchestrator-cutover arc)
status: done; sealed worker runner and faux-provider witness built
depends_on: executor-sandbox (FE-1109), D90-L..D93-L, D52-L, I49-L, I56-L
Expand Down
1 change: 1 addition & 0 deletions memory/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ The POC's purpose is to prove three things: (a) that pi's coding-agent harness c
| D98-L | Operational mode only: suspend strategy/lens/method runtime axes; target product modes are SPEC and CODE. The architectural correction is that the `strategy` / `lens` / `method` model is not yet proven as the right product/runtime abstraction. It may still organize prompt-resource files and concise agent-readable references, but it must not be a user-facing TUI picker, ... | See archive snapshot for full rationale. | active |
| D100-L | `project` is a distinct first-level live SPEC-mode skill home for cross-plane derivation, not a `generate` sub-mode. `generate` fans out alternatives within a target plane from context; `project` starts from accepted upstream graph anchors and derives downstream plane candidates/drafts plus connecting edge intent. It uses the existing structured-exchange triad (`present_candidates`, `request_response`, `present_review_set`) and hands exact graph expression back to `map` / review-set commitment; it adds no product tool, exchange schema family, or direct graph-write path. Depends on: D95-L, D96-L, D97-L, I51-L. | [`src/agents/skills/TOPOLOGY.md`](src/agents/skills/TOPOLOGY.md), [`src/agents/subagents/TOPOLOGY.md`](src/agents/subagents/TOPOLOGY.md) | active |
| D101-L | Execute orchestration cutover starts with bounded native executor tools, not a revived orchestrator role or shelling out to the old CLI. FE-1089 grows the old cook lifecycle back on alpha as `execute_*` Pi tools over product-core contracts: side-effect-free projection/check/outline/draft/preview tools are active in CODE mode; bounded artifact writers under `.brunch/execution-reports` / `.brunch/cook` plus descriptive run/worktree/source/report/slice/result/Petri/promotion-preparation tools are registered and test-covered but intentionally inactive until the real-execution stack lands, so placeholders are not independently reachable. No real agent/test execution or host git mutation occurs until a later accepted boundary. Depends on: D39-L, D40-L, D58-L, D90-L, D93-L, D98-L. | [`src/executor/TOPOLOGY.md`](src/executor/TOPOLOGY.md), [`src/.pi/extensions/TOPOLOGY.md`](src/.pi/extensions/TOPOLOGY.md) | active |
| D102-L | Executor run driving is a driver over a **scheduler seam**, not baked control flow. A single CODE-mode `execute_orchestrate` tool drives a run through the existing `execute_*` lifecycle steps by repeatedly asking a pure `RunScheduler.ready(state, plan)` for the ready step-set and executing it, folding results into run metadata; the steps and their injected `ExecutionPorts` are unchanged. The scheduler is pure (a decision, not a side effect) and lives in executor core, not in the `ExecutionPorts` bag. Two forward-compat invariants: `ready()` returns a **set** (`ReadyStep[]`, length-1 under today's `LinearScheduler`), and readiness is derived from per-slice completion facts (`run.json` completedSlices + `reports.jsonl`), not the global linear `status` enum — so a later `PetriScheduler` (real Petri-net execution, parked `geolog-and-petri-execution`) drops in without reshaping the loop. Extends the D101-L `orchestrate` tool intent. Depends on: D101-L, D52-L. | [`src/executor/TOPOLOGY.md`](src/executor/TOPOLOGY.md) | active |

### Critical Invariants

Expand Down
5 changes: 4 additions & 1 deletion src/.pi/extensions/__tests__/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
BRUNCH_EXECUTE_HOST_PROMOTION_PREFLIGHT_TOOL,
} from '../agent-runtime/execute-host-promotion/index.js';
import { BRUNCH_EXECUTE_LAUNCH_TOOL } from '../agent-runtime/execute-launch/index.js';
import { BRUNCH_EXECUTE_ORCHESTRATE_TOOL } from '../agent-runtime/execute-orchestrate/index.js';
import { BRUNCH_EXECUTE_PETRI_EXPORT_TOOL } from '../agent-runtime/execute-petri-export/index.js';
import { BRUNCH_EXECUTE_PLAN_CHECK_TOOL } from '../agent-runtime/execute-plan-check/index.js';
import { BRUNCH_EXECUTE_PLAN_DRAFT_ARTIFACT_TOOL } from '../agent-runtime/execute-plan-draft-artifact/index.js';
Expand Down Expand Up @@ -168,6 +169,7 @@ describe('Brunch explicit Pi extension registry', () => {
'web_fetch',
'web_search',
BRUNCH_EXECUTE_STATUS_TOOL,
BRUNCH_EXECUTE_ORCHESTRATE_TOOL,
BRUNCH_EXECUTE_AGENT_RESULT_TOOL,
BRUNCH_EXECUTE_PETRI_EXPORT_TOOL,
BRUNCH_EXECUTE_PROMOTION_PREPARE_TOOL,
Expand Down Expand Up @@ -1702,7 +1704,7 @@ describe('Brunch explicit Pi extension registry', () => {
// EXECUTOR_ALLOWED_TOOL_NAMES; the registered-but-unadmitted plan artifact
// tools are intentionally excluded, and text and details must agree.
expect(result.content[0]?.text).toContain(
'ported tools: execute_status, execute_snapshot, execute_plan_check, execute_plan_outline, execute_plan_draft, execute_plan_preview, execute_plan_file, execute_launch, execute_run_create, execute_worktree_create, execute_populate, execute_source_policy, execute_source_copy, execute_report_init, execute_slice_start, execute_slice_execute, execute_agent_result, execute_test_result, execute_slice_complete, execute_run_complete, execute_petri_export, execute_promotion_prepare, execute_host_promotion_preflight, execute_host_promotion_apply',
'ported tools: execute_status, execute_orchestrate, execute_snapshot, execute_plan_check, execute_plan_outline, execute_plan_draft, execute_plan_preview, execute_plan_file, execute_launch, execute_run_create, execute_worktree_create, execute_populate, execute_source_policy, execute_source_copy, execute_report_init, execute_slice_start, execute_slice_execute, execute_agent_result, execute_test_result, execute_slice_complete, execute_run_complete, execute_petri_export, execute_promotion_prepare, execute_host_promotion_preflight, execute_host_promotion_apply',
);
expect(result.content[0]?.text).toContain('pending tools: none');
expect(result.content[0]?.text).toContain(
Expand All @@ -1713,6 +1715,7 @@ describe('Brunch explicit Pi extension registry', () => {
availableDisciplines: ['strict', 'interpretive'],
portedTools: [
'execute_status',
'execute_orchestrate',
'execute_snapshot',
'execute_plan_check',
'execute_plan_outline',
Expand Down
67 changes: 67 additions & 0 deletions src/.pi/extensions/agent-runtime/execute-orchestrate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { ExtensionAPI, ToolDefinition } from '@earendil-works/pi-coding-agent';
import { Type, type Static } from 'typebox';

import type { ExecutionPorts } from '../../../../executor/execution-ports.js';
import { drive, type DriveOutcome } from '../../../../executor/orchestrate.js';
import { BRUNCH_EXECUTE_ORCHESTRATE_TOOL } from '../../../../session/schema/tool-names.js';

export { BRUNCH_EXECUTE_ORCHESTRATE_TOOL } from '../../../../session/schema/tool-names.js';

const ExecuteOrchestrateParams = Type.Object({
runId: Type.String({ description: 'Run id to drive to completion.' }),
});

type ExecuteOrchestrateParams = Static<typeof ExecuteOrchestrateParams>;

interface ExecuteOrchestrateDetails {
readonly outcome: DriveOutcome;
}

export function createExecuteOrchestrateTool(
ports: ExecutionPorts,
): ToolDefinition<typeof ExecuteOrchestrateParams, ExecuteOrchestrateDetails> {
return {
name: BRUNCH_EXECUTE_ORCHESTRATE_TOOL,
label: 'execute_orchestrate',
description:
'Drive an executor run end-to-end to promotion_prepared (run-local land) by advancing each lifecycle step the scheduler reports ready. Halts without advancing if a step cannot execute. Does not perform host promotion/land.',
parameters: ExecuteOrchestrateParams,
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const cwd = ctx?.cwd;
if (typeof cwd !== 'string' || cwd.trim().length === 0) {
throw new Error('execute_orchestrate requires an active cwd');
}
const outcome = await drive({
cwd,
runId: params.runId,
ports,
runtime: {
...(ctx.modelRegistry ? { modelRegistry: ctx.modelRegistry } : {}),
...(ctx.model ? { model: ctx.model } : {}),
...(_signal ? { signal: _signal } : {}),
},
...(_signal ? { signal: _signal } : {}),
});
return {
content: [
{
type: 'text' as const,
text: [
`execute_orchestrate: ${outcome.status}`,
`run status: ${'runStatus' in outcome ? outcome.runStatus : 'not_started'}`,
`run id: ${params.runId}`,
...(outcome.status === 'halted' ? [`halted at: ${outcome.step} (${outcome.reason})`] : []),
].join('\n'),
},
],
details: { outcome },
};
},
};
}

export function registerBrunchExecuteOrchestrate(pi: ExtensionAPI, ports: ExecutionPorts): void {
pi.registerTool(createExecuteOrchestrateTool(ports) as never);
}

export default registerBrunchExecuteOrchestrate;
1 change: 1 addition & 0 deletions src/.pi/extensions/agent-runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './execute-agent-result/index.js';
export * from './execute-orchestrate/index.js';
export * from './execute-host-promotion/index.js';
export * from './execute-launch/index.js';
export * from './execute-plan-file/index.js';
Expand Down
2 changes: 2 additions & 0 deletions src/agents/runtime/executor/active-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const EXECUTOR_ALLOWED_TOOL_NAMES = [
// Execute-mode orchestration footholds (FE-1089). Registered-but-inactive
// unless the executor admits them; side-effect-bounded per I52-L.
'execute_status',
// Run driver over the lifecycle steps (FE-1125, D102-L).
'execute_orchestrate',
'execute_snapshot',
'execute_plan_check',
'execute_plan_outline',
Expand Down
3 changes: 2 additions & 1 deletion src/app/__tests__/agent-runner-port.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ describe('createAgentRunnerPort', () => {
}),
).resolves.toEqual({
status: 'failed',
message: 'AgentRunnerPort is not implemented yet; inject sealed subagent deps to execute agent slices.',
message:
'AgentRunnerPort has no subagent deps injected in this launch, so the sealed worker cannot run. Compose subagents (execute mode or --dev-tools).',
});
});

Expand Down
2 changes: 2 additions & 0 deletions src/app/__tests__/brunch-tui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
BRUNCH_EXECUTE_PLAN_OUTLINE_TOOL,
BRUNCH_EXECUTE_SNAPSHOT_TOOL,
BRUNCH_EXECUTE_STATUS_TOOL,
BRUNCH_EXECUTE_ORCHESTRATE_TOOL,
BRUNCH_INTROSPECTION_COMMAND,
BRUNCH_MODE_COMMAND,
BRUNCH_SWITCH_COMMAND,
Expand Down Expand Up @@ -674,6 +675,7 @@ describe('Brunch TUI boot', () => {
'web_fetch',
'web_search',
BRUNCH_EXECUTE_STATUS_TOOL,
BRUNCH_EXECUTE_ORCHESTRATE_TOOL,
BRUNCH_EXECUTE_AGENT_RESULT_TOOL,
BRUNCH_EXECUTE_PETRI_EXPORT_TOOL,
BRUNCH_EXECUTE_PROMOTION_PREPARE_TOOL,
Expand Down
Loading
Loading