ADR-0073: Team live-run fixes — dataflow fan-out scheduling, board-driven item gating, retry reopen¶
Date: 2026-10-21
Status¶
Accepted
Context¶
The first live run of the team workflow (session 7c0abf71, a six-item game
build, 3 h 12 m) finished and its board was consistent, but the review found
three engine problems that cost real time or silently disabled a feature, plus
smaller rough edges:
- Refinement retries were dead. A worker marks its item
donebefore its evaluation, andBoard.Claimrefuses adoneitem even for its owner. When an evaluator saidneeds_refinement, the retried worker's mandatoryboard_claimwas refused and it did no work. - A second pass re-ran the whole fan-out.
more_workreopened one item, but all six were dispatched again; five workers answeredSKIPPEDand their evaluators still ran (36 requests, ≈38K completion tokens, ≈11 min). Item 6, the only item with real work, depends on all five, so its rework could not start until their wasted chains had finished — it began 10.7 min after the stand-up instead of at once. The same dead retry had earlier turned item 6's refinement attempt into a no-op even though its evaluator had already diagnosed the defect (and the fix); the coordinator then rediscovered it. - Dependency waves were barriers. Items were scheduled wave by wave, so an item waited for unrelated members of an earlier wave. In this plan the effect was small — item 6 depends on every other item and so waits for the slowest (item 3) either way; only item 5 (depends on item 1 alone) started ≈3 min later than possible — but it is a general inefficiency for plans with partial dependencies.
- Rough edges: the inner retry loop and the outer pass loop both exposed a
variable called
pass(the coordinator's "pass 2/3" read oddly and the panel labelled both loops "pass N"); board labels kept the plan's**bold**; a model repeating aboard_postduplicated it; token usage landed in oneprimary:workflowbucket with no per-role split.
Decision¶
Fix each in the engine as a generic, definition-driven capability — no code
path keys on a workflow name; team.yaml just uses the new fields.
- Retry reopen. A verdict rule may carry
board_status(validated: board definition, inside aparallel_group, settable status). When it fires, the current fan-out item's board entry is set to that status;todoreleases the claim, so the retried worker claims it again.Board.Claimadditionally treats a re-claim by the owner of anin_progressitem as idempotent (retry and resume), not only a still-claimedone. - Item gating.
parallel_group.skip_if_board_statusleaves out items whose board status is listed ([done]forteam). Skipped items are not dispatched, count as finished for their dependents, and keep the result the group'ssave_asaggregate held from the previous run, so the aggregate always describes every item. The coordinator reopens what needs rework (todo); everything else is left alone — noSKIPPEDworker turns, no due-diligence evaluator turns. - Dataflow scheduling. (A general improvement; it does not shorten this
run's critical path, see above.) The fan-out starts an item as soon as its own
depends_onitems have finished, bounded bymax_concurrency; waves remain only as the up-front cycle / missing-reference check. A failed dependency still releases its dependents (unchanged). - Names and labels.
team.yaml's inner loop isworker_attempt_loopwithiteration_var: attempt; the panel labels any<…>_<noun>_loop[N]as "N" from the id itself. Board labels are de-markdowned ( cleanBoardLabel); an exact repeat of a recent finding from the same sender and scope is absorbed. - Attribution. The interpreter puts the stage's role on the turn context
(
workflow.WithTurnRole); a workflow step executor tags its usage…:workflow:<role>, which keeps prefix queries working. - Designer guidance in
team.yamldiscourages a catch-all integration item that depends on every other (it serializes the tail even with dataflow scheduling).
Consequences¶
- A
teamrun does less redundant work in later passes. Replaying the first live run's timeline: ≈5–7 % fewer completion tokens, ≈6–8 % fewer fresh prompt tokens, ≈2–3 % fewer cached input tokens, and ≈14–24 min (≈7–12 %) less wall time — the dominant cost, the plan's serial item-3 → item-6 chain, is untouched (see the planner guidance). - Existing definitions are unaffected: both new fields are optional, an unset
skip_if_board_statusdispatches every item, and a definition withoutboard_statusbehaves as before. - Scheduling is by item, so progress events interleave differently; the panel and plan views already key off active/completed path snapshots and need no change.
- The other built-ins:
swarmgains the dataflow scheduling and the planner guidance (it fans out withdepends_onbut has no board and no repeated fan-out, so the retry-reopen and item-gating fields do not apply);devandpairare sequential and unaffected except for the shared fixes — per-role token tags, logging, and the snapshot-root rule (PR #300), whose absence made every built-in's panel draw a running workflow as done (pinned byTestWorkflowPanel_BuiltinsShowRunInProgressover dev, pair, swarm and team). - Not changed: the milk log stays off unless
otel.enabledis set, and debug log rotation can split a long run's evidence across files.