Examples
Example: Fan-Out / Join Research Line
A four-Station Assembly Line that demonstrates the v1.1 Fan-Out / Join extension — one Task fans out into parallel research children and joins their results back together.
What It Does
Takes a research question from the Task Card and runs four Stations:
- SCOPE — Claude breaks the question into 3–5 research topics and fans out one Child Task per topic
- RESEARCH — the children each research one topic in parallel
- SYNTHESIZE — the Join Station: waits for every child to settle, then merges their findings
- DELIVER — writes the final report
┌────────────┐
┌──────────►│ RESEARCH a │──────────┐
│ fan-out └────────────┘ settle │
┌───────┴──┐ ┌────────────┐ ▼
│ SCOPE │───────►│ RESEARCH b │───► ┌────────────┐ ┌─────────┐
│ (parent) │ parent └────────────┘ │ SYNTHESIZE │────►│ DELIVER │
└──────────┘ moves, ┌────────────┐ │ (join) │ └─────────┘
held │ RESEARCH c │───► └────────────┘
└────────────┘
The parent Task is the join: it moves to SYNTHESIZE after SCOPE succeeds, but its Job there is held until every child has Settled.
Assembly Line Definition
{
"id": "research-line",
"name": "Research Line",
"description": "Parallel research with fan-out/join",
"version": "1.1.0",
"owner": "your-username",
"project": "your-project",
"stations": [
{
"id": "scope",
"name": "SCOPE",
"trigger": {
"type": "dispatch_worker",
"labels": ["claude-code"],
"createAssemblyLineRepo": true,
"promptTemplate": "Research question: {{task.title}}\n\n{{task.description}}\n\nClone the Assembly Line Repository ($ASSEMBLY_LINE_REPO_URL) and write your scoping notes to reports/scope.md. Break the question into 3-5 independent research topics, then fan them out as instructed below. Commit, push, and conclude with success.",
"fanOut": {
"childColumnId": "research",
"joinColumnId": "synthesize",
"maxChildren": 5,
"instructions": "One child per topic. Put the topic and the exact questions to answer in the child's description."
}
}
},
{
"id": "research",
"name": "RESEARCH",
"trigger": {
"type": "dispatch_worker",
"labels": ["claude-code"],
"promptTemplate": "You are researching one topic of a larger question.\n\nTopic: {{task.title}}\n\n{{task.description}}\n\nClone the Assembly Line Repository ($ASSEMBLY_LINE_REPO_URL) and write your findings to reports/children/<your-task-title-slug>/findings.md. Commit, push, and conclude with success."
}
},
{
"id": "synthesize",
"name": "SYNTHESIZE",
"trigger": {
"type": "dispatch_worker",
"labels": ["claude-code"],
"promptTemplate": "Merge the parallel research results into one coherent analysis.\n\nOriginal question: {{task.title}}\n\nChild results ({{children.count}} children):\n{{children.results}}\n\nClone the Assembly Line Repository ($ASSEMBLY_LINE_REPO_URL). Each child's full findings are under reports/children/. Read them all, resolve contradictions, and write reports/synthesis.md. Commit, push, and conclude with success."
}
},
{
"id": "deliver",
"name": "DELIVER",
"trigger": {
"type": "dispatch_worker",
"labels": ["claude-code"],
"promptTemplate": "Turn reports/synthesis.md in the Assembly Line Repository into a polished final report at reports/final-report.md. Commit, push, and conclude with success."
}
}
],
"transitionRules": [
{ "id": "r1", "fromStationId": "scope", "toStationId": "synthesize", "condition": "success" },
{ "id": "r2", "fromStationId": "research", "toStationId": "synthesize", "condition": "success" },
{ "id": "r3", "fromStationId": "synthesize", "toStationId": "deliver", "condition": "success" }
]
}
Three rules do all the work:
- r1 moves the parent from SCOPE to the Join Station. Its dispatch there is held while children are unsettled.
- r2 is the natural child rule: each child that finishes RESEARCH moves to SYNTHESIZE — and because SYNTHESIZE is its parent's Join Station, arrival settles the child instead of dispatching it. Children never run the join.
- r3 carries the parent onward after the join Job succeeds.
Note the explicit rules: the reference implementation only auto-advances via explicit Transition Rules (see the divergence note in 03-assembly-lines.md). A child RESEARCH failure with no matching rule simply rests in place — which settles it with conclusion
failure, so a failed child never deadlocks the join.
Task Card
Title: How should a small team adopt agentic coding?
Description: Compare tooling, workflow changes, and security posture.
Audience: a 5-person product team already on GitHub.
What Happens (Step by Step)
| Step | Who | What |
|---|---|---|
| 1 | User | Submits Task Card — Task T queued at SCOPE |
| 2 | Runner | Claims the Job; SCOPE Agent writes reports/scope.md, picks 3 topics |
| 3 | Agent (SCOPE) | Calls POST .../tasks/T/fan-out with columnId: "research", 3 task cards, joinColumnId: "synthesize" |
| 4 | Server | Creates children C1 C2 C3 at RESEARCH (each sharing T's Assembly Line Repository), sets T.blockedBy = [C1, C2, C3], dispatches the children |
| 5 | Agent (SCOPE) | Concludes success → rule r1 moves T to SYNTHESIZE |
| 6 | Server | Dispatch of T at SYNTHESIZE finds unsettled blockers → held (join_waiting) |
| 7 | Runners | C1 C2 C3 run in parallel (parallelism = number of registered Runners) |
| 8 | Server | Each child concludes success → rule r2 moves it to SYNTHESIZE → arrival settles it (no Job runs) |
| 9 | Server | Last child settles → Join Release (at-most-once): T.childResults aggregated, held dispatch fires |
| 10 | Agent (SYNTHESIZE) | Prompt contains {{children.count}} = 3 and {{children.results}}; reads reports/children/*/findings.md, writes reports/synthesis.md, concludes success |
| 11 | Server | Rule r3 moves T to DELIVER; final report written |
| 12 | Server | No rule out of DELIVER → T rests (Settled success) |
ALP Concepts in This Example
| Concept | In the Research Line |
|---|---|
| Fan-Out | SCOPE's Agent spawns 3 children via the fan-out API |
| Parent-as-join | T itself waits at SYNTHESIZE — no separate join task |
| Child Task | C1 C2 C3 — ordinary Task Cards with parentTask = T |
| Join Station | SYNTHESIZE — children settle on arrival, only the parent runs it |
| Settled | What the join waits on; a failed child settles too |
| Join Release | At-most-once aggregation of childResults + release of the held dispatch |
| Shared repository | Children inherit T's Assembly Line Repository, write under reports/children/<slug>/ |
{{children.results}} | Per-child title/conclusion/summary injected into the join prompt |
| Declarative fan-out | trigger.fanOut makes the Server inject the exact API call into SCOPE's prompt |
Things to Try
- Kill a child's Runner mid-run — the child's Job is swept as
cancelled, which does not settle it. The join waits; retry the child, or force-release:POST .../tasks/T/release-join(unsettled children appear inchildResultsasunresolved). - Retry a failed child before the join releases — the dispatch clears its Settled state and the join re-arms for it.
- Call fan-out twice from SCOPE — the second call returns the existing children with
"reused": trueinstead of duplicating them. - Drag the held parent out of SYNTHESIZE — a manual move of a held parent is an implicit force release.