Skip to content
156 changes: 0 additions & 156 deletions memory/CARDS.md

This file was deleted.

16 changes: 9 additions & 7 deletions memory/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ The May 2026 intent-spec, multi-chat, changeset-ledger, prompt/context, and agen
1. `agent-fixture-substrate` — branch-complete off main, reconciling — FE-705 integration substrate for JSONL agent capability CLI and LLM-as-user probes.
2. `chat-runtime-secondary-chats` — FE-716; V1 done — PR #141 merged to main.

### Recently Completed

- `petri-semantic-lanes` (FE-738) — two-lane subnet, compiler topology/wiring split, engine factory, semantic rework budget, §7 events. PR #148. Criterion (5) stale-graph deferred → `petri-graph-compilation`.

### Next

1. `petri-semantic-lanes` — mechanical + semantic two-lane subnet template + §7 event vocabulary; first frontier where petri diverges meaningfully from proc. Follows `orchestrator-poc` Phase 0.
2. `petri-parallel-execution` — parallel firing, shared resource pools, worktree-per-slice coordination; the categorical break where petri earns its complexity. Decision gate: if petri doesn't beat proc on wall clock, pause petri investment. Follows `petri-semantic-lanes`.
1. `petri-parallel-execution` — parallel firing, shared resource pools, worktree-per-slice coordination; the categorical break where petri earns its complexity. Decision gate: if petri doesn't beat proc on wall clock, pause petri investment. Follows `petri-semantic-lanes`.
3. `intent-graph-semantics` — highest-coordination semantic substrate after FE-705 reconciliation.
4. `changeset-ledger` — Track 4 of the runtime umbrella; parallel with Track 2; semantic history spine needed before canonical proposal acceptance, direct-edit atomicity, and productized scenario options.
5. `chat-context-provision` — Track 5 of the runtime umbrella recast as transcript-first context; can proceed against chat/turn once secondary-chat entry/anchor shape is settled.
Expand Down Expand Up @@ -80,16 +83,15 @@ The May 2026 intent-spec, multi-chat, changeset-ledger, prompt/context, and agen
### petri-semantic-lanes

- **Name:** Petri semantic lanes — mechanical + semantic two-lane subnet template
- **Linear:** unassigned (create under umbrella H-6476)
- **Linear:** FE-738
- **Kind:** structural
- **Status:** not-started
- **Status:** done (criterion 5 deferred → `petri-graph-compilation`)
- **Objective:** Extend the extracted NetCompiler to produce a two-lane subnet template per slice: a mechanical lane (dispatch, artifact production, test execution, verification) and a semantic lane (oracle satisfaction, design exercise, intent establishment, completion claim review). Add the spec §7 structured event vocabulary (`transition_fired`, `oracle_passed`, `graph_revision_stale`, `semantic_review_requested`, `completion_claim_accepted`, `status_projection_suggested`, `net_deadlocked`, …) as the interpreter's durable event model. `TransitionContract` type (spec §6) governs each transition's kind, actor, guard, action binding, and emitted events. Mechanical transitions produce candidate evidence; semantic transitions judge that evidence against graph-derived requirements. `PlanDoneAccepted` is reachable only after both lanes complete.
- **Why now / unlocks:** First frontier where the Petri engine models a distinction the proc engine cannot express topologically: mechanical completion ≠ semantic completion. Unblocks Phase 2 parallel firing (lanes can fire concurrently) and Phase 3 graph compilation (semantic lane structure maps to relation-policy gates). Without this, the engine remains a serial task runner with token-shaped bookkeeping.
- **Acceptance:** (1) Compiled subnet has distinct mechanical and semantic places/transitions. (2) `PlanDoneAccepted` is unreachable unless both `VerifyPassed` and `OracleSatisfied` (or equivalent semantic tokens) are present. (3) Event log records §7 vocabulary events per transition firing. (4) `TransitionContract` type covers kind, actor, guard, and emits. (5) Stale-graph detection routes to reconciliation or context rebuild, not a dead-end. (6) Existing contract test suite passes with both engines.
- **Verification:** Contract tests extended with semantic-lane scenarios (happy-path Prototype A, stale-graph Prototype B, missing-oracle Prototype C from spec §10). Adapter test for two-lane net shape. Event-log assertions for §7 vocabulary.
- **Acceptance:** (1) ✅ Compiled subnet has distinct mechanical and semantic places/transitions. (2) ✅ `PlanDoneAccepted` is unreachable unless both `VerifyPassed` and `OracleSatisfied` (or equivalent semantic tokens) are present. (3) ✅ Event log records §7 vocabulary events per transition firing. (4) ✅ `TransitionContract` type covers kind, actor, guard, and emits. (5) **Deferred → `petri-graph-compilation`**: stale-graph detection requires `GraphRevisionCurrent` tokens and graph revision semantics from `intent-graph-semantics` (FE-700); the current Plan-from-YAML substrate has no mutable graph revision to detect staleness against. (6) ✅ Existing contract test suite passes. (7) ✅ `compileTopology(plan, policy) → NetBlueprint` is pure (no runtime refs); `wireHandlers(blueprint, input, ctx) → PetriNet` attaches closures. (8) ✅ `createOrchestrator(policy)` factory replaces identical engine classes. (9) ✅ `RunCtx` lives in `types.ts`, not compiler. (10) ✅ Semantic rework budget (`semantic-budget` place) prevents infinite rework loops. (11) ✅ `HandlerDescriptor` discriminated union describes each transition's routing recipe declaratively.
- **Verification:** Contract tests extended with semantic-lane scenarios (happy-path Prototype A, stale-graph Prototype B, missing-oracle Prototype C from spec §10). Adapter test for two-lane net shape via `compileTopology` (pure topology, no runtime bindings needed). Event-log assertions for §7 vocabulary. Semantic rework exhaustion contract test.
- **Traceability:** Requirements 46–50; spec §2 (layer split), §4 (canonical slice-net), §6 (transition contracts), §7 (event model), §8 (failure-mode nets), §10 (prototypes A–C).
- **Design docs:** `docs/next/architecture/plan-graph-petri-orchestration.md`; `docs/design/orchestrator.md`; umbrella H-6476.
- **Current execution pointer:** `memory/CARDS.md` — Card 1 (two-lane subnet with semantic completion gate).

### petri-parallel-execution

Expand Down
14 changes: 7 additions & 7 deletions src/orchestrator/src/cook-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ describe('parseCookArgs', () => {
it('parses dir only', () => {
const opts = parseCookArgs(['./fixtures/txt']);
expect(opts.dir).toContain('fixtures/txt');
expect(opts.engine).toBe('petri');
expect(opts.policy).toBe('serial');
expect(opts.maxRetries).toBe(3);
expect(opts.verbose).toBe(false);
});

it('parses --engine=petri', () => {
const opts = parseCookArgs(['./f', '--engine=petri']);
expect(opts.engine).toBe('petri');
it('parses --policy=serial', () => {
const opts = parseCookArgs(['./f', '--policy=serial']);
expect(opts.policy).toBe('serial');
});

it('parses --max-retries=5', () => {
Expand All @@ -22,11 +22,11 @@ describe('parseCookArgs', () => {
});

it('throws on missing dir', () => {
expect(() => parseCookArgs(['--engine=proc'])).toThrow('Usage');
expect(() => parseCookArgs(['--policy=serial'])).toThrow('Usage');
});

it('throws on unknown engine', () => {
expect(() => parseCookArgs(['./f', '--engine=unknown'])).toThrow('Unknown engine');
it('throws on unknown policy', () => {
expect(() => parseCookArgs(['./f', '--policy=unknown'])).toThrow('Unknown policy');
});

it('parses --verbose', () => {
Expand Down
26 changes: 12 additions & 14 deletions src/orchestrator/src/cook-cli.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import { existsSync } from 'node:fs';
import { join, resolve } from 'node:path';

import { PetriOrchestrator } from './engine-petri.js';
import { ProceduralOrchestrator } from './engine-proc.js';
import { createOrchestrator } from './engine.js';
import { FileReportSink } from './file-report-sink.js';
import type { FiringPolicy } from './petri-net.js';
import { createPiActions } from './pi-actions.js';
import { loadPlan } from './plan-loader.js';
import { BunTestRunner } from './test-runner.js';
import type { Orchestrator } from './types.js';
import { createWorktree } from './worktree.js';

export type CookOptions = {
dir: string;
engine: 'proc' | 'petri';
policy: FiringPolicy;
maxRetries: number;
verbose: boolean;
};

export function parseCookArgs(args: string[]): CookOptions {
let dir = '';
let engine: 'proc' | 'petri' = 'petri';
let policy: FiringPolicy = 'serial';
let maxRetries = 3;
let verbose = false;

for (let i = 0; i < args.length; i++) {
const arg = args[i]!;
if (arg.startsWith('--engine=')) {
if (arg.startsWith('--policy=')) {
const val = arg.split('=')[1]!;
if (val !== 'proc' && val !== 'petri') {
throw new Error(`Unknown engine: ${val}. Use proc or petri.`);
if (val !== 'serial') {
throw new Error(`Unknown policy: ${val}. Use serial.`);
}
engine = val;
policy = val;
} else if (arg.startsWith('--max-retries=')) {
const parsed = Number.parseInt(arg.split('=')[1]!, 10);
if (!Number.isFinite(parsed) || parsed < 0) {
Expand All @@ -45,10 +44,10 @@ export function parseCookArgs(args: string[]): CookOptions {
}

if (!dir) {
throw new Error('Usage: brunch cook <dir> [--engine=proc|petri] [--max-retries=N] [--verbose]');
throw new Error('Usage: brunch cook <dir> [--policy=serial] [--max-retries=N] [--verbose]');
}

return { dir: resolve(dir), engine, maxRetries, verbose };
return { dir: resolve(dir), policy, maxRetries, verbose };
}

function fmtDuration(ms: number): string {
Expand Down Expand Up @@ -84,7 +83,7 @@ export async function runCook(opts: CookOptions): Promise<void> {
console.error('');
console.error(` brunch cook`);
console.error(` ──────────────────────────────────────`);
console.error(` engine ${opts.engine}`);
console.error(` policy ${opts.policy}`);
console.error(` plan ${epicCount} epics, ${sliceCount} slices`);
console.error(` retries ${opts.maxRetries}`);
console.error(` worktree ${worktreeDir}`);
Expand All @@ -94,8 +93,7 @@ export async function runCook(opts: CookOptions): Promise<void> {
const reports = new FileReportSink(reportsPath);
const testRunner = new BunTestRunner();

const engine: Orchestrator =
opts.engine === 'petri' ? new PetriOrchestrator() : new ProceduralOrchestrator();
Comment thread
cursor[bot] marked this conversation as resolved.
const engine = createOrchestrator(opts.policy);

const runStart = Date.now();
const actions = createPiActions({ verbose: opts.verbose, runStart });
Expand Down
Loading
Loading