feat(conductor): Wave 2 — parallel batch execution + SWITCH branching step

- Parallel batch execution: batch field on Step, batchConfig on Flow,
  batch-aware readySteps with maxConcurrent gating, getReadyInBatch helper
- SWITCH branching step: new 'switch' StepKind with cases/programmed conditions,
  resolveSwitch() pure function, switch-excluded steps tracked in
  SchedulerState, non-selected branches excluded from execution
This commit is contained in:
2026-06-08 03:00:06 +00:00
parent 776c5f9307
commit 591d373534
4 changed files with 642 additions and 9 deletions

View File

@@ -38,7 +38,20 @@ export interface StepContext {
readonly model?: string;
}
export type StepKind = 'agent' | 'code' | 'approval';
export type StepKind = 'agent' | 'code' | 'approval' | 'switch';
/**
* One branch of a SWITCH step. The first case whose condition evaluates to true
* is selected; all other branches' stepIds are excluded from execution.
*/
export interface SwitchCase {
/** Human-readable label for this branch (reported in switch output). */
label: string;
/** Pure guard — called with the current step context to decide this branch. */
condition: (ctx: StepContext) => boolean;
/** stepIds belonging to this branch. */
stepIds: string[];
}
export type TriggerRule = 'all_success' | 'one_success' | 'all_done';
@@ -63,12 +76,19 @@ export interface Step {
/**
* For kind:'agent', returns the worker PROMPT (task + any prior outputs).
* For kind:'code', returns the step RESULT directly (the fold/transform).
* For kind:'switch', unused (the runner evaluates cases internally).
*/
run: (ctx: StepContext) => string | Promise<string>;
/** optional guard — when it returns false the step is skipped (e.g. no repo) */
when?: (ctx: StepContext) => boolean;
/** max retries on timeout (0 or unset = no retry) */
maxRetries?: number;
/** batch group id; steps sharing the same batch are gated by batchConfig.maxConcurrent */
batch?: string;
/** for kind:'switch' — ordered list of branches evaluated in declaration order */
cases?: SwitchCase[];
/** for kind:'switch' — fallback step ids when no case matches */
defaultBranch?: string[];
}
export interface Flow {
@@ -79,6 +99,8 @@ export interface Flow {
render: (ctx: StepContext) => string;
/** optional output filename for the artifact, derived from input */
output?: (ctx: StepContext) => string;
/** batch parallelism control — gates concurrent dispatch of steps sharing the same batch id */
batchConfig?: { maxConcurrent: number; timeoutMs?: number; joinRule?: TriggerRule };
}
export interface RunResult {