The sprint pattern
One sprint's gated pipeline: execute → gate → one bounded fix → re-gate → review → re-gate → post-review repair-or-revert → commit. Composed into `milestone`'s sprint loop by an `include` step — material for an includer, never selected or run on its own.
[header] name = "sprint" version = "1.5" description = """ One sprint's gated pipeline: execute → gate → one bounded fix → re-gate → review → re-gate → post-review repair-or-revert → commit. Composed into `milestone`'s sprint loop by an `include` step — material for an includer, never selected or run on its own. """ entry = "build_task" included_only = true
Only what this pipeline actually reads. The plan source and the baseline-relaxed flag belong to the milestone file's planning half, which declares them and passes these two through to its inclusion.
[[header.parameters]] name = "prompt_prefix" kind = "string" default = "" description = "Optional prefix for prompt ids this shared pipeline resolves, e.g. design/briefs/." [[header.parameters]] name = "review_prompt" kind = "string" default = "review" description = "Prompt id for the review stage; pass-specific sprint pipelines may provide their own."
The inner-unit state contract
The machine-readable files and durable completion effect the production sprint world consumes. Paths are relative to this pattern instance's orchestration directory, so sub-pattern invocations read and write their nested unit's state rather than repository-root paths.
[inner_unit_state] completion = { commit_block = "commit_task", record = "commit-and-ledger-done", idempotent_green_noop = true }
The whole contract lives here because a composed pattern admits at most one — two files declaring
one is a refusal — and it belongs with the completion contract, which is this file's: commit_task
records a sprint DONE. That is also why the three troubleshoot channels are declared below even
though the ladder producing and reading them is the milestone's. They are per-sprint-attempt state,
durably retired the moment a sprint goes DONE (retire_completed_task_channels in
src/engine/run/pattern_interpreter.rs), while pattern-level [[channels]] are left alone there.
The reviewer's scope verdict. The degrade default of complete is the legacy executor's silent
scope-complete-on-parse-failure bias made explicit and forkable: a careful fork writes retry here.
[[inner_unit_state.channels]] name = "review_scope" path = "state/review-scope.toml" producer = "{{review_prompt}}" values = ["complete", "retry", "blocked"] degrade_default = "complete" [[inner_unit_state.channels]] name = "retry_note" path = "state/retry-note.md" producer = "{{review_prompt}}"
An executor or fixer that cannot proceed says so here, and the run stops rather than gating noise.
[[inner_unit_state.channels]] name = "executor_blocked" path = "state/executor-blocked.toml" producer = "{{prompt_prefix}}execute" values = ["blocked"] [[inner_unit_state.channels]] name = "executor_blocked_note" path = "state/executor-blocked-note.md" producer = "{{prompt_prefix}}execute" [[inner_unit_state.channels]] name = "fixer_blocked" path = "state/fixer-blocked.toml" producer = "fix" values = ["blocked"] [[inner_unit_state.channels]] name = "fixer_blocked_note" path = "state/fixer-blocked-note.md" producer = "fix"
The post-review fixer's revert verdict. Only repair_review is ever told about this channel:
revert means review's own edits — the provably isolated cause of a post-review red — are cosmetic
damage, so Gantry restores the pre-review mark instead of fixing forward. Every other fix leaves the
file absent, and the producing prompt's channel clearing removes a stale verdict before each fix.
[[inner_unit_state.channels]] name = "review_repair" path = "state/review-repair.toml" producer = "fix" values = ["revert"]
The troubleshoot agent's declared outcomes, written only when the repair could not be verified:
retry asks for a briefed rollback-and-retry (the note reaches the next execute through
prepare-retry), handover stops the unit with the document as the stop artifact. A verified repair
writes neither — the gate, not the agent's word, is what advances the run. The handover channel's
required first line (<split|replan|flaky|contract|other> — <one sentence>) keeps the
machine-readable summary the old investigation_recommendation carried; the rest is the document the
next reader starts from.
[[inner_unit_state.channels]] name = "troubleshoot_disposition" path = "state/troubleshoot-disposition.toml" producer = "troubleshoot" values = ["retry", "handover"] degrade_default = "handover" [[inner_unit_state.channels]] name = "troubleshoot_note" path = "state/troubleshoot-note.md" producer = "troubleshoot" [[inner_unit_state.channels]] name = "handover" path = "state/handover.md" producer = "troubleshoot"
build_task
Execute the sprint — fall-through, because the gate that follows renders the verdict on whatever it did to the tree, in both directions. The mutating stages protect sprint briefs and run-control artifacts relative to this child orchestration, and confinement-check unsandboxed runs for writes that escaped the intended worktree.
[[blocks.build_task]] type = "agent" role = "build" prompt = "{{prompt_prefix}}execute" failure = "fall-through" protected = ["PROGRESS.md", ".preamble.sys.md", "bin", ":(glob)[0-9][0-9]-*.md"] confine = true [[blocks.build_task]] type = "conditional" condition = { kind = "channel-value", channel = "executor_blocked", value = "blocked" } then = "executor_blocked_stop" [[blocks.build_task]] type = "gate" if = "red" then = "repair" else = "after_green_gate"
On green — initially or after the bounded fix — an unchanged tree records a no-op DONE and skips review and commit; otherwise review and commit.
[[blocks.after_green_gate]] type = "conditional" condition = { kind = "inner-unit-green-noop" } then = "record_noop_done" [[blocks.after_green_gate]] type = "conditional" condition = { kind = "flag", value = "true" } then = "finish_task"
repair
The one bounded fix: a single repair agent, then the re-gate. It inherits execute's protected-brief,
protected-run-control and confinement contract. The empty situation clause keeps the shared fix.md
on its plain repair reading — only repair_review fills the clause, with the revert option.
[[blocks.repair]] type = "agent" role = "build" prompt = "fix" stage = "fix" failure = "fall-through" protected = ["PROGRESS.md", ".preamble.sys.md", "bin", ":(glob)[0-9][0-9]-*.md"] confine = true template_values = { SITUATION_CLAUSE = "" } [[blocks.repair]] type = "inner-unit" op = "note-channel" channel = "fixer_blocked"
The red-gate exit into the milestone file's escalation entry — half of the mutual contract that file states.
[[blocks.repair]] type = "gate" if = "green" then = "after_green_gate" else = "troubleshoot_or_handover"
finish_task
The mark records the pre-review working tree — the gate just proved it green — so the post-review fixer's revert option has a recorded point to restore. Marks are per-sprint-attempt state and never survive a rollback or the sprint's completion.
[[blocks.finish_task]] type = "git" op = "mark" name = "pre_review"
Review, which may edit. It carries the same protected and confinement contract as execute and fix. Protected sprint-plan edits are withheld as a draft: started and completed briefs route to the frozen-unit violation halt, while future-brief proposals go to the declared replanner. The fixed-point handler is deliberate — if the replanner changes plan files too, Gantry observes again and repeats until the plan stops changing, bounded by spend guards rather than a loop counter.
[[blocks.finish_task]] type = "agent" role = "review" prompt = "{{review_prompt}}" failure = "degrade" degrade_default = "complete" protected = ["PROGRESS.md", ".preamble.sys.md", "bin", ":(glob)[0-9][0-9]-*.md"] protected_draft = { path = "state/review-plan-draft.diff" } observed_plan_change = { changed = "replan_remaining_tasks_from_review_draft", unchanged = "verify_review_edits", source_plan_violation = "failure", frozen_unit_violation = "failure", handler = { kind = "fixed-point", block = "replan_remaining_tasks_from_review_draft" } } confine = true template_values = { REVIEW_SUBJECT = "sprint", ALTITUDE_CLAUSE = "Review altitude: sprint. The unit being reviewed is one sprint. You may edit the working tree to make safe improvements. The remaining plan means the later, not-yet-built sprints in this same sprint 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." }
Re-gate review's edits. A review that edits and leaves the gate red gets one bounded fix of its own before the attempt is discarded into the milestone's recovery rules.
[[blocks.verify_review_edits]] type = "gate" if = "green" then = "after_green_review" else = "after_red_review_gate" [[blocks.after_red_review_gate]] type = "conditional" condition = { kind = "channel-value", channel = "review_scope", value = "blocked" } then = "review_blocked_stop" [[blocks.after_red_review_gate]] type = "conditional" condition = { kind = "flag", value = "true" } then = "repair_review"
repair_review
Review's own edits turned a green gate red: give them the same bounded fix a red-after-execute gets,
rather than discarding the executor's working code along with them. This is one fix agent per
gate, not per sprint — the milestone file's recovery_attempts budget is what bounds the sprint.
This fix alone holds the revert option: the gate was green before review ran, so review's diff is
the bisect. Writing value = "revert" routes to revert_review_edits instead of the fix-forward
re-gate.
[[blocks.repair_review]] type = "agent" role = "build" prompt = "fix" stage = "fix" failure = "fall-through" protected = ["PROGRESS.md", ".preamble.sys.md", "bin", ":(glob)[0-9][0-9]-*.md"] confine = true
The revert file's path is written into the clause directly, with $orchestration expanded by the
engine, rather than through a second {{REVERT_PATH}} variable: template values fill in one
alphabetical pass, so a variable referenced from inside another value's text would be filled before
the clause carrying it is inserted, and would survive as a literal marker.
[blocks.repair_review.template_values] SITUATION_CLAUSE = """ Situation: the gate was green before a review agent edited this tree, so the review's own edits are the sole cause of the red — the diff below ends with them. Decide which kind of edit broke it. If the breaking edits are cosmetic, refactoring, or style changes, do not repair them: write the file `$orchestration/state/review-repair.toml` as TOML containing exactly the line `value = "revert"` and stop — gantry will restore the pre-review tree, keeping the executor's work. If an edit strengthened a check, corrected a wrong assertion, or surfaced a genuine defect, the red is information, not damage: fix forward as described below and leave that file absent. The review's own written rationale, visible in the diff and its notes, is available to decide.""" [[blocks.repair_review]] type = "inner-unit" op = "note-channel" channel = "fixer_blocked"
The fixer chose revert: restore the pre-review mark rather than judging the fixer's (non-)edits.
[[blocks.repair_review]] type = "conditional" condition = { kind = "channel-value", channel = "review_repair", value = "revert" } then = "revert_review_edits"
The post-review red-gate exit into the milestone file's _review escalation entry — the other half
of the mutual contract. A green repair rejoins the ordinary post-review routing, so review's scope
verdict still decides the sprint's fate.
[[blocks.repair_review]] type = "gate" if = "green" then = "after_green_review" else = "troubleshoot_or_handover_review"
revert_review_edits
Put the project tree back to the recorded pre-review point, preserving the orchestration directory
exactly like a project-only reset, then verify with the re-gate — green by construction, but
nothing untested reaches a commit. A red re-gate means the restored tree is not what the mark
promised, and it falls into the same review troubleshoot entry as a failed fix-forward.
[[blocks.revert_review_edits]] type = "git" op = "reset" scope = { kind = "to-mark", mark = "pre_review" } [[blocks.revert_review_edits]] type = "gate" if = "green" then = "after_green_review" else = "troubleshoot_or_handover_review"
The reviewer's scope verdict decides the sprint's fate on a green post-review gate.
[[blocks.after_green_review]] type = "conditional" condition = { kind = "channel-value", channel = "review_scope", value = "blocked" } then = "review_blocked_stop" [[blocks.after_green_review]] type = "conditional" condition = { kind = "channel-value", channel = "review_scope", value = "complete" } then = "commit_task"
Commit the sprint's work, marking it done so the milestone loop advances. The completion contract above names this block.
[[blocks.commit_task]] type = "inner-unit" op = "commit-task" note = "task complete"
An unchanged tree with a green gate records the sprint DONE without review or a new work commit.
[[blocks.record_noop_done]] type = "inner-unit" op = "record-noop-done" note = "already satisfied; gate green; recorded done" [[blocks.record_noop_done]] type = "conditional" condition = { kind = "flag", value = "true" } then = "continue"
The blocked stops
review_blocked_stop is reached from this file's post-review routing and from the milestone's
recovery rules alike.
[[blocks.executor_blocked_stop]] type = "inner-unit" op = "stop-blocked" channel = "executor_blocked" note_channel = "executor_blocked_note" [[blocks.review_blocked_stop]] type = "inner-unit" op = "stop-blocked" channel = "review_scope" note_channel = "retry_note"
replan_remaining_tasks_from_review_draft
Observed future-brief edits from review are proposal-style: the protected files were restored, the retained draft is supplied to the same sprint planner, and the fixed-point observation decides whether another handler round is needed before the review's code edits are verified.
[[blocks.replan_remaining_tasks_from_review_draft]] type = "agent" role = "plan" prompt = "{{prompt_prefix}}write-sprints" inputs = ["$work-list.future"] withheld_draft = "state/review-plan-draft.diff" failure = "halt" [[blocks.replan_remaining_tasks_from_review_draft]] type = "git" op = "commit"
sprint — one sprint's gated pipeline
execute → gate → one bounded fix → re-gate → review → re-gate → post-review repair-or-revert → commit. Every code-mutating agent stage is followed by a gate, and every gate that goes red gets one bounded fix.
This is the body
milestone.tomlcomposes into its sprint loop with anincludestep, and it isincluded_onlybecause that is exactly what it is: material for an includer, not a pattern a run starts. Its red-gate exits branch into escalation entries the milestone file defines, so it never validates alone. Together the two files are the whole statement of the compiled per-sprint pipeline Gantry ran before patterns became first class.