# Orchestrator Advanced Flows — Design ## Architecture ``` ┌───────────── Step dispatch ─────────────────┐ │ │ │ Flow-runner resolves step: │ │ 1. Check trigger_rule on deps │ │ 2. Substitute $vars in prompt │ │ 3. If approval gate → pause for user │ │ 4. INSERT task row → dispatcher picks up │ │ 5. On terminal: append to event log │ │ 6. Advance next ready step │ │ │ └──────────────────────────────────────────────┘ ``` ## Type Changes ### `apps/coder/src/conductor/types.ts` ```typescript export type TriggerRule = 'all_success' | 'one_success' | 'all_done'; export interface Step { id: string; kind: StepKind | 'approval'; // + new kind deps?: string[]; trigger_rule?: TriggerRule; // NEW: default 'all_success' agent?: string; run: (ctx: StepContext) => string | Promise; when?: (ctx: StepContext) => boolean; } ``` ### `apps/coder/src/services/flow-runner.ts` | Change | Detail | |--------|--------| | Trigger evaluation | Before dispatching a step, check deps statuses against `trigger_rule`. Skip if conditions not met | | Variable substitution | Scan prompt for `$word.word` patterns, resolve from previous step outputs | | Approval gate | When `step.kind === 'approval'`, insert a `tasks` row with `state='blocked'` and publish a `permission_requested` WS frame. Wait for `permission_resolved` to unblock | | Event log | Append-only per-step events: `{ step_id, event: 'started'|'completed'|'failed'|'paused'|'resumed', at: timestamp }` in `flow_step_events` table | ## Schema ```sql CREATE TABLE IF NOT EXISTS flow_step_events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), run_id UUID NOT NULL REFERENCES flow_runs(id), step_id VARCHAR(64) NOT NULL, event VARCHAR(32) NOT NULL, -- started, completed, failed, paused, resumed, skipped payload JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp() ); ``` ## Resolution Order 1. Collect all completed steps in the run 2. For each unstarted step whose deps are met: - Evaluate `trigger_rule` against dep statuses - If met → advance the step (with variable substitution) - If not met → skip (for `one_success`, mark it complete when any dep succeeds) 3. For approval gates: pause, publish frame, wait for user response ## Rollback All changes are additive to the Step type. Existing flows without `trigger_rule` default to `all_success`, preserving current behavior. Approval gates are opt-in per step definition.