| name | wfgen |
| description | Compose a runnable multi-agent Workflow script from 4 canonical examples; verify before accepting, every reducer emits a decision ledger. Use on '/wfgen', 'compose a workflow', 'fan this out'. |
wfgen
Compose a ready-to-run ultracode Workflow script from a task. wfgen is the
workflow-family sibling of loopgen: loopgen composes autonomous loop prompts;
wfgen composes workflow scripts (deterministic JS over Agent-SDK primitives —
agent() / parallel() / pipeline() / phase() / budget / isolation).
The four example scripts in examples/ are the product. You don't assemble a
script from a primitive vocabulary — you recognize which of four shapes the task
is, adapt the matching example, and emit it. The examples already encode the
taste (verify-before-accept, decision ledgers, declared effects); your job is to
fit them to the task without sanding off that discipline.
Foundation: references/foundation.md — a workflow is
only as trustworthy as the externalized contracts at its joins. The mechanism:
references/workflow-mechanism.md. The emitted-
script dialect + the ledger idiom: references/script-shape.md.
The schemas/predicates: references/borrowed-schemas.md.
The best practices for dynamic workflows: references/best-practices.md — the taste the examples encode.
Flow: classify → adapt → emit
1. Classify (by judgment, not a distance metric)
Recognize the task's dependency topology — how separable is it, and what kind
of ground truth is available? Pick one of four:
| archetype | shape | use when |
|---|
| understand | parallel readers → barrier → synthesize | map a corpus/codebase; many independent sources; answer = returned data |
| design | N candidates → judge panel → score → synthesize-from-winner | generate options and choose; no execution oracle; needs criteria + minority |
| review | rounds of a reviewer → dedup → loop-until-dry | audit a diff/artifact; composes any review skill you provide, else an inline reviewer |
| migrate | discover sites → transform-in-worktree → execute → repair | mechanical change across N sites; execution is the oracle |
If the task fans out on tightly coupled work (one decision that touches
everything), don't fan out — say so and hand back to /architect or a direct
edit. Breadth is for separable work with checkable interfaces.
If a task doesn't fit, compose from the closest example and note the divergence.
Do not invent a fifth archetype reflexively — four cover the common shapes.
One documented exception: coupled work + an all-or-nothing verifiable goal +
high expected mortality of approaches → portfolio search (fan out rival whole
attempts, not pieces — hard debugging, perf targets, exploit hunting). Candidate
shape, no example yet: see references/portfolio-search.md;
compose it from review-until-dry's loop chassis + design's rival portfolio.
2. Adapt the example
Open the matching examples/<archetype>.js, fit it to the task, and preserve the
non-negotiables (the reducer invariant + envelope below). Adapt: the prompts, the
schemas' fields, the decomposition source. Don't touch: the ledger discipline, the
effort knob, the declared effects.
When the source task includes author/user intent, pass it through as args.intent
for review and migrate workflows. Intent is context, not proof: it helps reviewers
and repair agents distinguish deliberate choices from mistakes. If intent is absent,
make that absence explicit and infer cautiously.
Derive the goal contract. At compose time, derive optional args.notDone
(near-miss checks) and args.traps (task-specific failure modes) and pass them to
the example's worker and verifier prompts. Follow three phrasing rules:
- Treat every contract item as a check to evaluate, never a claim to assume.
- Every trap ships with a carve-out that names the legitimate case.
- If a verifier encounters an unexplained state change, record
cause: unknown
rather than inventing a mechanism.
Before injecting an instruction, confirm every artifact it names is present in that
same prompt.
If the task has no meaningful near-misses or traps, pass neither; the example must
state that no goal contract was supplied. These rules come from the measured
CDC-steal evidence summary.
The emitted-script gate treats notDone in all four archetypes and traps in
review/design as non-adaptable seams. Design workflows must preserve this verifier
line verbatim: Reject any candidate whose key step presupposes a sub-solution as hard as the brief.
When the task is a review and a review skill exists, the flagship
review-until-dry.js composes that skill rather than reimplementing review — see
"Skill-as-stage" below.
3. Emit + (gated) fire
Emit the adapted script + a one-line provenance note (which archetype, what you
changed). Then run the parse-check: node scripts/check.mjs <script>.
Firing is gated. wfgen always composes; running the script (Workflow({script}))
requires the ultracode opt-in. If ultracode is off, emit the script and the
run instruction; do not fire.
The reducer invariant (non-negotiable)
Any step that combines, filters, selects, summarizes, deduplicates, accepts,
rejects, or passes worker output downstream is a reducer — and it must produce
or update a decision ledger. A bare agent("synthesize these") is the
failure mode: it launders unverified worker output into fluent final truth.
Every emitted script returns the canonical envelope:
{ ok, archetype, effort, result,
ledger: { accepted, rejected, conflicts, minority, evidence,
nextAction: "accept" | "retry" | "revise" | "block" | "escalate" },
observability: { rounds?, agents?, verifierTier?, effects?, isolation? } }
The generic ledger is necessary but not sufficient — each archetype carries its own
evidence-item schema (understand: claim+shard+quote+caveat; design:
candidate+scorecard+rejected-tradeoff+minority; review: finding+severity+location+
evidence; migrate: item+diff+command+test-result+repair). See borrowed-schemas.md.
Verification hierarchy (ordered; tier rises with effort, before width)
Trust comes from the strongest available contract at each join, in this order:
- execution — tests pass, code runs, the diff applies (the only hard oracle)
- verifier — a separate checker (cheaper to check than to produce)
- tool / ground-truth — citations resolve, tool output confirms
- heterogeneous — a different model family / evidence / objective
- judge — LLM-as-judge with position-bias controls
- consensus — N agents agree (the weakest; a fallback, never "verified")
Cheap deterministic gates (the preflight() regex/string-match) run first and
fail-closed, before spending an agent. The judge runs last, and any novel
blocking finding it introduces is re-grounded by a fresh verifier (post-judge
hallucination gate).
The effort knob (one args object — not a code fork)
const KNOB = {
low: { maxRounds: 1, dryStop: 1, verify: false },
med: { maxRounds: 3, dryStop: 2, verify: false },
high: { maxRounds: 4, dryStop: 2, verify: true, jurors: 3 },
}[args.effort || 'med']
Effort raises the verification tier and round budget, not a fixed fleet of
identical workers — correlated errors mean N clones ≈ one vote. Use the ordinal
QualityRating (POOR<FAIR<GOOD<EXCELLENT) as the verifier verdict and min_rating
as the threshold; return best-so-far, never last-seen (a too-high bar loops
forever). "Spare tokens" buys deeper verification + cross-model jurors, not width.
Skill-as-stage (the composition seam)
wfgen composes whatever skills you provide as units, rather than reimplementing
their logic — and is comfortable with any schema-returning skill, not wired to a
specific one. The seam: a
named, schema-returning unit invoked by reference —
async function runUnit(skill, input, schema, phase) {
return agent(`Run /${skill} on:\n${input}\nReturn ONLY results matching the schema.`,
{ label: `unit:${skill}`, phase, schema })
}
Composition is opt-in and graceful: pass reviewSkill: '<yours>' to compose it,
or omit it and the example runs a self-contained inline reviewer (zero local skills
required — so wfgen works for anyone out of the box).
When you DO compose a skill that is itself a fan-out orchestrator (e.g. a
persona-based /code-review that already dedups + cross-promotes with confidence
anchors), do not nest a second reducer over it:
- the wfgen loop owns rounds + cross-round SEEN-dedup + budget + convergence;
- each round invokes the skill once, in a fresh fork (never shown the prior
ledger — so a re-raised finding is real corroboration);
- dedup uses the skill's
fingerprint as a public seam (advisory; record
sourceSkillVersion) with a local fallback over stable fields (normalized
location + title + severity + category) when absent — never depend on its internals;
- the outer loop still has a reducer: it emits a ledger (accepted-new /
rejected-dupes / cross-round-conflicts / preserved minority / nextAction).
Diagnose (deferred to v2)
A mode that reads an existing workflow script + its run output and flags
uncontracted reducers (a bare agent("synthesize") where a ledger belongs) is
planned but not in V0. For now, scripts/check.mjs catches the structural cases.
What wfgen is NOT
It targets the native Claude Code dynamic Workflow tool only — deterministic,
in-process JS over agent/parallel/pipeline/phase.
It does not ship a primitive/axis vocabulary, an archetype DSL, a template-assembly
engine, a meta.joins manifest, a contract linter, or a formal IR. Those were
considered and cut (see foundation.md — "deferred / v2 if earned"). The examples
carry the discipline; the parse-check guards it; that's the whole skill.