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 critic -> green commit

verify_ac runs the unit’s executable acceptance criteria. conformance is a hard-fail gate. The critic is an independent pass, not the context that wrote the code. Each ruling is appended to a decisions ledger, so it survives a context reset - a decision nobody can find was not made.

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 - the handoff records what is carried
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 reached its goal owes a retrospective. A run that stopped for any other reason - budget spent, a unit blocked, you called it off - owes the person picking it up one thing: a document that says where to start.

/sdlc-studio handoff generate

It is a join over evidence the run already produced. It names every non-terminal unit with a pointer to start from - the failing criterion, the check it stalled at, the blocker, or the file - records the open decisions, links itself from the retro, and writes a worklist the next run can read straight back.

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