Fold a pending assignment into the value of its assignment key (issue 1094)

A wait for a build is the value of <node>.<module> with state pending and a pending object (id p-<unix nanoseconds>, a string), so every reader that skips a value with a state skips it, and the controller's own lists skip it through Stands. The transitions are compare-and-set on the key's revision (claim, release, settle, repoint, mark); assigning over a wait carries it into the value that stands, applied when it was waiting; a plan pass never overwrites it; one open wait per machine and module, and a key that holds the module refuses a new one. The table is no longer read or written, ForgetEndedPending is gone (no key is removed), and a wait of a machine the store dropped is none. Callers change type only; their conditions are the next issue.
This commit is contained in:
2026-10-18 05:17:30 +02:00
parent 0149a3d7de
commit 88ea361bf6
11 changed files with 620 additions and 147 deletions
+54 -1
View File
@@ -189,9 +189,62 @@ type Assignment struct {
// Ended is when the assignment ended (an unassignment): absent while it stands. **No key of the chain is removed**
// (ADR 0408 §4), so an unassigned module keeps its key, saying so; every reader treats a value with Ended, or with a
// State, as no assignment. State is, in a later delivery, the pending assignment waiting for its build (issue 1094).
// State, as no assignment. State is StatePending while the key holds an assignment waiting for its build, or one that
// was never made (issue 1094).
Ended time.Time `json:"ended,omitzero"`
State string `json:"state,omitempty"`
// Pending is the assignment's wait for its build (novox/hq ADR 0261), kept in the value of the key it waits for and, when
// it is made, carried into the value that stands: the record of what became of the wait.
Pending *Pending `json:"pending,omitempty"`
}
// StatePending is the State of a value that holds no assignment but a pending one.
const StatePending = "pending"
// The states of a pending assignment (novox/hq ADR 0261).
const (
PendingWaiting = "waiting"
PendingApplying = "applying"
PendingApplied = "applied"
PendingRefused = "refused"
PendingExpired = "expired"
PendingWithdrawn = "withdrawn"
)
// Pending is an assignment kept until its module is registered: the build it waits for, where that build is from, and the
// times of its claim and its end. Its id is p-<unix nanoseconds of Since>, unique for a key, which holds one at a time.
type Pending struct {
ID string `json:"id"`
State string `json:"state"`
Build string `json:"build,omitempty"`
Repository string `json:"repository,omitempty"`
Path string `json:"path,omitempty"`
Since time.Time `json:"since"`
Note string `json:"note,omitempty"`
Settled *time.Time `json:"settled,omitempty"`
Raised *time.Time `json:"raised,omitempty"`
Acknowledged *time.Time `json:"acknowledged,omitempty"`
Cleared *time.Time `json:"cleared,omitempty"`
}
// Open says the assignment is still to be made.
func (p Pending) Open() bool { return p.State == PendingWaiting || p.State == PendingApplying }
// CanSettle says a pending assignment in state from may end in state to: a wait expires or is withdrawn, a claim becomes
// applied or refused, and nothing else.
func CanSettle(from, to string) bool {
switch from {
case PendingWaiting:
return to == PendingExpired || to == PendingWithdrawn
case PendingApplying:
return to == PendingApplied || to == PendingRefused
}
return false
}
// CanMove says a pending assignment in state from may go to state to without ending: claimed, or released again.
func CanMove(from, to string) bool {
return (from == PendingWaiting && to == PendingApplying) || (from == PendingApplying && to == PendingWaiting)
}
// Stands says the value is an assignment that holds: it has not ended and is not waiting in a state of its own.