Pass a seat held once to its only claimant when its recorded holder is assigned nowhere (issue 1317, ADR 0449)

This commit is contained in:
2026-10-19 21:21:32 +00:00
parent caa3fc8995
commit 666dcff79b
7 changed files with 467 additions and 2 deletions
+104
View File
@@ -0,0 +1,104 @@
package main
import (
"context"
"fmt"
"io"
"os"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/catalogue"
"github.com/novox/mesh-controller/internal/inventory"
"github.com/novox/mesh-controller/internal/link"
)
// seatPassing is what passing seats on needs of the outside: the record's compare-and-set, and the hand act.
type seatPassing struct {
pass func(ctx context.Context, p catalogue.SeatPass) (bool, error)
act func(ctx context.Context, p catalogue.SeatPass) error
say io.Writer
}
// passSeatsOn records, in the composition that finds it, the new holder of each seat held once whose holder on
// record is assigned nowhere and which has exactly one claimant able to hold it, and writes the controller's own
// hand act for it (novox/hq ADR 0449 §4, issue 1317). It answers the holdings as they are after it, so the
// resolution that follows reads the record it just moved. A failed write is said and leaves the record as it was:
// a composition is never refused for it.
func passSeatsOn(ctx context.Context, sp seatPassing, holdings []catalogue.Held,
assignments []catalogue.Assignment) []catalogue.Held {
passes, said := catalogue.SeatsToPassOn(holdings, assignments)
for _, line := range said {
fmt.Fprintln(sp.say, line)
}
for _, p := range passes {
moved, err := sp.pass(ctx, p)
if err != nil {
fmt.Fprintf(sp.say, "%s could not be passed from %s on %s to %s on %s: %v\n", p.Seat.Name,
p.From.Module, p.From.Node, p.To.Module, p.To.Node, err)
continue
}
if !moved {
continue // another composition passed it first, and wrote the act
}
if err := sp.act(ctx, p); err != nil {
fmt.Fprintf(sp.say, "%s passed from %s on %s to %s on %s, and the hand act could NOT be recorded: %v\n",
p.Seat.Name, p.From.Module, p.From.Node, p.To.Module, p.To.Node, err)
}
var next []catalogue.Held
for _, h := range holdings {
if hs, ok := catalogue.SeatNamed(h.Claim); ok && hs.Name == p.Seat.Name {
continue
}
next = append(next, h)
}
p.To.Site = p.From.Site
holdings = append(next, p.To)
}
return holdings
}
// seatPassHandAct is the controller's own entry for a pass: it is the act that puts the holder on record,
// where a person's `seat --to` would have, so the record of who held what stays complete (ADR 0449 §4).
func seatPassHandAct(p catalogue.SeatPass) link.HandAct {
return link.HandAct{
By: "the controller",
Verb: "seat",
Args: []string{p.Seat.Name, "--to", p.To.Node + "/" + p.To.Module, "--was", p.From.Node + "/" + p.From.Module},
Why: fmt.Sprintf("%s on %s, the holder on record of %s, is assigned on no machine, and %s on %s is the only "+
"assignment that claims it and can hold it (ADR 0449 §4)", p.From.Module, p.From.Node, p.Seat.Name,
p.To.Module, p.To.Node),
Cause: "seat-passed",
Outcome: "done",
}
}
// theMeshPassesSeatsOn is passSeatsOn as the composition runs it: against the store's record, with the hand act
// on the bus. Said to where the composition says its other lines (sayLeftOut).
func theMeshPassesSeatsOn(ctx context.Context, inv *inventory.Inventory, shelf map[string]catalogue.Manifest,
assigned map[string][]string, holdings []catalogue.Held) []catalogue.Held {
var assignments []catalogue.Assignment
for node, modules := range assigned {
for _, module := range modules {
if m, ok := shelf[module]; ok {
assignments = append(assignments, catalogue.Assignment{Node: node, Manifest: m})
}
}
}
var w io.Writer = os.Stderr
if lw, ok := ctx.Value(leftOutKey{}).(io.Writer); ok {
w = lw
}
return passSeatsOn(ctx, seatPassing{
say: w,
pass: func(ctx context.Context, p catalogue.SeatPass) (bool, error) {
return inv.PassSeat(ctx, p.Seat.Name, p.Seat.Scope, p.From, p.To)
},
act: func(ctx context.Context, p catalogue.SeatPass) error {
return onTheBus(func(conn *nats.Conn) error {
_, err := link.RecordHandAct(ctx, conn, seatPassHandAct(p))
return err
})
},
}, holdings, assignments)
}