Files
boocode/openspec/changes/orchestrator-flow-advanced/design.md

2.8 KiB

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

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<string>;
  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'

Schema

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.