Files
mesh-sdk/go/approval/closed.go
T
jschoubben 5fa64be894 Revert the revert of G's mesh-sdk merge (#191): G's sdk stands again over reader release 3
Main reverted G's half merge (#189, reverted by 8afc3eb in #191) and then took the reader release's S1-S5, which
copied G's code. G's form holds where they differ (process_bus.go, seats.go, state.go and their tests); the
release's kept names stay so main's callers keep building: SeatViewPrefix beside SeatReadPrefix, and QueueInvoke's
empty strings on not-ok (with its test TestQueueInvoke). The tree differs from G's head f30dee54 only by those and
the release's two tests (objects cap option, seat states and stream kinds).

Builds: design 47 G (the milestone's sdk part, as at f30dee54)

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

101 lines
3.4 KiB
Go

package approval
import (
"fmt"
"time"
)
// UnreadableAfter is how long the state approvals fails before the runner's finding says so (§19).
const UnreadableAfter = 5 * time.Minute
// unreadableKey is the bound key whose expiry raises `approvals-unreadable` (lead answer A-157).
func (g *Gate) unreadableKey() string { return g.d.Node + ".unreadable." + StateName }
// held says why no marked call can be asked now, or "" (§19): no module holds operator-channel, as the assigner's view
// of the seat's holders says, or that view is not read.
func (g *Gate) held() string {
if g.d.Channel == nil {
return "the tool runner reaches no operator channel to ask on"
}
if g.d.Holder == nil && g.d.Holding {
return "" // a process holder reads no view of the seats' holders: an ask nobody takes is said by its gate-unanswered (design 47 G §677)
}
if g.d.Holder == nil {
return "the tool runner reads no view of the seats' holders, so it cannot tell that a module holds " + g.d.Channel.Seat()
}
on := g.d.Channel.Seat()
ok, err := g.d.Holder(on)
switch {
case err != nil:
return fmt.Sprintf("whether a module holds %s cannot be read: %v", on, err)
case !ok:
return "no module holds " + on + ", so nobody can be asked"
}
return ""
}
// stateFailed notes that the state approvals could not be read or written: no marked call runs on this machine until it
// can, the failed read or write is made again at the next marked call and at the next membership change (A-157), and
// five minutes on the runner's finding says it (§19).
func (g *Gate) stateFailed(err error) {
g.mu.Lock()
first := g.failingSince.IsZero()
if first {
g.failingSince = g.d.Now()
}
g.mu.Unlock()
if first {
g.d.Logf("[approval] the state %s on %s cannot be read or written (%v): no marked call runs here until it can", StateName, g.d.Node, err)
if g.d.Bounds != nil && !g.d.Holding {
if e := g.d.Bounds.CreateTTL(g.unreadableKey(), map[string]string{"since": g.d.Now().UTC().Format(time.RFC3339)}, UnreadableAfter); e != nil {
g.d.Logf("[approval] the bound of the state %s's failure is not kept: %v", StateName, e)
}
}
}
}
// stateRead notes that the state approvals was read or written: a failure noted before ends, its finding cleared.
func (g *Gate) stateRead() {
g.mu.Lock()
was := !g.failingSince.IsZero()
g.failingSince = time.Time{}
g.mu.Unlock()
if !was {
return
}
if g.d.Bounds != nil && !g.d.Holding {
_ = g.d.Bounds.Delete(g.unreadableKey())
}
g.finding(StateName, KindApprovalsUnreadable, false, "the state "+StateName+" is read again", "")
}
// failing says the state approvals is failing now.
func (g *Gate) failing() bool {
g.mu.Lock()
defer g.mu.Unlock()
return !g.failingSince.IsZero()
}
// StateAgain makes the failed read of the state approvals again: at the next membership change the runner hears (A-157).
func (g *Gate) StateAgain() {
if g.d.State == nil || !g.failing() {
return
}
if _, err := g.d.State.Keys(); err != nil {
return
}
g.stateRead()
}
// unreadableExpired is the bound key of a failing state expiring: the finding raised while the state still fails.
func (g *Gate) unreadableExpired() {
g.mu.Lock()
since := g.failingSince
g.mu.Unlock()
if since.IsZero() {
return
}
g.finding(StateName, KindApprovalsUnreadable, true, fmt.Sprintf("the state %s on %s cannot be read or written since %s: no marked call "+
"runs here until it can", StateName, g.d.Node, since.UTC().Format(time.RFC3339)), "approvals unreadable on "+g.d.Node)
}