Files
mesh-catalog/modules/mesh-delivery/cmd/mesh-delivery/table.go
T
jochen 6327f297ea
mesh/merge-gate pass: builds gitea, mesh-delivery → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
Cover a held merge only when the forge says a later delivered merge contains it (review of #150)
Merge order on a protected trunk was the whole proof; the review asked for ancestry. The gitea module
gains gitea_contains, a read-only compare of two commits (total_commits 0 is contained), and mesh-delivery
asks it once per pair, off the lock like its plan ask, keeping the answer on the delivery. A forge that
cannot say leaves the merge held, not covered. Merge order stays the cheap first filter.

A later delivery now carries a module only when its own check passed (so a merge held without a passing
check is closed only by one that passed), its walk is of its own merge commit on the same trunk, and its
steps say the module reached every machine running it, not one. The words say what is known: a later
merge that contains it was delivered.
2026-10-09 02:57:18 +02:00

540 lines
23 KiB
Go

package main
import (
"errors"
"fmt"
"strings"
"time"
)
// The state table (novox/hq ADR 0239 decision 2, to-be 47): every transition a delivery may take, each with
// its guard, compiled once. The code moves a delivery only through Apply, which finds the row for the
// delivery's state and the event, runs its guard and refuses anything else, naming what was asked. A test
// walks the table: every row it holds, and every pair it does not.
// State is where a delivery is.
type State string
// The states, in the order a delivery passes through them. None is a delivery not yet made: the rows from
// it are how one comes to exist.
const (
None State = ""
Proposed State = "proposed"
Checked State = "checked"
Ready State = "ready"
Rejected State = "rejected"
Published State = "published"
Delivering State = "delivering"
Held State = "held"
Delivered State = "delivered"
Failed State = "failed"
Superseded State = "superseded"
Stopped State = "stopped"
// Retired is history: a merge this owner heard only from the forge's word, long after it happened —
// replayed from the bus when it first held the seat. It reached the machines before this owner existed,
// and nothing is delivered, released or put back for it (novox/hq issue 313).
Retired State = "retired"
// Covered is a merge a later one that contains it delivered: every module it moves was sent to every machine
// running it by the finished walk of a later merge into the same trunk, whose own check passed and which the
// forge says contains this one. It is not walked itself, and it is no failure: its pull request says covered,
// as a success (novox/hq ADR 0270). A revert merged in between is still possible: covered says what was
// delivered, not what runs.
Covered State = "covered"
)
// AllStates are every state a delivery can be in, for the table's own test and its verb.
var AllStates = []State{Proposed, Checked, Ready, Rejected, Published, Delivering, Held, Delivered, Failed,
Superseded, Stopped, Retired, Covered}
// Final is whether nothing follows a state.
func (s State) Final() bool {
return s == Delivered || s == Failed || s == Superseded || s == Stopped || s == Retired || s == Covered
}
// Reached is whether a final state says the change reached the machines, or had nothing to reach them
// with: what a group counts as delivered.
func (s State) Reached() bool { return s == Delivered || s == Retired || s == Covered }
// Event is what moves a delivery.
type Event string
// The events. Observed ones are taken by settle whenever their guard holds; the others are acts — a
// person's, or healer H2's — taken only when asked.
const (
EvAnnounced Event = "announced" // the forge announced a pull request's head
EvAppeared Event = "appeared" // a trunk commit the mesh is delivering, with no pull request known
EvAdopted Event = "adopted" // a walk already running at the switch, or on the controller's own path
EvChecked Event = "checked" // its check's verdict arrived
EvAccepted Event = "accepted" // the verdict passes
EvRefused Event = "refused" // the verdict fails, or the check could not run
EvRecheck Event = "recheck" // a person asked for it again, or its group changed
EvReask Event = "re-ask" // healer H2 asked a check asked and not answered once more
EvNewHead Event = "new-head" // a newer head of the same pull request
EvClosed Event = "closed" // the pull request closed unmerged
EvMerged Event = "merged" // it reached the trunk
EvMergedUnchecked Event = "merged-unchecked" // it reached the trunk without a passing check
EvGo Event = "go" // its walk started
EvHold Event = "hold" // something holds it for a person
EvRelease Event = "release" // a person's word that it goes on
EvDone Event = "done" // every machine of its deploy plan took it, or nothing was to be sent
EvFailed Event = "failed" // its walk failed: a gate, a build, a machine
EvSuperseded Event = "superseded" // a newer delivery to the same trunk took over its walk
EvStop Event = "stop" // a person stopped it, or its group did
EvHistory Event = "history" // made from the forge's word for a merge long past: history
EvRetire Event = "retire" // a person's word that a held delivery is history, never to be walked
EvCovered Event = "covered" // a later merge into the same trunk delivered every module it moves
EvLifted Event = "lifted" // what held it for a person is no longer so: its group's composed check passed
)
// Facts are what a guard reads beyond the delivery itself: the moment, and who asks, for an act.
type Facts struct {
Now time.Time
By string
Why string
}
// Row is one transition the table holds.
type Row struct {
From []State
Event Event
To State
// Guard says, in words, what must be true; Holds checks it, answering why not.
Guard string
Holds func(d *Delivery, f Facts) error
// Act says the row is taken only when asked — a person's or H2's — never by settle.
Act bool
// Says is the transition's why when nobody gave one: the fact that moved it, rather than the guard.
Says func(d *Delivery) string
}
func unless(cond bool, why string) error {
if cond {
return nil
}
return fmt.Errorf("%s", why)
}
var notFinal = []State{Proposed, Checked, Ready, Rejected, Published, Delivering, Held}
// Table is every transition there is. The order within one state is the order settle tries them.
var Table = []Row{
{From: []State{None}, Event: EvAnnounced, To: Proposed,
Guard: "a pull request's head the forge announced",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Number > 0 && d.Commit != "", "no pull request's head")
}},
{From: []State{None}, Event: EvAppeared, To: Held,
Guard: "a commit on the trunk, its walk waiting, and no pull request known: it waits for a person",
Holds: func(d *Delivery, f Facts) error {
return unless(d.MergedAs != "" && d.HeldWhy != "", "no trunk commit, or nothing says why it is held")
}},
{From: []State{None}, Event: EvAdopted, To: Delivering,
Guard: "a walk already running: at the switch, or on the controller's own path",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.Started(), "no running walk")
}},
// History is taken before anything else of a delivery off the trunk: a merge long past, heard only from
// the forge's word, is neither checked, held nor walked (novox/hq issue 313).
{From: []State{Proposed, Checked, Ready, Rejected}, Event: EvHistory, To: Retired,
Guard: "history: made from the forge's word for a merge on the trunk heard " + historyLate.String() + " or more " +
"after it happened — replayed when this owner first held the seat — and no walk of it open",
Holds: func(d *Delivery, f Facts) error {
if d.History == "" {
return errors.New("nothing says it is history")
}
return isHistory(d)
}},
{From: []State{Held}, Event: EvRetire, To: Retired, Act: true,
Guard: "a person's word, with why, for history only: made from the forge's word for a merge on the trunk heard " +
historyLate.String() + " or more after it happened, and no walk of it open — never released or delivered",
Holds: func(d *Delivery, f Facts) error {
if f.Why == "" || f.By == "" {
return errors.New("a retire says who and why")
}
return isHistory(d)
}},
{From: []State{Proposed, Checked, Ready, Rejected}, Event: EvNewHead, To: Superseded,
Guard: "a newer head of the same pull request",
Holds: func(d *Delivery, f Facts) error { return unless(d.NewerHead != "", "no newer head") }},
{From: []State{Proposed, Checked, Ready, Rejected}, Event: EvClosed, To: Stopped,
Guard: "the pull request closed unmerged",
Holds: func(d *Delivery, f Facts) error { return unless(d.ClosedUnmerged, "the pull request is open") }},
{From: []State{Ready}, Event: EvMerged, To: Published,
Guard: "on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move",
Holds: func(d *Delivery, f Facts) error {
return unless(d.MergedAs != "" && (d.Walk != nil || d.MovesNothing()), "not on the trunk yet")
}},
{From: []State{Proposed}, Event: EvChecked, To: Checked,
Guard: "the verdict names this commit",
Holds: func(d *Delivery, f Facts) error { return unless(d.Check != nil, "no verdict") }},
{From: []State{Checked}, Event: EvAccepted, To: Ready,
Guard: "the gate passed or warned, and the repository's own check did not fail",
Holds: func(d *Delivery, f Facts) error { return unless(d.Check.Passes(), "the verdict does not pass") }},
{From: []State{Checked}, Event: EvRefused, To: Rejected,
Guard: "the gate failed or could not run, or the repository's own check failed",
Holds: func(d *Delivery, f Facts) error { return unless(!d.Check.Passes(), "the verdict passes") }},
{From: []State{Proposed, Checked, Rejected, Held}, Event: EvDone, To: Delivered,
Guard: "merged, and its plan builds nothing for a walk to move — nothing at all, or only a new module the " +
"controller builds and sends nowhere: nothing to deliver and nothing for a person to decide, whatever its check said",
Holds: func(d *Delivery, f Facts) error {
return unless(d.MergedAs != "" && d.MovesNothing() && d.Walk == nil, "not merged, or a walk moves it")
},
Says: func(d *Delivery) string { return "merged, and nothing for a walk to move: " + d.Plan.Summary }},
{From: []State{Ready, Held}, Event: EvCovered, To: Covered,
Guard: "merged, no walk of its own open, and every module it moves sent to every machine running it by the " +
"finished walk of a later merge into the same trunk, whose own check passed and which the forge says contains " +
"it: a later merge that contains it was delivered",
Holds: func(d *Delivery, f Facts) error {
switch {
case d.MergedAs == "" || d.CoveredBy == "":
return errors.New("not merged, or nothing later carried its modules")
case d.Walk != nil && (d.Walk.State == walkBuilding || d.Walk.State == walkRolling):
return fmt.Errorf("its own walk %s is open: the controller delivers it", d.Walk.ID)
}
return nil
},
Says: func(d *Delivery) string {
return "covered: a later merge that contains it was delivered: " + d.CoveredBy
}},
{From: []State{Proposed, Checked, Rejected}, Event: EvMergedUnchecked, To: Held,
Guard: "merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on",
Holds: func(d *Delivery, f Facts) error { return unless(d.MergedAs != "", "not merged") }},
{From: []State{Rejected, Ready}, Event: EvRecheck, To: Proposed, Act: true,
Guard: "a person asked, with why, or its group changed",
Holds: func(d *Delivery, f Facts) error { return unless(f.Why != "", "a recheck says why") }},
{From: []State{Proposed}, Event: EvReask, To: Proposed, Act: true,
Guard: "its check was asked and no verdict came within its bound, and it was not re-asked before: healer H2 " +
"asks it once more, with why — after that it is the operator's",
Holds: func(d *Delivery, f Facts) error {
switch {
case f.Why == "" || f.By == "":
return errors.New("a re-ask says who and why")
case d.Check != nil:
return errors.New("its verdict is known")
case d.CheckAsk == nil:
return errors.New("its check was never asked: the tick asks it, not H2")
case d.CheckAsk.Reasked > 0:
return errors.New("its check was re-asked once already: the operator's")
}
return nil
}},
{From: []State{Published}, Event: EvSuperseded, To: Superseded,
Guard: "a newer delivery to the same trunk took over its walk",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.State == walkSuperseded, "its walk is not superseded")
}},
{From: []State{Published}, Event: EvDone, To: Delivered,
Guard: "nothing for a walk to move",
Holds: func(d *Delivery, f Facts) error { return unless(d.Walk == nil && d.MovesNothing(), "a walk moves it") }},
{From: []State{Published, Ready}, Event: EvHold, To: Held,
Guard: "something holds it for a person: its group's composed check did not pass for the heads that merged, " +
"or it merged and no walk was opened for it",
Holds: func(d *Delivery, f Facts) error {
return unless(d.HeldWhy != "" && d.MergedAs != "", "nothing holds it, or it is not on the trunk")
}},
{From: []State{Published}, Event: EvGo, To: Delivering,
Guard: "its walk started: let go by this owner in its group's order, by a person, or on the controller's own path",
Holds: func(d *Delivery, f Facts) error { return unless(d.Walk != nil && d.Walk.Started(), "its walk waits") }},
{From: []State{Held}, Event: EvGo, To: Delivering,
Guard: "its walk started on a word that was not this owner's: the controller's own path, which waits for " +
"nobody, or a person's `plans go`",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.Started() && (!d.Walk.Waited() || d.Walk.LetGoByAPerson()),
"its walk was not started by a person or the controller's own path")
}},
{From: []State{Held}, Event: EvLifted, To: Published,
Guard: "held only for its group's composed check, which has since passed or warned for the heads that merged: " +
"it is published again and waits for its turn",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Lifted != "" && d.HeldWhy == "" && d.MergedAs != "" && walkCanGoOn(d), "nothing lifted its hold")
},
Says: func(d *Delivery) string { return "lifted: " + d.Lifted }},
{From: []State{Held}, Event: EvRelease, To: Delivering, Act: true,
Guard: "a person's decision, with why",
Holds: func(d *Delivery, f Facts) error {
return unless(f.Why != "" && f.By != "", "a release is a person's word, with why")
}},
{From: []State{Delivering}, Event: EvSuperseded, To: Superseded,
Guard: "a newer delivery to the same trunk took over its walk",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.State == walkSuperseded, "its walk is not superseded")
}},
{From: []State{Delivering}, Event: EvDone, To: Delivered,
Guard: "its walk is done: every machine of its deploy plan passed, or was left as its policy says",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.State == walkDone, "its walk is not done")
}},
{From: []State{Delivering}, Event: EvStop, To: Stopped,
Guard: "its walk was stopped through this owner",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.State == walkFailed && d.Walk.StoppedBy != "", "its walk was not stopped")
}},
{From: []State{Delivering}, Event: EvFailed, To: Failed,
Guard: "its walk failed: a gate on a first machine (what it carried put back), a build, a machine",
Holds: func(d *Delivery, f Facts) error {
return unless(d.Walk != nil && d.Walk.State == walkFailed && d.Walk.StoppedBy == "", "its walk did not fail")
}},
{From: notFinal, Event: EvStop, To: Stopped, Act: true,
Guard: "a person, with why, or its group stopped by a member before it",
Holds: func(d *Delivery, f Facts) error { return unless(f.Why != "" && f.By != "", "a stop says who and why") }},
}
// walkCanGoOn is whether a delivery published again would be delivered by what it has: its own walk still
// open, or nothing for a walk to move.
func walkCanGoOn(d *Delivery) bool {
if d.Walk == nil {
return d.MovesNothing()
}
return d.Walk.State == walkBuilding || d.Walk.State == walkRolling
}
// historyLate is how long after its merge a merge heard only from the forge's word is history: the forge
// announces a merge within seconds, so one heard this late was replayed from the bus's history.
const historyLate = 15 * time.Minute
// isHistory is whether a delivery is history, answering why not: made from the forge's word at its merge, on
// the trunk, its merge heard historyLate or more after it happened, and no walk of it open or waiting — a
// walk the controller keeps for it is a delivery, never history.
func isHistory(d *Delivery) error {
switch {
case d.MergedAs == "":
return errors.New("it is not on the trunk")
case !d.FromForgesWord():
return errors.New("this owner heard its head: it is not history made from the forge's word")
case d.MergedAt.IsZero() || d.Created.Sub(d.MergedAt) < historyLate:
return fmt.Errorf("its merge was heard %s after it happened, within %s: a live merge, not history",
d.Created.Sub(d.MergedAt).Round(time.Second), historyLate)
case d.Walk != nil && (d.Walk.Waits || d.Walk.State == walkBuilding || d.Walk.State == walkRolling):
return fmt.Errorf("its walk %s is open: the controller delivers it", d.Walk.ID)
}
return nil
}
// rowFor is the row a state and event name; nil when the table holds none.
func rowFor(from State, ev Event, act bool) *Row {
for i := range Table {
r := &Table[i]
if r.Event != ev || r.Act != act {
continue
}
for _, s := range r.From {
if s == from {
return r
}
}
}
return nil
}
// ErrRefused is a transition the table does not hold, or whose guard does not.
type ErrRefused struct {
ID string
From State
Event Event
Why string
}
func (e ErrRefused) Error() string {
from := string(e.From)
if from == "" {
from = "nothing"
}
return fmt.Sprintf("%s: %s from %s is refused — %s", e.ID, e.Event, from, e.Why)
}
// Apply takes one transition, or refuses it. The delivery keeps the transition, bounded.
func Apply(d *Delivery, ev Event, act bool, f Facts) (Transition, error) {
r := rowFor(d.State, ev, act)
if r == nil {
kind := "observed"
if act {
kind = "asked"
}
return Transition{}, ErrRefused{ID: d.ID, From: d.State, Event: ev,
Why: "the table holds no such transition (" + kind + ")"}
}
if err := r.Holds(d, f); err != nil {
return Transition{}, ErrRefused{ID: d.ID, From: d.State, Event: ev, Why: "its guard does not hold: " + err.Error()}
}
why := f.Why
if why == "" && r.Says != nil {
why = r.Says(d)
}
if why == "" {
why = r.Guard
}
t := Transition{At: f.Now, From: d.State, To: r.To, Event: ev, Why: why, By: f.By}
d.State, d.Since = r.To, f.Now
d.Transitions = append(d.Transitions, t)
if len(d.Transitions) > keptTransitions {
d.Transitions = d.Transitions[len(d.Transitions)-keptTransitions:]
}
return t, nil
}
// keptTransitions is how many transitions a delivery keeps; its note on the commit keeps every one.
const keptTransitions = 100
// settle takes every observed transition whose guard holds, in the table's order, until none does — so a
// delivery always stands where its facts put it, whatever order they arrived in.
func settle(d *Delivery, f Facts) []Transition {
var taken []Transition
for range len(Table) + 1 {
moved := false
for i := range Table {
r := &Table[i]
if r.Act || !contains(r.From, d.State) {
continue
}
if t, err := Apply(d, r.Event, false, f); err == nil {
taken = append(taken, t)
moved = true
break
}
}
if !moved {
break
}
}
return taken
}
func contains(states []State, s State) bool {
for _, x := range states {
if x == s {
return true
}
}
return false
}
// Bounds are how long a delivery may be in a state before `stalled` lists it, and what healer H2 may do
// then — always a transition the table holds, never one of its own (novox/hq ADR 0239 decision 9).
type Bound struct {
For time.Duration
// H2 is the transitions H2 may take after the bound, by event; empty when the state is the operator's.
H2 []Event
Says string
}
// Bounds are every state's.
var Bounds = map[State]Bound{
Proposed: {For: time.Hour, H2: []Event{EvReask}, Says: "its check was asked and not answered: H2 asks it once " +
"more, then the check's own watchdog (S6) and the operator speak for it"},
Checked: {For: time.Minute, Says: "a verdict is decided at once"},
Published: {For: 30 * time.Minute, H2: []Event{EvSuperseded, EvGo}, Says: "its walk waits for its turn or its word"},
Held: {For: 24 * time.Hour, Says: "it waits for the operator"},
Delivering: {For: 2 * time.Hour, H2: []Event{EvDone, EvSuperseded, EvFailed}, Says: "its walk runs"},
}
// checkGrace is how long a proposed delivery is left to the controller's own check of its announcement before
// this owner asks the check itself: longer than a check takes from announcement to verdict. A head adopted while
// it already waited — announced again from the bus's history — is asked once it has waited this long too.
const checkGrace = 15 * time.Minute
// boundSince is when a delivery's bound runs from: its state's start, except for a proposed delivery whose
// check this owner asked — its bound is "asked and not answered", so it runs from the ask. One never asked
// runs from its start plus the grace before it is asked: an ask that cannot be made is still listed.
func boundSince(d *Delivery) time.Time {
if d.State == Proposed {
if d.CheckAsk != nil {
return d.CheckAsk.At
}
return d.Since.Add(checkGrace)
}
return d.Since
}
// pastBound is whether a delivery has been where it is longer than its state's bound.
func pastBound(d *Delivery, now time.Time) bool {
b, bounded := Bounds[d.State]
return bounded && !d.State.Final() && now.Sub(boundSince(d)) > b.For
}
// StepState is where one machine stands in a delivering walk.
type StepState string
// The machine steps (ADR 0239 decision 2): sent, judged, passed or failed, and put back.
const (
StepSent StepState = "sent"
StepJudging StepState = "judging"
StepPassed StepState = "passed"
StepFailed StepState = "failed"
StepRolledBack StepState = "rolled-back"
)
// StepTable is every move a machine's step may make. From "" is a step first heard of.
var StepTable = map[StepState][]StepState{
"": {StepSent, StepJudging, StepPassed, StepFailed, StepRolledBack},
StepSent: {StepJudging, StepPassed, StepFailed},
StepJudging: {StepPassed, StepFailed},
StepFailed: {StepRolledBack},
}
// StepMay is whether a step may move from one state to another: directly, or through states the walk's
// record passed between two readings of it (sent, judged and failed and put back, read once).
func StepMay(from, to StepState) bool {
if from == to {
return true
}
seen := map[StepState]bool{}
next := []StepState{from}
for len(next) > 0 {
s := next[0]
next = next[1:]
for _, t := range StepTable[s] {
if t == to {
return true
}
if !seen[t] {
seen[t] = true
next = append(next, t)
}
}
}
return false
}
// TableText is the table as the `table` verb answers it.
func TableText() map[string]any {
var rows []map[string]any
for _, r := range Table {
from := make([]string, 0, len(r.From))
for _, s := range r.From {
if s == None {
from = append(from, "(none)")
continue
}
from = append(from, string(s))
}
kind := "observed"
if r.Act {
kind = "asked"
}
rows = append(rows, map[string]any{"from": strings.Join(from, ", "), "event": r.Event, "to": r.To,
"guard": r.Guard, "taken": kind})
}
bounds := map[string]any{}
for s, b := range Bounds {
var h2 []string
for _, e := range b.H2 {
h2 = append(h2, string(e))
}
bounds[string(s)] = map[string]any{"bound": b.For.String(), "h2": h2, "says": b.Says}
}
steps := map[string][]StepState{}
for from, to := range StepTable {
name := string(from)
if name == "" {
name = "(first heard)"
}
steps[name] = to
}
return map[string]any{"transitions": rows, "bounds": bounds, "machine-steps": steps}
}