A-104: an optional step still waiting does not hold a pipeline's end

Only a required step still waiting or running holds endPipeline; a code review nobody gave on an optional policy no
longer keeps the pipeline open. The review verb itself is not built: see the report.

Builds: design 47 G §22, §70, §482, A-104

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-11 23:39:07 +01:00
parent 53db5c8dd9
commit f067f2cdb0
2 changed files with 28 additions and 2 deletions
@@ -272,8 +272,8 @@ func stepOfAsk(p seat.Pipeline, id string) int {
}
// endPipeline ends a pipeline whose every step ended: passed when every required step passed or was carried, else
// failed, naming each required step that held it (ADR 0666 §7, §9; design 47 G §24, §26). A failed optional step holds
// nothing. An approval step still waiting when a required step did not pass is `not asked` (§26). It says whether the
// failed, naming each required step that held it (ADR 0666 §7, §9; design 47 G §24, §26). A failed optional step, and an optional step
// still waiting, hold nothing. An approval step still waiting when a required step did not pass is `not asked` (§26). It says whether the
// pipeline ended now, its end count one higher.
func endPipeline(p *seat.Pipeline, now time.Time) bool {
if p.Outcome != seat.PipelineOutcomeRunning {
@@ -286,6 +286,10 @@ func endPipeline(p *seat.Pipeline, now time.Time) bool {
case s.Step == seat.StepResultStepApproval && s.Outcome == seat.StepResultOutcomeWaiting:
notAsked = i
continue
case s.Outcome == seat.StepResultOutcomeWaiting && s.Status != seat.StepResultStatusRequired:
// An optional step still waiting (a code review nobody has given) holds nothing: only a required one holds the
// pipeline's end (A-104).
continue
case s.Outcome == seat.StepResultOutcomeWaiting, s.Outcome == seat.StepResultOutcomeRunning:
return false
}
@@ -276,3 +276,25 @@ func TestApprovalEnds(t *testing.T) {
t.Errorf("asked %d; %+v", len(asker.asked), p)
}
}
// An optional step still waiting (a code review nobody has given) does not hold a pipeline's end; a required one does
// (design 47 G A-104).
func TestAnOptionalStepStillWaitingHoldsNoEnd(t *testing.T) {
now := time.Date(2026, 11, 11, 12, 0, 0, 0, time.UTC)
for name, c := range map[string]struct {
review string
ends bool
}{"optional review waiting": {seat.StepResultStatusOptional, true}, "required review waiting": {seat.StepResultStatusRequired, false}} {
p := seat.Pipeline{Outcome: seat.PipelineOutcomeRunning, Steps: []seat.StepResult{
{Step: seat.StepResultStepBuild, Status: seat.StepResultStatusRequired, Outcome: seat.StepResultOutcomePassed},
{Step: seat.StepResultStepReview, Status: c.review, Outcome: seat.StepResultOutcomeWaiting},
{Step: seat.StepResultStepApproval, Status: seat.StepResultStatusRequired, Outcome: seat.StepResultOutcomePassed},
}}
if got := endPipeline(&p, now); got != c.ends {
t.Errorf("%s: ended = %v, want %v", name, got, c.ends)
}
if c.ends && p.Outcome != seat.PipelineOutcomePassed {
t.Errorf("%s: outcome %s", name, p.Outcome)
}
}
}