Skip to main content
<DelegationChain> is the composite behind the delegation-chain workflow: a <Sequence> of seven phases running in <Parallel> with a live-edit signal listener. Every phase is reactive: it re-derives its slice of the delegation tree from the dc* output rows on each render, so fan-out materializes level by level as rows land, and user edits or probe findings replan the affected subtree without restarting anything.
// Props
import { DelegationChain } from "smithers-orchestrator";

type Tier = "fable" | "opus" | "sonnet" | "haiku";
type DelegationAgents = Partial<Record<Tier, AgentLike | AgentLike[]>>;

type DelegationSharedProps = {
  idPrefix?: string;              // physical node-id prefix; default "dc"
  agents: DelegationAgents;       // one agent (or failover chain) per tier label
  outputs: DelegationOutputs;     // the dc* output targets (see below)
  approvalPolicy?: string;        // absent = no approval gates anywhere
  tierOrder?: Tier[];             // strongest first; default ["fable","opus","sonnet","haiku"]
  maxDepth?: number;              // default 3
  maxConcurrency?: number;        // default 4
  maxDeriskRounds?: number;       // default 3
  poll?: boolean;                 // default true
  budget?: { maxUsd?: number; maxMinutes?: number };
  scorers?: { exec?: ScorersMap; review?: ScorersMap; run?: ScorersMap };
  skipIf?: boolean;
};

type DelegationChainProps = DelegationSharedProps & {
  prompt: string;                 // the original (possibly ambiguous) ask
  maxQuestions?: number;          // default 10
  prefetchDepth?: number;         // question forms rendered ahead; default 10
  maxAttempts?: number;           // exec/review attempts per leaf; default 3
  maxEdits?: number;              // live edits accepted per run; default 25
};
import { CodexAgent, DelegationChain, createSmithers, delegationSchemas } from "smithers-orchestrator";

const { Workflow, smithers, outputs } = createSmithers({ ...delegationSchemas });

// The legacy tier keys are labels, not provider or model ids. Point them at
// Codex tiers by strength; the seeded workflow uses generated pools instead.
const sol = new CodexAgent({ model: "gpt-5.6-sol" });
const terra = new CodexAgent({ model: "gpt-5.6-terra" });
const luna = new CodexAgent({ model: "gpt-5.6-luna", config: { model_reasoning_effort: "medium" } });
const lunaCheap = new CodexAgent({ model: "gpt-5.6-luna", config: { model_reasoning_effort: "medium" } });

export default smithers((ctx) => (
  <Workflow name="delegation-chain">
    <DelegationChain
      prompt={ctx.input?.prompt ?? ""}
      agents={{ fable: sol, opus: terra, sonnet: luna, haiku: lunaCheap }}
      outputs={outputs}
    />
  </Workflow>
));
Register delegationSchemas in createSmithers and pass the matching outputs subset. Tiers are labels only; missing tiers fall back to the nearest configured tier in tierOrder. When budget.maxMinutes is set the whole chain is wrapped in <Aspects> with a wall-clock latencySlo; dollar budgets are enforced by per-leaf guard tasks over rolled-up dcExec actuals (hard error over the limit, warning row at 80%).

Phase composites

Each phase is exported separately for standalone use and takes DelegationSharedProps (plus the extras noted):
ComponentPhaseExtra props
<GoalRefinement>Question forecast, prefetched forms, one durable question at a time, refined-prompt approvalprompt, maxQuestions, prefetchDepth
<DelegationPlanning>Recursive decomposition fan-out until the frontier is all leavesprompt? (standalone root brief; otherwise waits for the approved goal)
<DelegationPreview>Zero-backpressure expected-output previews, skippable via dc-skip-previewnone
<BackpressurePlanning>Every node declares gates and dependencies before executionnone
<DeriskLoop>Risk probes, findings to the nearest parent, replan/reaffirm cascadesnone
<DelegationExecution>Dependency-ordered leaf pipelines: exec, gates, approvals, developer previews, budget guardsmaxAttempts
<DelegationScoring>Run-level score digest plus the satisfaction pollnone
<DelegationEditListener>Re-arming dc-edit signal wait feeding the derisk loopuntil?, maxEdits

Output tables

delegationSchemas (from smithers-orchestrator) registers every table; DelegationOutputs is the matching prop shape. dcApproval is only needed with an approvalPolicy, dcBudget only with a budget, and dcScore only with run-level scorers.
TableOne row per
dcGoalRefined goal (prompt, assumptions, questions asked)
dcQuestionRendered question form / resolved answer
dcForecastThe goal agent’s upfront question batch (internal)
dcGoalApprovalThe human’s { approved, refinedPrompt } decision
dcPlanA delegating node’s plan: children, risks, estimates; replans append superseding rows
dcPreviewA leaf’s never-executed expected output
dcGatesA node’s declared gates and logical dependencies
dcProbeA probe’s finding (planImpact: changes / confirms / none)
dcReplanA replan decision (invalidated or reaffirmed) with its trigger
dcExecOne execution attempt, with best-effort actual usage and the measured commitRange
dcReviewA review/check gate verdict for one attempt
dcDevPreviewA developer-preview build (builtOk is required to pass)
dcApprovalAn approval-gate decision
dcEditA live user edit (signal payload)
dcSkipThe skip-previews signal payload
dcPollThe end-of-run poll answers
dcBudgetA budget-guard checkpoint (ok or warn)
dcScoreThe run-level scoring digest

Physical node ids

Every task id follows dc:<logicalId>:<phase> (the dc prefix is idPrefix). Logical ids are /-separated paths (root/core/reducer); in physical ids the / is encoded as : because node ids only allow [a-zA-Z0-9:_-]. Row logicalId fields keep the / form. Phases:
  • Goal: dc:goal:forecast, dc:goal:forms:question-<seq>, dc:goal:question-<seq> (the durable human answer), dc:goal:goal, dc:goal:approve
  • Planning: dc:<logicalId>:plan, replan versions dc:<logicalId>:plan-<k>
  • Previews: dc:<logicalId>:preview
  • Gates: dc:<logicalId>:gates
  • Derisk: dc:<logicalId>:probe-<n>, dc:<logicalId>:replan-<k>
  • Execution: dc:<logicalId>:exec, dc:<logicalId>:review-<i> (reviews then checks, in declared order), dc:<logicalId>:approval-<i>, dc:<logicalId>:dev-preview (then dev-preview-2, …), dc:<logicalId>:budget
  • Scoring: dc:root:score, dc:root:poll

Signals

Two fixed durable signal names (exported as DC_EDIT_SIGNAL and DC_SKIP_PREVIEW_SIGNAL):
  • dc-edit with payload { editId, logicalId, editedOutput, note? }. Each delivered edit writes a dcEdit row that the derisk loop treats exactly like a plan-changing probe finding.
  • dc-skip-preview with payload { skipped: true }. Once a dcSkip row exists, no further preview tasks mount.

Gates

A dcGates row declares an ordered list of gates:
type Gate =
  | { method: "review"; tier: Tier; brief: string }
  | { method: "check"; command: string }
  | { method: "approval"; policyMatch: string }   // honored only under an approvalPolicy
  | { method: "preview"; kind: "app" | "terminal" | "api" | "throwaway-ui" | "slideshow"; brief: string };
Review gates receive the node’s output and its dcExec.commitRange values with instructions to inspect the commits themselves; chunk-level reviews get the union of their subtree’s ranges (exec agents are wrapped with the exported withCommitRange, which measures the working-copy commit before and after the attempt, jj first with a git fallback). preview gates build a developer preview after execution; builtOk: false fails the attempt like a failed review. The gates prompt requires the root node to declare a slideshow preview, so a run always ends showable.

UI side

smithers-orchestrator/gateway-react exports the matching read model: useDelegationChain folds a run’s dc* rows into a DelegationGraph (nodes with status, versions, attention rollups, budget rollup, phase) and returns the submitEdit, skipPreviews, answerHuman, and submitPoll actions. The pure reducer foldDelegation is exported for tests and non-React use.

Notes

  • Approval gates require outputs.dcApproval; <DelegationExecution> throws INVALID_INPUT if the policy produced approval gates without it.
  • dcPlan.orchestration ("tasks" | "workflow") is reserved for planned higher-order orchestration (a node authoring its own workflow as its execution strategy); it is accepted and ignored today. The fold store behind useDelegationChain runs on Effect.ts behind the frozen hook signature.
  • Scoring wiring: scorers.exec / scorers.review ride the exec and review tasks; scorers.run rides the digest task. The delegation scorers live in smithers-orchestrator/scorers (see the scorer reference).