LaufwerkLaufwerk
Learn Laufwerk

Branches, loops and parallel work

Express the process in ordinary code and bound retries by a clear policy.

Code owns the process: an agent proposes work, while your workflow decides what happens next. You do not need a separate graph definition to run a branch or loop.

Parallelize independent checks

This complete workflow simulates two deterministic reviewers. Save it at laufwerk/workflows/check-draft/workflow.ts and run with a request ID and draft. It uses no model or credentials.

workflow.ts
import { Activity, Workflow } from "@effect/workflow";
import { Effect, Schema } from "effect";

const Check = Schema.Struct({ name: Schema.String, passed: Schema.Boolean });
export const workflow = Workflow.make({
  name: "check-draft",
  payload: { requestId: Schema.String, draft: Schema.String },
  success: Schema.Struct({ accepted: Schema.Boolean, checks: Schema.Array(Check) }),
  idempotencyKey: input => input.requestId,
});
export const layer = workflow.toLayer(input => Effect.gen(function* () {
  const rules = [
    { name: "has-content", passed: input.draft.trim().length > 0 },
    { name: "length", passed: input.draft.length <= 500 },
  ];
  const checks = yield* Effect.all(rules.map(rule => Activity.make({
    name: rule.name, success: Check, execute: Effect.succeed(rule),
  })), { concurrency: 2 });
  return { accepted: checks.every(check => check.passed), checks };
}));

"Hello" passes both checks; an empty draft fails has-content. The activity results are recorded separately. These rules are deliberately simple so you can see the orchestration; they do not measure writing quality.

Replace the activities with independent Session.run calls when you need agent review. Give every session a stable key. Shared Docker read-only workspaces suit parallel review; independent writable copies need separate runs.

Bound a revision loop

Draft → review → accepted? ── yes → return accepted result

                    no → revision available? ── yes → revise → review

                                    no → return unresolved result

Set a maximum number of drafts before starting. Derive durable keys from the round, such as draft-0 and review-0. Keep feedback linked to the candidate it assessed. A negative review is ordinary data; an exception means the review did not finish. Do not catch every error and translate it into “please try again.”

The complete workflow implements a two-draft limit and returns revision-limit if the second draft is still rejected. That policy differs from the optional issue-to-fix starter, which can continue with objections after its two-pass limit. Your code chooses the policy.

Execution order and pictures

Effect.all(..., { concurrency: 2 }) controls parallelism. A Visual annotation only describes the structure for Studio; it does not schedule work or enforce the declared concurrency.

Reference: Activities · Visual annotations


← Structured data and real checks · Next: Human input and review →

On this page