Skip to content

Delivery

You approve the plan, and the run goes. What happens then is deterministic enough to describe.

Each unit runs the same lifecycle:

decompose -> build (TDD) -> verify_ac -> conformance -> independent review -> green commit

verify_ac runs the unit’s executable acceptance criteria. conformance is a hard-fail gate. The review is one independent reviewer, never the context that wrote the code: briefed with critic.py brief, recorded with critic.py record, in at most two rounds - see reviewing a unit. Each ruling is appended to a decisions ledger, so it survives a context reset - a decision nobody can find was not made.

A question inside one seat’s lens - a dependency pin, a test convention - is ruled by that seat and recorded with decisions.py rule, so the run does not stop to ask you.

Every plan states whether the batch may be built in parallel worktrees, and why.

Parallel is offered only when the batch partitions into two or more file-disjoint groups. A one-unit batch, an all-coupled batch, or a unit with no declared Affects is delivered sequentially.

Three things count as coupling that people routinely miss:

  • Test files. A shared test module conflicts on merge exactly as a shared source module does.
  • A shared temp directory. A worktree isolates the tree; it does not isolate /tmp.
  • The build tooling itself. A unit that changes the pre-commit hook is coupled to every other unit in the batch, whatever its Affects says.

The plan also emits a lane partition - the batch split so no file appears in two lanes - derived from the same Affects and verify files the clusters use. On one measured run, seven file-disjoint lanes worked; two units that straddled two lanes each were deliberately held back and built afterwards, and no lane collided with another.

A run does not stop because one unit is stuck. At three attempts, or on a repeated failure signature, loop_guard quarantines the unit: it is marked Blocked, logged with why, skipped, and the batch continues.

The completion oracle declares the batch done only when every unit is terminal - Done or Blocked. A blocked unit is a result, not an omission.

With model-tier routing enabled, a failed attempt escalates one model tier before the cap quarantines, so cheap units run on your smaller model and the hard ones climb.

A run is a controllable object. Every control below is recorded, so the close can say what the delivered batch was and why it differs from the plan.

Terminal window
# trade units - one recorded decision, not a drop that happens to sit beside an add
sprint.py batch swap --out US0001,US0002 --in US0003 --reason "the blocking one first"
# pull a unit from the batch the done-gate reads
sprint.py batch drop US0001 --reason "premise unbuilt"
# put one in, under the same gates as the rest
sprint.py batch add US0004
# put an epic's stories in as one priced set - the growth is one number, not five entries
sprint.py batch add-epic --epic EP0010 --status Ready
# raise the ceiling on the record
sprint.py appetite resize --units 16 --reason "an epic joined the batch"
# end a run that will not reach its goal
sprint.py stop --reason "the dependency slipped"
# resume a stopped run rather than minting a fresh one over the same work
sprint.py reopen --reason "the dependency landed"

When a unit needs a ruling only you can give, the run does not stall on it and it does not guess. The unit is set aside and the batch continues:

/sdlc-studio sprint decision defer --unit US0001 \
--question "should a superseded verdict still count its reviewer?" \
--option "a|yes - the historical fact stands" \
--option "b|no - supersession retires the attribution"
/sdlc-studio sprint decision list # every accumulated decision, asked together
/sdlc-studio sprint decision resolve --index 1 --choice a # your ruling, recorded on the run and the ledger

Questions arrive together, structured, at the stop - rather than interrupting you one at a time across a run.

A run that stopped short - budget spent, a unit blocked, you called it off - hands over what is left on the report you sign. Its known-issues section lists every open finding raised in the run and every carried unit, and the next sprint plan names that report and its open items unasked. To plan them as the next run:

/sdlc-studio sprint plan --worklist RPT0001

A run ended by sprint.py stop --force files no report, so the next plan names the units the stop waived instead. No command writes a separate handoff document; handoff.py show still prints the remaining work without writing anything.

sprint.py breakdown --stories Ready --bugs Open # the census the planner reads
sprint.py close --retro RETRO0001 --dry-run # every refusal the close would raise, writing nothing
/sdlc-studio status # the dashboard, and the open run