Files
mesh-delivery/modules/mesh-delivery-builder/internal/versions/oldform.go
T
jschoubben 39db267630 D1 (builder): the builder names its own module and buckets from its credential's module
Its states' buckets, its finding observer, its build log entries and its step asks' asker take the credential's module (tools: the module the runtime serves them as) instead of constants of its name; builds.StateBucket/BuildsBucket replace the Bucket constants. The readers of the builds bucket (checker, view, grants) still name the builder: the next D1 subtask finds it by the seat mesh-builds.

Builds: design 47 G A-296, §428

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 22:34:08 +01:00

155 lines
6.4 KiB
Go

package versions
import (
"encoding/json"
"errors"
"fmt"
"slices"
"sort"
"strings"
"time"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds/forms"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/window"
)
// The old reference form's window (design 51 *Every build keeps the old reference form*, ADR 0624 §1, ADR 0618 §2-§4,
// DLV-B9). Which forms a build key keeps is mesh-delivery's sdk/forms (Kept, Choose), the counted nodes' statements the
// builder's state engine-reads; this is only the window over them: open while a counted node-engine, present or away,
// reads of some artifact kind none of the newest form the builder knows for it, so an older form stays in new build keys
// for it; closed, said once with `old-form-closed` and a log line, when none does. It moves on what moves a statement
// (the node-engine seat's verb `manifest-reads` and its event `manifest-reads-changed`), the assignments, a hand drop and
// each build key written; never on a timer.
// OldFormState is the builder's state of the window: one key, OldFormKey, an OldFormWindow. builder_old_form reads it.
const (
OldFormState = "old-form"
OldFormKey = "window"
)
// OldFormHeld is the finding of a node holding the window open for FindingAfter, about that node, cleared when the
// window closes or the node no longer holds it.
const OldFormHeld = "old-form-held"
// Holding are the counted nodes, those dropped by hand left out, whose engine reads, of some artifact kind fs knows, not
// the newest form of that kind: the nodes for which an older form stays in new build keys (forms.ArtifactForms.Kept). A
// node stating nothing reads the first forms (fs.Reads), sorted.
func Holding(fs forms.ArtifactForms, counted map[string][]string, dropped map[string]bool) []string {
newest := map[string]string{} // kind -> newest form
var kinds []string
for _, f := range fs {
for _, k := range f.Kinds {
if _, ok := newest[k]; !ok {
kinds = append(kinds, k)
}
newest[k] = f.Name
}
}
var out []string
for node, stated := range counted {
if dropped[node] {
continue
}
for _, k := range kinds {
if !fs.Reads(stated, newest[k]) {
out = append(out, node)
break
}
}
}
sort.Strings(out)
return out
}
// OldFormWindow is the window's record: whether it is open, who holds it and since when (window.WindowHeld, the part
// every window keeper shares), and the nodes dropped by hand; what builder_old_form answers and what `old-form-closed`
// carries when it closes.
type OldFormWindow struct {
window.WindowHeld
Dropped []Dropped `json:"dropped,omitempty"`
}
// Dropped is one retired node dropped from the count by hand (builder_old_form): 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"`
}
// OldFormClosedBody is the body of OldFormClosedEvent (design 51): when, the nodes that held it last and the nodes dropped
// by hand.
type OldFormClosedBody struct {
At time.Time `json:"at"`
LastHolding []string `json:"last_holding,omitempty"`
Dropped []Dropped `json:"dropped,omitempty"`
}
// 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 by mesh-sdk's one window rule (window.StepWindow): named is every counted node the read named,
// those dropped by hand included, and holding those of them holding the window (Holding). A read naming no node (no
// assignments read yet, an empty node_haves) judges nothing, so it never closes the window. It answers the new window,
// whether it closed in this step (so the builder says `old-form-closed` and its log line once), and the nodes holding it
// past FindingAfter (the finding, cleared for every other node).
func (w OldFormWindow) Step(named, holding []string, now time.Time) (next OldFormWindow, closed bool, overdue []string) {
held, closed, overdue := window.StepWindow(w.WindowHeld, named, holding, now, FindingAfter)
return OldFormWindow{WindowHeld: held, Dropped: w.Dropped}, closed, overdue
}
// Drop drops a retired node from the count by hand; dropping it again changes nothing.
func (w OldFormWindow) Drop(node, by, why string, at time.Time) (OldFormWindow, error) {
node, why = strings.TrimSpace(node), strings.TrimSpace(why)
if node == "" || why == "" {
return w, errors.New("name the retired node (node) and say why it leaves the count (why)")
}
if w.DroppedSet()[node] {
return w, nil
}
w.Dropped = append(slices.Clone(w.Dropped), Dropped{Node: node, By: by, Why: why, At: at.UTC()})
return w, nil
}
// DroppedSet is the dropped nodes, as Holding takes them.
func (w OldFormWindow) DroppedSet() map[string]bool {
out := map[string]bool{}
for _, d := range w.Dropped {
out[d.Node] = true
}
return out
}
// Closing is the event's body of a window that closed in a Step.
func (w OldFormWindow) Closing() OldFormClosedBody {
return OldFormClosedBody{At: w.Closed, LastHolding: w.LastHolding, Dropped: w.Dropped}
}
// DecodeOldFormWindow reads the state's value; none is a closed window no node holds.
func DecodeOldFormWindow(raw []byte) (OldFormWindow, error) {
var w OldFormWindow
if len(raw) == 0 {
return w, nil
}
if err := json.Unmarshal(raw, &w); err != nil {
return OldFormWindow{}, fmt.Errorf("the old-form window is not readable: %w", err)
}
return w, nil
}
// OldFormDescription is builder_old_form's description.
const OldFormDescription = "The old reference form's window (design 51 *Every build keeps the old reference form*, ADR 0624): whether it is open, " +
"the counted nodes, present or away, whose node-engine reads an older reference form alone and since when (a node holding it seven days " +
"is the builder's finding " + OldFormHeld + "), when it last closed and who held it then, and the retired nodes dropped from the count by " +
"hand. With drop (a node) and why, drops that retired node from the count by hand, a hand act the builder answers; a node still placed " +
"leaves the count by itself only when the assignments no longer names it. builder_engine_reads shows what each node's engine states."
// AnswerOldForm is builder_old_form's read: the window as the state holds it.
func AnswerOldForm(r Reader) (OldFormWindow, error) {
raw, ok, err := r.Get(OldFormKey)
if err != nil || !ok {
return OldFormWindow{}, err
}
return DecodeOldFormWindow(raw)
}