TypeDrop
2026-05-01 Challenge
2026-05-01
Hard
Typed Workflow Orchestrator
You're building the execution engine for a low-code automation platform. User-defined workflow definitions arrive as `unknown` from a configuration store; your orchestrator must validate them, topologically sort their steps into dependency-respecting execution stages, run each stage with typed retry/timeout policies, and emit a discriminated-union execution report per step — with zero `any`.
Goals
- Implement `validateWorkflow` to parse `unknown` input into a typed `WorkflowDef`, rejecting missing fields, unknown dependencies, and cyclic graphs — using a `ValidationResult<T>` return type with no throws.
- Implement `buildExecutionStages` using Kahn's BFS topological sort to bucket `StepDef`s into sequentially-ordered, concurrently-executable stages.
- Implement `withTimeout`, `withRetry`, and `executeStep` so every step attempt is individually raced against a `Promise<never>` timeout and retried with exponential back-off — with all errors captured as `StepFailure` (never thrown out of `executeStep`).
- Implement `executeStage` and `orchestrate` to run stages sequentially, steps within a stage concurrently, propagate failure via a `ReadonlySet<StepId>` to skip downstream steps, and assemble a fully-typed `WorkflowReport`.
challenge.ts
// Key types & main entry-point — enough context to understand the challenge
export type StepId = string & { readonly __brand: "StepId" };
export type StepContext = Readonly<Record<StepId, unknown>>;
export type StepHandler = (ctx: StepContext) => Promise<unknown>;
export type StepReport =
| { kind: "success"; stepId: StepId; output: unknown; attempts: number; durationMs: number }
| { kind: "failure"; stepId: StepId; error: string; attempts: number; durationMs: number }
| { kind: "skipped"; stepId: StepId; reason: "dependency_failed" };
export interface WorkflowReport {
workflowId: string;
status: "completed" | "partial_failure";
stepReports: StepReport[];
totalDurationMs: number;
}
export type HandlerRegistry = Record<StepId, StepHandler>;
// Validate raw unknown → typed def, build stages, execute, report
export async function orchestrate(
rawDef: unknown,
registry: HandlerRegistry
): Promise<WorkflowReport> {
throw new Error("Not implemented");
}
Hints (click to reveal)
Hints
- For cycle detection in `validateWorkflow`, a DFS with three node colours (white/grey/black) is the classic approach — or you can re-use Kahn's algorithm: if the topological sort doesn't consume all nodes, a cycle exists.
- `withTimeout` should return `Promise<never>` — a promise that only ever rejects. `Promise.race([handlerCall(), withTimeout(...)])` gives you a timed attempt that resolves to `unknown` on success.
- In `executeStage`, collect per-step results with `Promise.all`, but be careful: steps marked as skipped must short-circuit before the handler is called, and context must be updated only after all concurrent results are in.
Useful resources
Or clone locally
git clone -b challenge/2026-05-01 https://github.com/niltonheck/typedrop.git