Files
mesh-tools/node-tools/internal/approval/contracts.go
T
jschoubben d350c98823 Join 11: the merged heads compile where no area's fix is owed
The join's importers name the delivery repository's moved contracts and sdk
(MV1), node-tools' tests use mesh-sdk's gate, the duplicate node-engine claim
rule of the reader release gives way to G's, and the assigner names its first
watched branch, never a constant. The seat holders' AnswerSeat callers are
area 6's.

Builds: design 47 G join 11 (merge of fix round 11)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 10:41:44 +01:00

101 lines
3.8 KiB
Go

// Package approval is the tool runner's side of mesh-sdk's approval gate (go/approval; design 47 G lead answers A-224,
// A-228): it translates what the gate reads beyond itself from the generated contracts, the gate's findings into the
// contract's findings about the runner on its machine, the catalog's entries into the gate's, and the assigner's view of
// the seats' holders into its holder function. The gate itself is mesh-sdk's; none is written here.
package approval
import (
"context"
"encoding/json"
"fmt"
"time"
assignerseat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
condseat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
catalogseat "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
gate "git.novox.be/novox/mesh-sdk/go/approval"
"git.novox.be/novox/mesh-sdk/go/views"
)
// HeldWriter is the runner's held finding writer (views.HeldWriter over the contract's Finding).
type HeldWriter interface {
Write(ctx context.Context, finding condseat.Finding) error
Clear(ctx context.Context, scope, id, kind, why string) error
}
// scopeMachine is the scope of the gate's findings on the tool runner: its machine (lead answer A-154).
const scopeMachine = "machine"
// Findings is the gate's finding writer over the runner's held writer: each finding about the runner's module on node, at
// scope machine, and its pending named in the writer's `findings-stated` where the writer keeps one (design 47 G §384).
func Findings(w HeldWriter, node, runner string, now func() time.Time) gate.FindingWriter {
if w == nil {
return nil
}
if now == nil {
now = time.Now
}
return findingWriter{w: w, node: node, runner: runner, now: now}
}
type findingWriter struct {
w HeldWriter
node, runner string
now func() time.Time
}
func (f findingWriter) Raise(ctx context.Context, kind, id, summary, headline string) error {
return f.w.Write(ctx, condseat.Finding{Kind: kind, Scope: scopeMachine, ID: id, Severity: condseat.FindingSeverityWarning,
Source: "approval", Observer: f.runner, Subject: condseat.Subject{Scope: "module", ID: f.runner, Machine: f.node},
Summary: summary, Headline: headline, At: f.now().UTC()})
}
func (f findingWriter) Clear(ctx context.Context, kind, id, why string) error {
return f.w.Clear(ctx, scopeMachine, id, kind, why)
}
// pender is the held writer's pending (views.HeldWriter's Pend and Judged).
type pender interface {
Pend(views.PendingKey)
Judged(key string)
}
func (f findingWriter) Pend(kind, id string, since time.Time, bound time.Duration) {
if p, ok := f.w.(pender); ok {
p.Pend(views.PendingKey{Key: views.FindingKey(scopeMachine, id, kind), Since: since, Bound: bound.String()})
}
}
func (f findingWriter) Judged(kind, id string) {
if p, ok := f.w.(pender); ok {
p.Judged(views.FindingKey(scopeMachine, id, kind))
}
}
// Entry is a catalog entry as the gate reads its marks.
func Entry(e catalogseat.CatalogEntry) gate.Entry {
manifest, _ := json.Marshal(e.Manifest)
return gate.Entry{Module: e.Module, Fingerprint: e.Fp, Serves: e.Serves, Manifest: manifest}
}
// HoldersIn is the gate's holder function over the assigner's view of the seats' holders as the runner holds it (§19):
// view answers the view's values by key, an error while its first view is not answered; a seat is held when its key names
// a holder.
func HoldersIn(view func() (map[string][]byte, error)) func(seat string) (bool, error) {
return func(seat string) (bool, error) {
values, err := view()
if err != nil {
return false, err
}
raw, ok := values[assignerseat.SeatHoldersBySeatKey(seat)]
if !ok {
return false, nil
}
var h assignerseat.SeatHolders
if err := json.Unmarshal(raw, &h); err != nil {
return false, fmt.Errorf("the holders of %s cannot be read: %w", seat, err)
}
return len(h.Holders) > 0, nil
}
}