Best Practices
Keep Plans & Tasks simple, visible, and easy to compose.
Keep The Plan Reviewable
Plans should explain the objective, constraints, and intended approach. Avoid storing raw conversation history in a plan.
await roadmap.update({
content: [
"## Objective",
"Prepare the launch announcement.",
"",
"## Constraints",
"- Use approved positioning.",
"- Cite source material.",
].join("\n"),
});Choose Dynamic Or Defined Early
Use dynamic ledgers when task IDs are discovered at runtime.
const work = await tasks({ title: "Runtime checklist" });
await work.add({ id: generatedId, label });Use defined ledgers when IDs and result schemas are part of your program contract.
import { z } from "zod";
const work = await tasks({
items: {
review: task("Review output", {
result: z.object({ approved: z.boolean() }),
}),
},
});Use Lifecycle Methods
Prefer start(), progress(), complete(), fail(), skip(), cancel(), and remove() over generic status mutation.
await work.start("review");
await work.progress("review", "Checking facts");
await work.complete("review", { approved: true });Keep Results JSON-Safe
Task metadata, progress details, and results are persisted. Store JSON-safe values only.
await work.complete("extract", {
rows: 1200,
warnings: ["Missing phone number on row 42"],
});Scope Tools Narrowly
Give planners plan tools, coordinators ledger tools, and workers task-scoped tools.
const worker = work.worker("draft");
prompt({
use: [roadmap.asContext(), worker.asContext()],
tools: worker.asTools(),
});Do Not Treat Ledgers As Queues
Task ledgers record state. They do not provide retries, schedules, locks, or concurrency limits. Use flow() or a durable runner for those guarantees, and store the plan ID plus task-ledger ID in that orchestration layer.