Delete go/handacts: no importer is left in any tree; the list of packages and notInSDK follow (S-7, ADR 0634 §5; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -1,133 +0,0 @@
|
||||
// Package handacts is the hand-act log's contract (novox/hq ADR 0565 clauses 10 and 11, issue 1953): the layout of an act,
|
||||
// moved unchanged from the controller's (mesh-controller internal/link HandAct) so one reader reads both logs alike; the
|
||||
// names of the seat mesh-hand-acts and of what its holder keeps; and the recorder every module records through, which
|
||||
// asks the seat alone: the controller's hand-act and warranted are gone (issue 1953 L5b) and the bus refuses them.
|
||||
package handacts
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The seat, its holder and what it keeps (ADR 0565 clause 10).
|
||||
const (
|
||||
Seat = "mesh-hand-acts"
|
||||
Module = "mesh-hand-acts"
|
||||
// ActsLog is the log of every act, kept 400 days; WarrantsState one key `<asker>.<ask>` per warrant, made once.
|
||||
ActsLog = "acts"
|
||||
WarrantsState = "warrants"
|
||||
// EventRecorded is said on the seat for every act, with the act as its body.
|
||||
EventRecorded = "act-recorded"
|
||||
)
|
||||
|
||||
// KeptFor is how long an act is kept.
|
||||
const KeptFor = 400 * 24 * time.Hour
|
||||
|
||||
// The verbs an act is recorded under, as the controller's log names them.
|
||||
const (
|
||||
VerbRecord = "hand-act record"
|
||||
VerbDrill = "hand-act drill"
|
||||
VerbWarrant = "warrant"
|
||||
// CauseDrill is a drill's cause, never counted as a repair (S15); CauseOperatorAnswer an act the operator chose.
|
||||
CauseDrill = "drill"
|
||||
CauseOperatorAnswer = "operator-answer"
|
||||
// KindRecordedBuilds is a push that only moved recorded builds: no repair (ADR 0242).
|
||||
KindRecordedBuilds = "recorded-builds"
|
||||
)
|
||||
|
||||
// Act is one entry of the log, the controller's HandAct unchanged.
|
||||
type Act struct {
|
||||
ID string `json:"id"`
|
||||
At time.Time `json:"at"`
|
||||
// By is who: the caller the runtime proved.
|
||||
By string `json:"by"`
|
||||
// Verb and Args are the act as given.
|
||||
Verb string `json:"verb"`
|
||||
Args []string `json:"arguments,omitempty"`
|
||||
Why string `json:"why"`
|
||||
// Cause is the condition kind, or a word the person gives; the verb's own name when neither.
|
||||
Cause string `json:"cause"`
|
||||
Condition string `json:"condition,omitempty"`
|
||||
// Kind is what the act was read to be from what it did, never a word a person gives.
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Carried []string `json:"carried,omitempty"`
|
||||
// Via, Ask, Proofs and RequestedBy are an act the operator chose on a warrant.
|
||||
Via string `json:"via,omitempty"`
|
||||
Ask string `json:"ask,omitempty"`
|
||||
Proofs []string `json:"proofs,omitempty"`
|
||||
RequestedBy string `json:"requested-by,omitempty"`
|
||||
// Outcome is what came of an act recorded after it was done.
|
||||
Outcome string `json:"outcome,omitempty"`
|
||||
// Log is which log holds it, set when it is read (LogSeat); never written.
|
||||
Log string `json:"log,omitempty"`
|
||||
}
|
||||
|
||||
// LogSeat is the log an act is held in: the seat's (the ended controller's log is read from its backup, ADR 0590).
|
||||
const LogSeat = Seat
|
||||
|
||||
// Repair says the act counts toward a cause repeated (S15): not a drill, not an operator's answer, not recorded builds.
|
||||
func (a Act) Repair() bool {
|
||||
return a.Cause != CauseDrill && a.Verb != VerbDrill && a.Cause != CauseOperatorAnswer && a.Kind != KindRecordedBuilds
|
||||
}
|
||||
|
||||
// Request is one act a recorder records: what, why, its cause, the condition it addresses, and its verb in the log (a
|
||||
// repair when empty).
|
||||
type Request struct {
|
||||
What string `json:"what"`
|
||||
Why string `json:"why"`
|
||||
Cause string `json:"cause,omitempty"`
|
||||
Condition string `json:"condition,omitempty"`
|
||||
Verb string `json:"verb,omitempty"`
|
||||
}
|
||||
|
||||
// Taken is which log took an act, and what its holder answered.
|
||||
type Taken struct {
|
||||
Log string `json:"log"`
|
||||
Answer json.RawMessage `json:"answer,omitempty"`
|
||||
}
|
||||
|
||||
// Recorder records acts for a module (ADR 0565 clause 11) through the seat mesh-hand-acts alone. An act the seat does
|
||||
// not take is an error that says nothing was recorded, never a quiet success.
|
||||
type Recorder struct {
|
||||
// Ask calls a seat verb by its address (`seat:<seat>.<verb>`): stdio.Ask through the runtime.
|
||||
Ask func(key string, body any) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
// NoResponder says err is the bus finding nobody to answer: the seat has no holder now.
|
||||
func NoResponder(err error) bool {
|
||||
return err != nil && strings.Contains(err.Error(), "no responders")
|
||||
}
|
||||
|
||||
// Record records one act.
|
||||
func (r Recorder) Record(q Request) (Taken, error) {
|
||||
if strings.TrimSpace(q.What) == "" || strings.TrimSpace(q.Why) == "" {
|
||||
return Taken{}, errors.New("a hand act says what was done and why: nothing was recorded")
|
||||
}
|
||||
body := map[string]any{"what": q.What, "why": q.Why}
|
||||
for k, v := range map[string]string{"cause": q.Cause, "condition": q.Condition, "verb": q.Verb} {
|
||||
if v != "" {
|
||||
body[k] = v
|
||||
}
|
||||
}
|
||||
return r.record("hand-act", body)
|
||||
}
|
||||
|
||||
// RecordWarrant records what the asking module did on the operator's warrant for its ask.
|
||||
func (r Recorder) RecordWarrant(asker, ask string) (Taken, error) {
|
||||
body := map[string]any{"asker": asker, "ask": ask}
|
||||
return r.record("warranted", body)
|
||||
}
|
||||
|
||||
func (r Recorder) record(verb string, body any) (Taken, error) {
|
||||
answer, err := r.Ask("seat:"+Seat+"."+verb, body)
|
||||
if err == nil {
|
||||
return Taken{Log: LogSeat, Answer: answer}, nil
|
||||
}
|
||||
if NoResponder(err) {
|
||||
return Taken{}, fmt.Errorf("%s has no holder that answers: nothing was recorded (%w)", Seat, err)
|
||||
}
|
||||
return Taken{}, fmt.Errorf("%s.%s refused, nothing was recorded: %w", Seat, verb, err)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package handacts
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ADR 0565 clauses 10 and 11 (issue 1953 L5-S2, S4, L5b): an act reads the controller's entries unchanged, and a
|
||||
// recorder records through the seat alone, the controller's verbs being gone, and says loudly when nothing was recorded.
|
||||
func TestAnActIsRecordedInTheLogThatAnswers(t *testing.T) {
|
||||
// The shape of mesh-controller internal/link HandAct, as its bucket keeps an entry (handacts.go at 0fbb5ef3).
|
||||
raw := `{"id":"act-1","at":"2026-10-28T09:00:00Z","by":"person.jochen","verb":"hand-act record","why":"a stuck consumer",` +
|
||||
`"cause":"consumer-lost","condition":"bus.EVENTS.x.consumer-lost","via":"telegram","ask":"a-1","proofs":["P1"],` +
|
||||
`"requested-by":"bus.EVENTS.x.consumer-lost","outcome":"done"}`
|
||||
var a Act
|
||||
if err := json.Unmarshal([]byte(raw), &a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
again, _ := json.Marshal(a)
|
||||
var want, got map[string]any
|
||||
_ = json.Unmarshal([]byte(raw), &want)
|
||||
_ = json.Unmarshal(again, &got)
|
||||
if !reflect.DeepEqual(want, got) || !a.Repair() || (Act{Cause: CauseDrill}).Repair() || (Act{Verb: VerbDrill}).Repair() {
|
||||
t.Errorf("the controller's entry reads back as %s", again)
|
||||
}
|
||||
noResponders := errors.New("nats: no responders available for request")
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
seat error
|
||||
ctrl error
|
||||
log string
|
||||
refused string
|
||||
asked string
|
||||
warrants bool
|
||||
}{
|
||||
{name: "the seat answers", log: LogSeat, asked: "seat:mesh-hand-acts.hand-act"},
|
||||
{name: "no holder: nothing recorded, the controller never asked", seat: noResponders, refused: "nothing was recorded",
|
||||
asked: "seat:mesh-hand-acts.hand-act"},
|
||||
{name: "the seat refuses", seat: errors.New("why is required"), refused: "refused, nothing was recorded",
|
||||
asked: "seat:mesh-hand-acts.hand-act"},
|
||||
{name: "a warrant without a holder", seat: noResponders, warrants: true, refused: "nothing was recorded",
|
||||
asked: "seat:mesh-hand-acts.warranted"},
|
||||
{name: "a warrant recorded", warrants: true, log: LogSeat, asked: "seat:mesh-hand-acts.warranted"},
|
||||
} {
|
||||
var asked []string
|
||||
r := Recorder{Ask: func(key string, body any) (json.RawMessage, error) {
|
||||
asked = append(asked, key)
|
||||
if strings.HasPrefix(key, "seat:mesh-hand-acts.") {
|
||||
return json.RawMessage(`{}`), c.seat
|
||||
}
|
||||
return json.RawMessage(`{}`), c.ctrl
|
||||
}}
|
||||
var taken Taken
|
||||
var err error
|
||||
if c.warrants {
|
||||
taken, err = r.RecordWarrant("mesh-config", "ask-1")
|
||||
} else {
|
||||
taken, err = r.Record(Request{What: "silenced", Why: "known", Cause: "silent", Verb: "conditions silence"})
|
||||
}
|
||||
if (c.refused != "") != (err != nil) || (err != nil && !strings.Contains(err.Error(), c.refused)) || taken.Log != c.log ||
|
||||
strings.Join(asked, ",") != c.asked {
|
||||
t.Errorf("%s: took %q, %v, asked %v", c.name, taken.Log, err, asked)
|
||||
}
|
||||
}
|
||||
if _, err := (Recorder{}).Record(Request{What: "x"}); err == nil {
|
||||
t.Error("an act with no why was recorded")
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// request: does every module share it, or is it a shape more than one part reads?
|
||||
var meshSDKPackages = []string{
|
||||
".", "announce", "announcetest", "buildstep", "buscalls", "buscompose", "busstate", "busretry", "calladdress", "catalogue", "channel",
|
||||
"declaration", "delivery", "forgekey", "handacts", "health", "internal/beside", "keys", "kvlimit", "membership", "motion",
|
||||
"declaration", "delivery", "forgekey", "health", "internal/beside", "keys", "kvlimit", "membership", "motion",
|
||||
"objectlink", "outward", "overlay", "pernode", "process", "provisions",
|
||||
"sdkversion", "seal", "seatdef", "seatlist", "seatlist/seatlisttest", "testbus", "views", "views/viewstest",
|
||||
}
|
||||
@@ -39,7 +39,7 @@ var clientSeats = []string{"operator-channel", "mesh-vault", "node-engine", "npm
|
||||
var moduleWord = regexp.MustCompile(`\bmesh-(?:delivery|grants|vault|host|config|provisioner|broker|knowledge|enrolment)\b[-a-z]*|\b(?:indexer|assigner|teller|watchman|checker|builder)(?:-views|_read_)`)
|
||||
|
||||
// notInSDK are the packages that moved to a seat's client or contract (ADR 0627 §1, §4) and may not come back.
|
||||
var notInSDK = []string{"asks", "asker", "publish", "composer", "parts", "provider"}
|
||||
var notInSDK = []string{"asks", "asker", "publish", "composer", "parts", "provider", "handacts"}
|
||||
|
||||
// untilTheJoin is what ADR 0627 still moves at the group's join, each `<file>: <what>`, naming what the checks below
|
||||
// would refuse; it only shrinks. The vault module's name in delivery and provisions stays past this milestone's pin
|
||||
|
||||
Vendored
-1
@@ -37,7 +37,6 @@ delivery/placement.go: mesh-self-check
|
||||
delivery/provided.go: the-private-network
|
||||
delivery/signers.go: mesh-vault
|
||||
delivery/stepasks.go: node-build-node
|
||||
handacts/handacts.go: mesh-hand-acts
|
||||
health/findings.go: mesh-conditions
|
||||
health/findings.go: mesh-healers
|
||||
health/findings.go: mesh-self-check
|
||||
|
||||
Reference in New Issue
Block a user