The build pattern
The default milestone build: decompose a plan into ordered milestone briefs, then build each milestone as the shared milestone unit. Use for an ambitious plan larger than one work unit; the omakase route a flagless `gantry <plan>` takes.
[header] name = "build" version = "1.2" description = """ The default milestone build: decompose a plan into ordered milestone briefs, then build each milestone as the shared milestone unit. Use for an ambitious plan larger than one work unit; the omakase route a flagless `gantry <plan>` takes. """ entry = "main" display_flat_when_single_top_level = true
Both levels are declared up front, not the milestone alone: a run whose milestones are planned
but not yet decomposed is then already known to be two-level. Declaring only milestone left that
window looking flat, and the TUI sidebar drew the run title as a phantom milestone with the real
milestones nested under it. The leaf noun sprint matches the rows the milestone unit supplies,
so the two declarations dedup to one ["milestone", "sprint"] list rather than a spurious third
level. Single-milestone flatness is display behaviour only.
[[header.levels]] label = "milestone" [[header.levels]] label = "sprint"
Build's whole input contract: the run's own plan for a top-level build, goal's per-cycle plan when build runs as a sub-pattern.
[[header.parameters]] name = "plan_source" kind = "string" default = "plan.md" declared_plan_source = true description = "The plan prose or source path the milestone planner reads (the run's plan for a top-level build; goal's per-cycle plan when build runs as a sub-pattern)."
The milestone reviewer's verdict. The degrade_default also covers a review agent that failed
outright.
[[channels]] name = "milestone_review" path = "state/milestone-review.toml" producer = "review" values = ["continue", "halt"] degrade_default = "continue"
main
plan → about card → commit the briefs → empty campaign or full campaign.
The planner. halt: everything downstream builds on the milestone split. It carries no file
contract — an empty directory is the declared empty-campaign outcome below, not a halt. On resume
the briefs are already on disk and the production world skips this step rather than re-decomposing.
[[blocks.main]] type = "agent" role = "plan" prompt = "write-milestones" inputs = ["{{plan_source}}"] failure = "halt" skip_when_work_list_pre_populated = { source = { kind = "directory", path = "milestones" } } halt_message = "directory campaign planner stopped the run" template_values = { ORCH = "$orchestration/milestones" }
The about card is an ordinary agent-authored artifact. The roster announcement below only reads the persisted sidecar; it never dispatches a hidden decoration agent.
[[blocks.main]] type = "agent" role = "util" prompt = "write-about" inputs = ["$plan.name", "$work-list.roster:milestones"] produces = ["about.md"] read_only = true failure = "note-and-continue"
Commit the briefs as an audit artifact before building — a no-op on resume, when the planner was skipped and nothing is staged.
[[blocks.main]] type = "git" op = "commit" commit_fallback = "gantry: milestones {{PLAN_NAME}}" commit_log_base = "milestones-commitmsg" skip_when_work_list_pre_populated = { source = { kind = "directory", path = "milestones" } } skip_when_work_list_empty = { source = { kind = "directory", path = "milestones" } }
Branch on the same work-list source the loop will consume, so an empty directory takes the declared empty-campaign outcome instead of entering a loop with nothing to build.
[[blocks.main]] type = "conditional" condition = { kind = "work-list-empty", source = { kind = "directory", path = "milestones" } } then = "empty_campaign" else = "build_campaign"
build_campaign
The roster is announced before the loop that consumes it, so it names its list explicitly — there is no active work-list context to inherit. Same source as the loop below and the branch above.
[[blocks.build_campaign]] type = "inner-unit" op = "announce-roster" work_list = { source = { kind = "directory", path = "milestones" } }
The campaign loop, re-enumerated after every iteration so a review's future-brief edits are built before the loop reaches them. The survival invariants ride the directory source's defaults: a DONE milestone vanishing is a corrupt-plan halt, inserted milestones are announced, and the resume position is recomputed as first-not-DONE each pass.
[[blocks.build_campaign]] type = "loop" body = "build_one_milestone" rules = "campaign_ending" work_list = { source = { kind = "directory", path = "milestones" } }
build_one_milestone
One milestone, run as the shared unit and wrapped in the same bookkeeping envelope regardless of campaign length. The engine re-points the nested unit's orchestration, ledger and preamble per milestone; a single-milestone run uses the same nested ledger and only presents flat in displays.
[[blocks.build_one_milestone]] type = "git" op = "capture-head" [[blocks.build_one_milestone]] type = "sub-pattern" pattern = "milestone" parameters = { plan_source = "$work-list.entry" }
The milestone review may improve the tree, but its edits are verified before the milestone is recorded DONE. It declares no required output on purpose: a declared output is a hard file contract the interpreter halts on, and this review is advisory — no verdict means "continue".
protected covers the campaign's own briefs. Edits to future briefs are withheld as a draft and
routed through the replanner below; edits to started or completed briefs are frozen-unit violations.
The handler is a fixed point: if the replanner also changes plan files, Gantry observes again and
repeats until the plan stops changing. Spend guards bound the pathological case — a hard counter
would risk accepting a still-changing plan.
[[blocks.build_one_milestone]] type = "agent" role = "review" prompt = "review" protected = ["milestones"] protected_draft = { path = "state/milestone-review-plan-draft.diff" } observed_plan_change = { changed = "replan_future_milestones_from_review_draft", unchanged = "verify_milestone_review_edits", source_plan_violation = "failure", frozen_unit_violation = "failure", handler = { kind = "fixed-point", block = "replan_future_milestones_from_review_draft" } } failure = "degrade" degrade_default = "continue" template_values = { REVIEW_SUBJECT = "whole milestone", ALTITUDE_CLAUSE = "Review altitude: milestone. The unit being reviewed is one fully built milestone. You may edit the working tree to make safe improvements. The remaining plan means the later, not-yet-built milestone briefs. Beyond correctness, judge the architecture: responsibilities and interfaces, dependencies, maintainability, and whether the structure will suffice for the remaining plan. Express any required brief changes by editing the scoped plan files; protected future-brief edits are withheld as a draft and routed through the observed plan-change handler." }
Gate the review's edits, then commit them.
[[blocks.verify_milestone_review_edits]] type = "gate" if = "red" then = "failure" [[blocks.verify_milestone_review_edits]] type = "git" op = "commit"
The reviewer's verdict reaches the run here, and a continue verdict records the milestone DONE.
[[blocks.build_one_milestone]] type = "conditional" condition = { kind = "channel-value", channel = "milestone_review", value = "halt" } then = "failure" [[blocks.build_one_milestone]] type = "inner-unit" op = "finish-entry"
replan_future_milestones_from_review_draft
The withheld draft is handed back to the same planner, scoped to the future briefs, then committed and gated.
[[blocks.replan_future_milestones_from_review_draft]] type = "agent" role = "plan" prompt = "write-milestones" inputs = ["$work-list.future"] withheld_draft = "state/milestone-review-plan-draft.diff" failure = "halt" template_values = { ORCH = "$orchestration/milestones" } [[blocks.replan_future_milestones_from_review_draft]] type = "git" op = "commit" [[blocks.replan_future_milestones_from_review_draft]] type = "gate" if = "red" then = "failure"
The campaign ends when every milestone is DONE. An unbuilt milestone is never empty here — the branch above kept a zero-milestone plan out of this loop entirely.
[[rule_blocks.campaign_ending]] condition = { kind = "work-list-empty" } outcome = "success"
empty_campaign
The planner produced nothing to do. A first-class terminating disposition a parent (goal) branches
on, rather than a stop-message substring. The reserved empty target reaches the same disposition,
but a named block is what carries the message.
[[blocks.empty_campaign]] type = "terminate" disposition = "empty-campaign" message = "the planner authored no milestone files — see the plan card / logs"
build — the default milestone campaign
What a flagless
gantry <plan>runs: a planner writes a directory of milestone briefs, then a loop builds each brief through the sharedmilestonesub-pattern, reviewing and optionally re-planning between them. Build is itself invocable whole —goalreuses it as its cycle body — soplan_sourceis its input contract andempty-campaignis a first-class outcome a parent branches on throughsub-pattern-outcome.As-built reference:
run_whole_build_with_worldinsrc/engine/run/driver_milestone.rs.