The floor asks the seat's versions, bridge_tags and indexer_requirements; a silent source is named in the floor finding and woken by an event of that source (the holder's published, the forge bridge's next message, the indexer's next manifest-accepted) or its module's condition clearing, or by hand (builder_floor_again), never by a timer (design 51 *The floor*, ADR 0617 §1). The old form's window (ADR 0624 §1-§3, ADR 0618 §2): each counted engine gets the newest form it reads among those written, the new form only once every present engine states it, the oldest kept in the single reference; a node not serving manifest-reads counts as the baseline; a node dropped by hand counts no more; old-form-closed is said once, and a node holding it seven days is a finding. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
166 lines
6.0 KiB
Go
166 lines
6.0 KiB
Go
package versions
|
|
|
|
import (
|
|
"slices"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
// The old reference form's window (design 51 *Every build keeps the old reference form*, ADR 0624, ADR 0618 §2-§4,
|
|
// DLV-B9). The builder writes into every build key each reference form a counted node-engine states, the oldest of them
|
|
// into the key's single reference and the others beside it; the new form only once every present node-engine states it.
|
|
// The window closes when no counted node-engine states the old form alone. What each node states is read through the
|
|
// node-engine seat's verb `manifest-reads` at the builder's start and again on the seat's event
|
|
// `manifest-reads-changed`, never by a watch of the node-engine's state and never on a timer.
|
|
|
|
// NodeReads is one counted node: what its node-engine states of the reference forms, and whether it is present. Stated
|
|
// nil is a node-engine that states nothing, or does not serve `manifest-reads` yet: read as the baseline's forms.
|
|
type NodeReads struct {
|
|
Node string `json:"node"`
|
|
Present bool `json:"present"`
|
|
Stated []string `json:"stated,omitempty"`
|
|
// Serves is false while the node-engine does not serve `manifest-reads`: it counts as stating the baseline.
|
|
Serves bool `json:"serves"`
|
|
}
|
|
|
|
// FormsAnswer is what the builder writes into a build key, and why.
|
|
type FormsAnswer struct {
|
|
// Single is the form written into the key's single reference: the oldest a counted engine states.
|
|
Single string `json:"single"`
|
|
// Beside are the other forms written beside it, oldest first.
|
|
Beside []string `json:"beside,omitempty"`
|
|
// Open is whether the old form's window is open: a counted engine states the oldest form alone.
|
|
Open bool `json:"open"`
|
|
// Holding are the counted nodes whose engine states the oldest form alone (present or away), sorted.
|
|
Holding []string `json:"holding,omitempty"`
|
|
}
|
|
|
|
// Forms decides the reference forms of new build keys. forms are every reference form the builder can write, oldest
|
|
// first; baseline what an engine stating nothing reads; dropped the nodes a person dropped from the count by hand
|
|
// (builder_old_form, a hand act), which then count no more. A node leaves the count when it is retired (by hand here),
|
|
// never because it is away (ADR 0624 §1).
|
|
func Forms(forms, baseline []string, nodes []NodeReads, dropped map[string]bool) FormsAnswer {
|
|
if len(forms) == 0 {
|
|
return FormsAnswer{}
|
|
}
|
|
oldest, newest := forms[0], forms[len(forms)-1]
|
|
type counted struct {
|
|
node string
|
|
reads []string
|
|
}
|
|
var cs []counted
|
|
everyPresentNewest := true
|
|
var holding []string
|
|
for _, n := range nodes {
|
|
if dropped[n.Node] {
|
|
continue
|
|
}
|
|
stated := n.Stated
|
|
if !n.Serves || len(stated) == 0 {
|
|
stated = baseline
|
|
}
|
|
var reads []string
|
|
for _, f := range forms {
|
|
if slices.Contains(stated, f) {
|
|
reads = append(reads, f)
|
|
}
|
|
}
|
|
if n.Present && !slices.Contains(reads, newest) {
|
|
everyPresentNewest = false
|
|
}
|
|
if len(reads) == 1 && reads[0] == oldest {
|
|
holding = append(holding, n.Node)
|
|
}
|
|
cs = append(cs, counted{n.Node, reads})
|
|
}
|
|
// The new form is written once every present engine states it (ADR 0618 §2); each counted engine then gets the
|
|
// newest form it reads among those written, so a form leaves once no counted engine needs it (ADR 0624 §1). An
|
|
// engine reading none of them is held by the assigner (ADR 0624 §2), not served by an older form here.
|
|
keep := map[string]bool{}
|
|
if everyPresentNewest {
|
|
keep[newest] = true
|
|
}
|
|
for _, c := range cs {
|
|
for i := len(c.reads) - 1; i >= 0; i-- {
|
|
if f := c.reads[i]; f != newest || everyPresentNewest {
|
|
keep[f] = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
var kept []string
|
|
for _, f := range forms {
|
|
if keep[f] {
|
|
kept = append(kept, f)
|
|
}
|
|
}
|
|
if len(kept) == 0 {
|
|
kept = []string{oldest} // nothing counted: the oldest stays, so no engine is cut off
|
|
}
|
|
sort.Strings(holding)
|
|
var beside []string
|
|
if len(kept) > 1 {
|
|
beside = kept[1:]
|
|
}
|
|
return FormsAnswer{Single: kept[0], Beside: beside, Open: len(holding) > 0, Holding: holding}
|
|
}
|
|
|
|
// OldFormWindow is the window's record: whether it is open, who holds it and since when, and the nodes dropped by
|
|
// hand; what builder_old_form answers and what `old-form-closed` carries when it closes.
|
|
type OldFormWindow struct {
|
|
Open bool `json:"open"`
|
|
Holding []string `json:"holding,omitempty"`
|
|
Since map[string]time.Time `json:"since,omitempty"`
|
|
Dropped []Dropped `json:"dropped,omitempty"`
|
|
Closed time.Time `json:"closed,omitempty"`
|
|
// LastHolding are the nodes that held it last, said when it closes.
|
|
LastHolding []string `json:"last_holding,omitempty"`
|
|
}
|
|
|
|
// Dropped is one node dropped from the count by hand: a hand act with who and why.
|
|
type Dropped struct {
|
|
Node string `json:"node"`
|
|
By string `json:"by"`
|
|
Why string `json:"why"`
|
|
At time.Time `json:"at"`
|
|
}
|
|
|
|
// FindingAfter is how long a node holds the window open before it is the builder's finding naming the node.
|
|
const FindingAfter = 7 * 24 * time.Hour
|
|
|
|
// Step moves the window to what Forms answered at now: it answers the new window, whether it closed in this step (so
|
|
// the builder says `old-form-closed` and its build-log line once), and the nodes holding it past FindingAfter (the
|
|
// finding, cleared when the window closes).
|
|
func (w OldFormWindow) Step(a FormsAnswer, now time.Time) (next OldFormWindow, closed bool, overdue []string) {
|
|
next = OldFormWindow{Dropped: w.Dropped, Since: map[string]time.Time{}}
|
|
if !a.Open {
|
|
if w.Open {
|
|
next.Closed, next.LastHolding = now, w.Holding
|
|
return next, true, nil
|
|
}
|
|
next.Closed, next.LastHolding = w.Closed, w.LastHolding
|
|
return next, false, nil
|
|
}
|
|
next.Open, next.Holding = true, a.Holding
|
|
for _, n := range a.Holding {
|
|
since, had := w.Since[n]
|
|
if !had {
|
|
since = now
|
|
}
|
|
next.Since[n] = since
|
|
if now.Sub(since) >= FindingAfter {
|
|
overdue = append(overdue, n)
|
|
}
|
|
}
|
|
return next, false, overdue
|
|
}
|
|
|
|
// DroppedSet is the dropped nodes as Forms takes them.
|
|
func (w OldFormWindow) DroppedSet() map[string]bool {
|
|
out := map[string]bool{}
|
|
for _, d := range w.Dropped {
|
|
out[d.Node] = true
|
|
}
|
|
return out
|
|
}
|