# conditional-step _(no submodules)_ ## Purpose `ConditionalStep` is the branching primitive in `@suaveplan/workflow`. It evaluates a `GuardFn` against the context and dispatches to one of two `Step` instances (`onTrue` or `onFalse`). When the false branch is omitted and the condition resolves falsy, the step is recorded as `"skipped"` — giving callers a way to express "do this only if the predicate holds" without filling in a no-op alternative. This class is the workflow analogue of a ternary expression: it never declares its own retry policy or compensation directly; both branches are fully-fledged `Step` instances whose own configuration governs those concerns. `compensate` rolls back only the branch that actually ran. ## Features - `onTrue` / optional `onFalse` branches. - `lastBranch` accessor reflecting which path executed. - Skipped branch records a `"skipped"` `StepResult` and emits an OTel `"skipped"` counter increment (the duration histogram is intentionally skipped — see `otel/instruments`). - `compensate(ctx)` rolls back only the branch that ran. - Injectable `Clock`. ## Usage: Basic Example ```ts import { createConditionalStep, createStep } from "@suaveplan/workflow"; const branch = createConditionalStep<{ premium: boolean }>({ id: "discount", name: "Discount", condition: (ctx) => ctx.premium, onTrue: createStep({ id: "premium", name: "Premium discount", execute: () => ({ discountPct: 30 }), }), onFalse: createStep({ id: "standard", name: "Standard discount", execute: () => ({ discountPct: 5 }), }), }); const result = await branch.run({ premium: true }); ``` ## Usage: Advanced Example ```ts import { createConditionalStep, createStep } from "@suaveplan/workflow"; const ship = createStep<{ shipped: boolean }>({ id: "ship", name: "Ship", execute: async (ctx) => { await dispatch(ctx); }, compensate: async (ctx) => { await recall(ctx); }, }); const guard = createConditionalStep<{ ready: boolean }>({ id: "maybe-ship", name: "Maybe ship", condition: (ctx) => ctx.ready, onTrue: ship, }); await guard.run({ ready: true }); // Later, on outer-step rollback: await guard.compensate({ ready: true }); ``` ## API Reference - `ConditionalStepOptions` — `{ id; name; condition; onTrue; onFalse?; clock?; }`. - `ConditionalStep` — - `trueBranch`, `falseBranch?`, `lastBranch?` - `run(context): Promise` - `compensate(context): Promise` - `createConditionalStep(options): ConditionalStep`. ## Implementation Notes `lastBranch` is set before the branch runs so callers that observe the step (e.g. via hooks) can attribute downstream side-effects to the chosen arm. `compensate` checks both the branch identity and the underlying step's `hasCompensation` flag, so a guard whose `onTrue` step has no compensation is a no-op rather than a thrown error. `recordStepExecuted(stepId, "skipped", 0)` is emitted on the no-false-branch fallthrough so the metric for skipped steps lines up with what `WorkflowEngine` emits when a top-level `Step.shouldExecute` returns false.