Files
mesh-delivery/modules/mesh-delivery-sdk/window/window.go
T
jschoubben 71a35a6376 MV1: move contracts and sdk under modules/, importers follow
The delivery repository's contracts and SDK live under modules/ as
mesh-delivery-contracts and mesh-delivery-sdk, their Go module paths
renamed to match the folders (the TypeScript names already did), and
every importer in mesh-delivery, mesh-catalog, mesh-host, mesh-tools
and mesh-sdk follows in the same group: a design as it should be, one
path per module, checked by the group's check building every importer.

Builds: design 47 G §427, §88

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 05:09:56 +01:00

65 lines
2.6 KiB
Go

package window
import (
"slices"
"sort"
"time"
)
// WindowHeld is the part of a window every window keeper shares (design 51: distribution's old seat name and direct
// writes, the builder's old reference form): whether it is open, the nodes holding it and since when, and when it last
// closed and who held it then. A keeper embeds it in its own record, so its JSON is the record's.
type WindowHeld struct {
Open bool `json:"open"`
Holding []string `json:"holding,omitempty"`
Since map[string]time.Time `json:"since,omitempty"`
Closed time.Time `json:"closed,omitzero"`
// LastHolding are the nodes that held it last, said when it closes.
LastHolding []string `json:"last_holding,omitempty"`
}
// StepWindow is the one rule every window keeper steps its window by (security-upgrade review F3, the second of a
// kind): named is every node the read named, dropped ones included, and holding those of them that hold the window at
// now. A read that names no node judges nothing, as a first view's empty answer at revision 0 is no empty state (ADR
// 0615): an empty node_haves or assignments not yet read never closes a window, and the window stays as it was. A read
// naming nodes, none holding, closes an open window (closed true, once); one naming a holder opens it, each holder's
// since kept from w. overdue are the holders whose since is at least findingAfter before now (the keeper's finding,
// cleared for every other node); a read naming no node keeps w's holders' overdue as they stand at now.
func StepWindow(w WindowHeld, named, holding []string, now time.Time, findingAfter time.Duration) (next WindowHeld, closed bool, overdue []string) {
now = now.UTC()
if len(named) == 0 {
next = w
for _, n := range w.Holding {
if since, ok := w.Since[n]; ok && now.Sub(since) >= findingAfter {
overdue = append(overdue, n)
}
}
return next, false, overdue
}
if len(holding) == 0 {
next = WindowHeld{Closed: w.Closed, LastHolding: w.LastHolding}
if w.Open {
next.Closed, next.LastHolding = now, slices.Clone(w.Holding)
if next.LastHolding == nil {
next.LastHolding = []string{}
}
return next, true, nil
}
return next, false, nil
}
held := slices.Clone(holding)
sort.Strings(held)
next = WindowHeld{Open: true, Holding: held, Since: map[string]time.Time{}, Closed: w.Closed, LastHolding: w.LastHolding}
for _, n := range held {
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
}