1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
# Recursive Story Decomposition — Design Spec
**Date:** 2026-07-08
**Status:** Approved
---
## Goal
Give claudomator a native way to drive a stated user intent through review-gated
implementation, to arbitrary depth, without a human or chatbot controller
hand-orchestrating dispatch → review → fix → advance the way this session's
tasks-board build required. Replace that hand-driven loop with one recursive,
uniform mechanism inside claudomator itself.
---
## Context
Building claudomator's own "unified Tasks board" web feature (2026-07-06/08)
used an adapted `subagent-driven-development`: a human-facing plan document
with 8 sequential tasks, each dispatched to claudomator as an implementer,
each reviewed by a separately-dispatched local reviewer, fixes re-dispatched
on failure, advanced by hand. It worked, but the retro on it surfaced a real
architecture gap (see memory `claudomator-sequential-orchestration-gap` for
the narrative): `internal/scheduler.StoryOrchestrator` already automates a
Builder → 4-parallel-Evaluators → Arbitration → `REVIEW_READY` pipeline, but
that pipeline runs exactly once, at a story's outer level, for one atomic
deliverable. It does not decompose, and it does not apply itself recursively
to sub-pieces of a larger story.
Two early design drafts for closing this gap were rejected during
brainstorming, for reasons worth recording so they aren't re-proposed later:
- **A separate "chain mode" with a distinct sequential pipeline** (single
evaluator per task, `Story.Mode` field) was rejected as unnecessary
special-casing — two pipelines selected by a flag, instead of asking why
the one that already exists (arbitrated multi-evaluator review) shouldn't
just apply everywhere.
- **A distinct "Planner" role that runs once, up front, and hands off to a
different "Implementer" role** was rejected for the same reason — it
special-cases "decide how to break this down" as something only one kind
of agent does at one point in time, rather than a judgment every task
makes about its own assignment, recursively.
The design below is the corrected shape: one role, one recursive mechanism,
applied at every depth.
---
## Design
### Story is the root of a tree, and only the root
A Story represents user intent — what a human (or a chatbot acting for one)
asked for. It carries `Spec` (prose intent) and `AcceptanceCriteria`, and
owns the single human-facing accept-gate
(`POST /api/stories/{id}/accept`, unchanged from today). Nothing beneath the
root is a Story. A human's attention lives at this level; an agent driving
the work via MCP may need to inspect or adjust individual tasks at any
depth, but that's a different altitude of view onto the same tree, not a
different data model. `Epic` (grouping multiple Stories) is untouched by
this design — orthogonal, already exists.
### Every Task, at every depth, gets identical treatment
There is one role (`builder` — already named in existing seed data, unchanged
purpose: deliver work meeting its assignment's completion criteria). On
dispatch, a `builder`-role task is handed its assignment (a story's
`Spec`/`AcceptanceCriteria` if it's the root, or a parent task's own stated
subtask instructions/`AcceptanceCriteria` otherwise) and makes one judgment:
**is this minimal and targeted?**
- **Yes:** implement directly, commit, done — this node's "work" is its own
diff.
- **No:** decompose. Spawn subtasks via `spawn_subtask`, each carrying its
own `Instructions` and `AcceptanceCriteria` (the field already exists on
`Task`, mostly unused today). This node becomes a roll-up; its "work" is
the aggregate of its children once each has independently completed.
This recurses to arbitrary depth. A subtask that itself judges its own
assignment too large repeats the same decision. Depth is driven by how big
the actual work is — a well-scoped leaf doesn't decompose and is evaluated
once, same as any leaf; there's no depth ceiling imposed by the mechanism
itself (see Non-Goals for the cost implication).
This judgment has to actually be instructed, not just structurally
possible: the `builder` role's `RoleConfig.SystemPrompt` needs to be
written (or updated, if a draft already exists from prior seeding) to tell
the agent explicitly to make this decompose-or-implement call against its
own assignment, and to use `spawn_subtask`'s new `DependsOn` field when the
pieces it's creating have a genuine order dependency rather than being
independently parallel. Without this, the mechanism is inert plumbing — the
plan for this design must include writing/updating that prompt as a real
deliverable, not an assumed side effect of adding the data-model fields.
### `SubtaskSpec` gains `DependsOn`
Today, `agentchannel.SubtaskSpec` (`Name`, `Instructions`, `Model`,
`MaxBudgetUSD`, `Role`) has no way to express ordering between siblings —
every spawned subtask is structurally parallel, and `storeChannel.SpawnSubtask`
never sets `DependsOn` on the child it creates. Add `DependsOn []string` to
`SubtaskSpec`, wired through to `child.DependsOn` the same
backward-compatible-optional-field way `Role` was added ("every
pre-existing caller unchanged"). `spawn_subtask` already returns each new
task's ID, so a decomposing agent can spawn step 1, capture its ID, spawn
step 2 with `depends_on: [step1_id]`, and so on — or spawn three independent
branches followed by one integration task depending on all three. One
primitive expresses pure-sequential, pure-parallel, or mixed DAGs; nothing
needs to pick a mode. This reuses 100% of the existing `DependsOn`
dispatch-gating and cascade-fail-on-dependency-failure machinery already
built for top-level tasks — no new scheduling logic.
### Arbitrated review runs at every node, not once at the story's outer level
The existing Builder → 4-Evaluators → Arbitration pipeline generalizes: once
*any* node's own work is done (a leaf's direct commit, or a roll-up's
children having all independently completed), that node goes through the
same multi-angle evaluation against its own `AcceptanceCriteria` (the
story's, for the root; the parent-assigned criteria, for anything beneath
it). This is what catches integration problems that only exist at a roll-up
level and are invisible in any single child's own diff — exactly the shape
of the CSS cascade collision found in the tasks-board build's final
whole-branch review (a bug between Task 3's and Task 4's changes, invisible
in either task's own scoped review). Making arbitrated review the universal
mechanism means every roll-up gets that same integration check, not just
the outermost one, and not only when a human remembers to run one by hand.
### The fix-and-re-evaluate loop has to become real, not stay a documented simplification
Today, `StoryOrchestrator.finalizeArbitration` never actually parses the
Arbitration task's verdict — it always routes to `REVIEW_READY` regardless,
trusting a human to read the summary and manually set `NEEDS_FIX` if they
disagree (see `CLAUDE.md`'s own "Documented simplification" note on this).
That gap can't stay a simplification once arbitrated review is the
universal per-node mechanism instead of a rare final gate a human is
already watching for. This design requires: parse the verdict; on reject,
spawn a fix attempt (same node, same completion criteria) and re-run
evaluation; on approve, auto-proceed (matching today's existing
Builder/Evaluator/Arbitration auto-accept behavior). Repeated non-convergence
escalates through the `builder` role's `EscalationLadder` (existing
mechanism, same one `Scheduler` already uses for FAILED-task retry/escalate
— no new retry-cap concept to invent).
### MCP surface: story-level tools, additive to what exists
New: `create_story` (name, spec, acceptance_criteria, repository_url →
story_id), `get_story`, `list_stories`, `accept_story` (wraps the existing
`POST /api/stories/{id}/accept`). These didn't exist on chatbot MCP before —
today it's task-only. `submit_task` and the rest of the existing task-level
toolset are unchanged and remain the right choice for a genuinely
single-step ask where creating a story and running it through decomposition
judgment is unwarranted overhead.
---
## Non-Goals / Deferred
- **No depth or cost bound in this design.** Recursion is unbounded and
arbitrated review (4 parallel evaluators) runs at every node, which is a
real cost multiplier for a large, deep story — not addressed here. The
intended mitigation is architectural, not a cap: a leaf task that judges
itself minimal/targeted should default to a cheap/local model tier
(`LocalRunner`/tinyllama already exists for this; the `EscalationLadder`
already supports tiering from cheap to expensive), reserving expensive
models for roll-up judgment, arbitration, and escalation. This connects
directly to the same session's `--model` wiring fix
(`internal/executor/container.go`, commit `2d9ae7e`) — model selection
has to actually work before tiering leaf tasks to cheap models is
possible at all. Left as a distinct, future piece of work.
- **No resumable-orchestrator design here.** Retro item 4
(resume after session/model failures) is deliberately deferred until this
design's discipline — durable, DB/event-backed state per node, no
ledger-in-a-file — is actually built and proven. Resumability is a
property this design should make easier, not a feature to add on top of
it separately.
- **Epic is untouched.** Grouping multiple Stories under an Epic is an
existing, orthogonal concept this design doesn't change.
---
## Testing
- `internal/scheduler`: tests for the generalized per-node arbitrated-review
branch (fake store, assert correct spawn/evaluate/fix/advance sequencing
at both leaf and roll-up nodes) — extending the existing
`StoryOrchestrator` test style.
- `internal/executor`: tests for `SubtaskSpec.DependsOn` wiring through to
the created child task (mirrors existing `Role`-field tests in the same
package).
- `internal/api`: tests for the new `create_story`/`get_story`/
`list_stories`/`accept_story` chatbot MCP tools (mirrors existing
`chatbotmcp_test.go` conventions for the task-level tools).
- No browser/UI testing needed — backend and chatbot MCP only.
|