K21a (regenerate, in between): the generated Go and TypeScript follow the definitions so far
The conditions developer reads kinds, attempt-changed, wait-held and wait-ended from it. K21 regenerates last. The client meshconditionsclient does not build against the findings state's new layout until the question on it is answered. Builds: design 47 G §341, §523, §580, §541, §499 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -278,8 +278,17 @@ type ListInput struct {
|
||||
Module string `json:"module,omitempty"`
|
||||
}
|
||||
|
||||
// ListAnswerCaps is the sum of the bus's caps against its limits, in MiB; an absent limit is unlimited (design 47 G 547, 661).
|
||||
type ListAnswerCaps struct {
|
||||
AccountLimitMiB int64 `json:"account-limit-mib,omitempty"`
|
||||
ServerLimitMiB int64 `json:"server-limit-mib,omitempty"`
|
||||
SumMiB int64 `json:"sum-mib"`
|
||||
}
|
||||
|
||||
// ListAnswer is what list answers.
|
||||
type ListAnswer struct {
|
||||
// the sum of the bus's caps against its limits, in MiB; an absent limit is unlimited (design 47 G 547, 661)
|
||||
Caps *ListAnswerCaps `json:"caps,omitempty"`
|
||||
// every entry
|
||||
Entries []Held `json:"entries"`
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "list answers the binding of an entry the bus holds", subject: "", given: "{\"entries\":[{\"binding\":{\"history\":\"mesh-knowledge_ledger\",\"indexes\":{\"by-kind\":\"mesh-knowledge_ledger_index-by-kind\"},\"local\":\"ledger\",\"objects\":\"mesh-knowledge_ledger_state-objects\",\"snapshot\":\"mesh.ledger.mesh-knowledge.ledger.snapshot\",\"state\":\"mesh-knowledge_ledger_state\",\"storage\":\"ledger\",\"subject\":\"mesh.ledger.mesh-knowledge.ledger.history\",\"template\":\"ledger\",\"types\":[{\"indexes\":[{\"name\":\"by-kind\",\"on\":[\"kind\"]}],\"name\":\"decision\",\"operations\":[{\"effect\":\"set-fields\",\"fields\":[\"title\",\"kind\"],\"name\":\"set\"}],\"schema\":{\"kind\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"}}}]},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"settings\":{\"max-mib\":64},\"storage-type\":\"ledger\",\"template\":\"ledger\"}]}", wire: "{\"entries\":[{\"binding\":{\"history\":\"mesh-knowledge_ledger\",\"indexes\":{\"by-kind\":\"mesh-knowledge_ledger_index-by-kind\"},\"local\":\"ledger\",\"objects\":\"mesh-knowledge_ledger_state-objects\",\"snapshot\":\"mesh.ledger.mesh-knowledge.ledger.snapshot\",\"state\":\"mesh-knowledge_ledger_state\",\"storage\":\"ledger\",\"subject\":\"mesh.ledger.mesh-knowledge.ledger.history\",\"template\":\"ledger\",\"types\":[{\"indexes\":[{\"name\":\"by-kind\",\"on\":[\"kind\"]}],\"name\":\"decision\",\"operations\":[{\"effect\":\"set-fields\",\"fields\":[\"title\",\"kind\"],\"name\":\"set\"}],\"schema\":{\"kind\":{\"type\":\"string\"},\"title\":{\"type\":\"string\"}}}]},\"bus-name\":\"mesh-knowledge_ledger\",\"fill\":{\"bytes\":2048,\"cap\":67108864,\"count\":10},\"local-name\":\"ledger\",\"module\":\"mesh-knowledge\",\"settings\":{\"max-mib\":64},\"storage-type\":\"ledger\",\"template\":\"ledger\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "list answers the sum of the caps and both limits (design 47 G 661)", subject: "", given: "{\"caps\":{\"account-limit-mib\":1024,\"server-limit-mib\":2048,\"sum-mib\":512},\"entries\":[]}", wire: "{\"caps\":{\"account-limit-mib\":1024,\"server-limit-mib\":2048,\"sum-mib\":512},\"entries\":[]}",
|
||||
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "list answers a sum with no limit, which is unlimited", subject: "", given: "{\"caps\":{\"sum-mib\":64},\"entries\":[]}", wire: "{\"caps\":{\"sum-mib\":64},\"entries\":[]}",
|
||||
decode: func(b []byte) (any, error) { var v ListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// Package meshrecovery is the contract of the seat "mesh-recovery", defined whole (seats/mesh-recovery/seat.json): Takes, sets aside and restores a module's kept data on the live bus, each act in the hand-act log (design 47 G 554). Its verbs answer at once and the result is the event.
|
||||
package meshrecovery
|
||||
|
||||
import "time"
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "mesh-recovery"
|
||||
|
||||
const (
|
||||
VerbSnapshotKept = "snapshot-kept"
|
||||
VerbRestoreKept = "restore-kept"
|
||||
VerbKeptPoints = "kept-points"
|
||||
VerbRestoreAside = "restore-aside"
|
||||
VerbCancel = "cancel"
|
||||
)
|
||||
|
||||
const (
|
||||
EventEnded = "ended"
|
||||
EventRestoreStarted = "restore-started"
|
||||
EventRestored = "restored"
|
||||
EventSetAside = "set-aside"
|
||||
EventAsideDiscarded = "aside-discarded"
|
||||
EventPointRemoved = "point-removed"
|
||||
EventExpiryEnded = "expiry-ended"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbSnapshotKept, VerbRestoreKept, VerbKeptPoints, VerbRestoreAside, VerbCancel}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventEnded, EventRestoreStarted, EventRestored, EventSetAside, EventAsideDiscarded, EventPointRemoved, EventExpiryEnded}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
const (
|
||||
Family = "mesh-recovery.events"
|
||||
FamilyMaxAgeDays = 7
|
||||
)
|
||||
|
||||
// StreamEnded is the stream ended is kept in.
|
||||
const StreamEnded = "SEAT_mesh-recovery_ended"
|
||||
|
||||
// SubjectEnded is where ended is published, on mesh.seat.mesh-recovery.event.ended.<module>.<kept>.
|
||||
func SubjectEnded(module, kept string) string {
|
||||
return "mesh.seat.mesh-recovery.event.ended." + module + "." + kept
|
||||
}
|
||||
|
||||
// SubjectEndedAll hears every ended.
|
||||
const SubjectEndedAll = "mesh.seat.mesh-recovery.event.ended.*.*"
|
||||
|
||||
// StreamRestoreStarted is the stream restore-started is kept in.
|
||||
const StreamRestoreStarted = "SEAT_mesh-recovery_restore-started"
|
||||
|
||||
// SubjectRestoreStarted is where restore-started is published, on mesh.seat.mesh-recovery.event.restore-started.<module>.<kept>.
|
||||
func SubjectRestoreStarted(module, kept string) string {
|
||||
return "mesh.seat.mesh-recovery.event.restore-started." + module + "." + kept
|
||||
}
|
||||
|
||||
// SubjectRestoreStartedAll hears every restore-started.
|
||||
const SubjectRestoreStartedAll = "mesh.seat.mesh-recovery.event.restore-started.*.*"
|
||||
|
||||
// StreamRestored is the stream restored is kept in.
|
||||
const StreamRestored = "SEAT_mesh-recovery_restored"
|
||||
|
||||
// SubjectRestored is where restored is published, on mesh.seat.mesh-recovery.event.restored.<module>.<kept>.
|
||||
func SubjectRestored(module, kept string) string {
|
||||
return "mesh.seat.mesh-recovery.event.restored." + module + "." + kept
|
||||
}
|
||||
|
||||
// SubjectRestoredAll hears every restored.
|
||||
const SubjectRestoredAll = "mesh.seat.mesh-recovery.event.restored.*.*"
|
||||
|
||||
// StreamSetAside is the stream set-aside is kept in.
|
||||
const StreamSetAside = "SEAT_mesh-recovery_set-aside"
|
||||
|
||||
// SubjectSetAside is where set-aside is published, on mesh.seat.mesh-recovery.event.set-aside.<module>.<kept>.
|
||||
func SubjectSetAside(module, kept string) string {
|
||||
return "mesh.seat.mesh-recovery.event.set-aside." + module + "." + kept
|
||||
}
|
||||
|
||||
// SubjectSetAsideAll hears every set-aside.
|
||||
const SubjectSetAsideAll = "mesh.seat.mesh-recovery.event.set-aside.*.*"
|
||||
|
||||
// StreamAsideDiscarded is the stream aside-discarded is kept in.
|
||||
const StreamAsideDiscarded = "SEAT_mesh-recovery_aside-discarded"
|
||||
|
||||
// SubjectAsideDiscarded is where aside-discarded is published, on mesh.seat.mesh-recovery.event.aside-discarded.<module>.<kept>.
|
||||
func SubjectAsideDiscarded(module, kept string) string {
|
||||
return "mesh.seat.mesh-recovery.event.aside-discarded." + module + "." + kept
|
||||
}
|
||||
|
||||
// SubjectAsideDiscardedAll hears every aside-discarded.
|
||||
const SubjectAsideDiscardedAll = "mesh.seat.mesh-recovery.event.aside-discarded.*.*"
|
||||
|
||||
// StreamPointRemoved is the stream point-removed is kept in.
|
||||
const StreamPointRemoved = "SEAT_mesh-recovery_point-removed"
|
||||
|
||||
// SubjectPointRemoved is where point-removed is published, on mesh.seat.mesh-recovery.event.point-removed.<module>.<kept>.
|
||||
func SubjectPointRemoved(module, kept string) string {
|
||||
return "mesh.seat.mesh-recovery.event.point-removed." + module + "." + kept
|
||||
}
|
||||
|
||||
// SubjectPointRemovedAll hears every point-removed.
|
||||
const SubjectPointRemovedAll = "mesh.seat.mesh-recovery.event.point-removed.*.*"
|
||||
|
||||
// StreamExpiryEnded is the stream expiry-ended is kept in.
|
||||
const StreamExpiryEnded = "SEAT_mesh-recovery_expiry-ended"
|
||||
|
||||
// SubjectExpiryEnded is where expiry-ended is published.
|
||||
const SubjectExpiryEnded = "mesh.seat.mesh-recovery.event.expiry-ended"
|
||||
|
||||
// Aside is the live data set aside before a restore: a directory under aside/ in the recovery module's directory, nothing of it on the bus (design 47 G 516).
|
||||
type Aside struct {
|
||||
// the directory's name
|
||||
ID string `json:"id"`
|
||||
// the bus name it was live under
|
||||
Name string `json:"name"`
|
||||
// its bytes on disk
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// CatchUp is the catch-up of the newer manifest: a tool and its arguments, never text (design 47 G 451, 516, 631).
|
||||
type CatchUp struct {
|
||||
Arguments map[string]any `json:"arguments,omitempty"`
|
||||
Tool string `json:"tool"`
|
||||
}
|
||||
|
||||
// Consumer is one durable consumer of the kept data and the sequence it had acknowledged.
|
||||
type Consumer struct {
|
||||
Name string `json:"name"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
Stream string `json:"stream"`
|
||||
}
|
||||
|
||||
// Digest is the digest of one object of a point's bytes.
|
||||
type Digest struct {
|
||||
Digest string `json:"digest"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Point is a restore point: the kept data whole and its consumers' positions, in the recovery module's points directory (design 47 G 516, 560).
|
||||
type Point struct {
|
||||
// when it was taken, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
CatchUp *CatchUp `json:"catch-up,omitempty"`
|
||||
Consumers []Consumer `json:"consumers,omitempty"`
|
||||
// the digest of each object's bytes, checked before a restore (design 47 G 567)
|
||||
Digests []Digest `json:"digests,omitempty"`
|
||||
// the fingerprint it was taken before
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
// its form, the number the holder raises when it changes how it writes it (0 when undeclared)
|
||||
Form int64 `json:"form"`
|
||||
// the kept data, its local name in the module's manifest
|
||||
Kept string `json:"kept"`
|
||||
// the module
|
||||
Module string `json:"module"`
|
||||
// its bytes
|
||||
Size int64 `json:"size"`
|
||||
// the point's name, also its directory's
|
||||
Snapshot string `json:"snapshot"`
|
||||
}
|
||||
|
||||
// Running is the act that runs, since when and what it waits on (design 47 G 517, 554).
|
||||
type Running struct {
|
||||
Act string `json:"act"`
|
||||
Since time.Time `json:"since"`
|
||||
Step string `json:"step,omitempty"`
|
||||
// what it waits on, when it waits
|
||||
WaitsOn string `json:"waits-on,omitempty"`
|
||||
}
|
||||
|
||||
// SnapshotKeptInput is what snapshot-kept takes.
|
||||
type SnapshotKeptInput struct {
|
||||
CatchUp *CatchUp `json:"catch-up,omitempty"`
|
||||
// the fingerprint the restore point is taken before, or reverted from
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
// its form, the number the holder raises when it changes how it writes it (0 when undeclared)
|
||||
Form int64 `json:"form"`
|
||||
// the kept data, its local name in the module's manifest
|
||||
Kept string `json:"kept"`
|
||||
// the module
|
||||
Module string `json:"module"`
|
||||
// why, which the hand-act log keeps
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// SnapshotKeptAnswer is what snapshot-kept answers.
|
||||
type SnapshotKeptAnswer struct {
|
||||
// accepted: the act's id, which the ending event carries
|
||||
Accepted string `json:"accepted,omitempty"`
|
||||
// busy: the id of the act that runs; it keeps no queue, so the caller asks again on that act's ending event
|
||||
Busy string `json:"busy,omitempty"`
|
||||
// why it was not taken; set alone
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// RestoreKeptInput is what restore-kept takes.
|
||||
type RestoreKeptInput struct {
|
||||
// the fingerprint the restore point is taken before, or reverted from
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
// its form, the number the holder raises when it changes how it writes it (0 when undeclared)
|
||||
Form int64 `json:"form"`
|
||||
// ask the backup holder for the points directory when the point is missing there (design 47 G 516)
|
||||
FromBackup bool `json:"from-backup,omitempty"`
|
||||
// the kept data, its local name in the module's manifest
|
||||
Kept string `json:"kept"`
|
||||
// the module
|
||||
Module string `json:"module"`
|
||||
// the restore point's name
|
||||
Snapshot string `json:"snapshot,omitempty"`
|
||||
// why, which the hand-act log keeps
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// RestoreKeptAnswer is what restore-kept answers.
|
||||
type RestoreKeptAnswer struct {
|
||||
// accepted: the act's id, which the ending event carries
|
||||
Accepted string `json:"accepted,omitempty"`
|
||||
// busy: the id of the act that runs; it keeps no queue, so the caller asks again on that act's ending event
|
||||
Busy string `json:"busy,omitempty"`
|
||||
// why it was not taken; set alone
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// KeptPointsInput is what kept-points takes.
|
||||
type KeptPointsInput struct {
|
||||
// one kept data (optional)
|
||||
Kept string `json:"kept,omitempty"`
|
||||
// one module (optional)
|
||||
Module string `json:"module,omitempty"`
|
||||
}
|
||||
|
||||
// KeptPointsAnswer is what kept-points answers.
|
||||
type KeptPointsAnswer struct {
|
||||
Asides []Aside `json:"asides"`
|
||||
Points []Point `json:"points"`
|
||||
Running *Running `json:"running,omitempty"`
|
||||
}
|
||||
|
||||
// RestoreAsideInput is what restore-aside takes.
|
||||
type RestoreAsideInput struct {
|
||||
// the aside's id
|
||||
ID string `json:"id"`
|
||||
// the kept data, its local name in the module's manifest
|
||||
Kept string `json:"kept"`
|
||||
// the module
|
||||
Module string `json:"module"`
|
||||
// why, which the hand-act log keeps
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// RestoreAsideAnswer is what restore-aside answers.
|
||||
type RestoreAsideAnswer struct {
|
||||
// accepted: the act's id, which the ending event carries
|
||||
Accepted string `json:"accepted,omitempty"`
|
||||
// busy: the id of the act that runs; it keeps no queue, so the caller asks again on that act's ending event
|
||||
Busy string `json:"busy,omitempty"`
|
||||
// why it was not taken; set alone
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// CancelInput is what cancel takes.
|
||||
type CancelInput struct {
|
||||
// the act's id
|
||||
Act string `json:"act"`
|
||||
// why, which the hand-act log keeps
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// CancelAnswer is what cancel answers.
|
||||
type CancelAnswer struct {
|
||||
// the act ended cancelled
|
||||
Cancelled bool `json:"cancelled,omitempty"`
|
||||
// why not, naming the step an act has begun; set alone
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// EndedEvent is the body of the event ended.
|
||||
type EndedEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
CatchUp *CatchUp `json:"catch-up,omitempty"`
|
||||
Consumers []Consumer `json:"consumers,omitempty"`
|
||||
Digests []Digest `json:"digests,omitempty"`
|
||||
// when failed
|
||||
Error string `json:"error,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Form int64 `json:"form,omitempty"`
|
||||
Kept string `json:"kept"`
|
||||
Module string `json:"module"`
|
||||
// how the act ended
|
||||
Outcome string `json:"outcome"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Snapshot string `json:"snapshot,omitempty"`
|
||||
}
|
||||
|
||||
// The values of EndedEvent's outcome.
|
||||
const (
|
||||
EndedEventOutcomeDone = "done"
|
||||
EndedEventOutcomeFailed = "failed"
|
||||
)
|
||||
|
||||
// RestoreStartedEvent is the body of the event restore-started.
|
||||
type RestoreStartedEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Form int64 `json:"form,omitempty"`
|
||||
Kept string `json:"kept"`
|
||||
Module string `json:"module"`
|
||||
Snapshot string `json:"snapshot,omitempty"`
|
||||
}
|
||||
|
||||
// RestoredEvent is the body of the event restored.
|
||||
type RestoredEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
Aside *Aside `json:"aside,omitempty"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Form int64 `json:"form,omitempty"`
|
||||
Kept string `json:"kept"`
|
||||
// the point is not in the recovery module's directory
|
||||
Missing bool `json:"missing,omitempty"`
|
||||
Module string `json:"module"`
|
||||
// how the act ended
|
||||
Outcome string `json:"outcome"`
|
||||
Snapshot string `json:"snapshot,omitempty"`
|
||||
}
|
||||
|
||||
// The values of RestoredEvent's outcome.
|
||||
const (
|
||||
RestoredEventOutcomeDone = "done"
|
||||
RestoredEventOutcomeFailed = "failed"
|
||||
)
|
||||
|
||||
// SetAsideEvent is the body of the event set-aside.
|
||||
type SetAsideEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// AsideDiscardedEvent is the body of the event aside-discarded.
|
||||
type AsideDiscardedEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// PointRemovedEvent is the body of the event point-removed.
|
||||
type PointRemovedEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Form int64 `json:"form,omitempty"`
|
||||
Kept string `json:"kept"`
|
||||
Module string `json:"module"`
|
||||
Snapshot string `json:"snapshot,omitempty"`
|
||||
}
|
||||
|
||||
// ExpiryEndedEvent is the body of the event expiry-ended.
|
||||
type ExpiryEndedEvent struct {
|
||||
// the id the act was answered accepted with, or a hand act's own id (design 47 G 560)
|
||||
Act string `json:"act"`
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
// the number of points the run removed, 0 included
|
||||
Removed int64 `json:"removed"`
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshrecovery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-recovery/fixtures: each decodes into its type, a field the type does not
|
||||
// know is dropped (ADR 0608 §6), and it reads back as its wire says; each subject named is the contract's.
|
||||
func TestFixtures(t *testing.T) {
|
||||
cases := []struct {
|
||||
name, subject, given, wire string
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "ended is said", subject: "mesh.seat.mesh-recovery.event.ended.mesh-knowledge.ledger", given: "{\"act\":\"a-1\",\"at\":\"2026-11-11T10:00:00Z\",\"catch-up\":{\"tool\":\"knowledge_catch_up\"},\"consumers\":[{\"name\":\"c1\",\"sequence\":42,\"stream\":\"KV_x\"}],\"digests\":[{\"digest\":\"sha256:00\",\"name\":\"KV_x\"}],\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"outcome\":\"done\",\"size\":2048,\"snapshot\":\"2-0123456789ab\"}", wire: "{\"act\":\"a-1\",\"at\":\"2026-11-11T10:00:00Z\",\"catch-up\":{\"tool\":\"knowledge_catch_up\"},\"consumers\":[{\"name\":\"c1\",\"sequence\":42,\"stream\":\"KV_x\"}],\"digests\":[{\"digest\":\"sha256:00\",\"name\":\"KV_x\"}],\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"outcome\":\"done\",\"size\":2048,\"snapshot\":\"2-0123456789ab\"}",
|
||||
decode: func(b []byte) (any, error) { var v EndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.ended\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "ended is said", subject: "mesh.seat.mesh-recovery.event.ended.mesh-knowledge.ledger", given: "{\"act\":\"a-2\",\"at\":\"2026-11-11T10:00:00Z\",\"error\":\"stopped\",\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"outcome\":\"failed\"}", wire: "{\"act\":\"a-2\",\"at\":\"2026-11-11T10:00:00Z\",\"error\":\"stopped\",\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"outcome\":\"failed\"}",
|
||||
decode: func(b []byte) (any, error) { var v EndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.ended\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "restore-started is said", subject: "mesh.seat.mesh-recovery.event.restore-started.mesh-knowledge.ledger", given: "{\"act\":\"a-3\",\"at\":\"2026-11-11T10:00:00Z\",\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"snapshot\":\"2-0123456789ab\"}", wire: "{\"act\":\"a-3\",\"at\":\"2026-11-11T10:00:00Z\",\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"snapshot\":\"2-0123456789ab\"}",
|
||||
decode: func(b []byte) (any, error) { var v RestoreStartedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.restore-started\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "restored is said", subject: "mesh.seat.mesh-recovery.event.restored.mesh-knowledge.ledger", given: "{\"act\":\"a-3\",\"at\":\"2026-11-11T10:00:00Z\",\"error\":\"restore point missing\",\"kept\":\"ledger\",\"missing\":true,\"module\":\"mesh-knowledge\",\"outcome\":\"failed\"}", wire: "{\"act\":\"a-3\",\"at\":\"2026-11-11T10:00:00Z\",\"error\":\"restore point missing\",\"kept\":\"ledger\",\"missing\":true,\"module\":\"mesh-knowledge\",\"outcome\":\"failed\"}",
|
||||
decode: func(b []byte) (any, error) { var v RestoredEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.restored\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "set-aside is said", subject: "mesh.seat.mesh-recovery.event.set-aside.mesh-knowledge.ledger", given: "{\"act\":\"a-4\",\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"20261111T121200Z\",\"name\":\"mesh-knowledge_ledger\",\"size\":4096}", wire: "{\"act\":\"a-4\",\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"20261111T121200Z\",\"name\":\"mesh-knowledge_ledger\",\"size\":4096}",
|
||||
decode: func(b []byte) (any, error) { var v SetAsideEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.set-aside\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "aside-discarded is said", subject: "mesh.seat.mesh-recovery.event.aside-discarded.mesh-knowledge.ledger", given: "{\"act\":\"a-5\",\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"20261111T121200Z\",\"name\":\"mesh-knowledge_ledger\",\"size\":4096}", wire: "{\"act\":\"a-5\",\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"20261111T121200Z\",\"name\":\"mesh-knowledge_ledger\",\"size\":4096}",
|
||||
decode: func(b []byte) (any, error) { var v AsideDiscardedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.aside-discarded\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "point-removed is said", subject: "mesh.seat.mesh-recovery.event.point-removed.mesh-knowledge.ledger", given: "{\"act\":\"a-6\",\"at\":\"2026-11-11T10:00:00Z\",\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"snapshot\":\"1-aaaaaaaaaaaa\"}", wire: "{\"act\":\"a-6\",\"at\":\"2026-11-11T10:00:00Z\",\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"snapshot\":\"1-aaaaaaaaaaaa\"}",
|
||||
decode: func(b []byte) (any, error) { var v PointRemovedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.point-removed\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "expiry-ended says 0 when it removed nothing", subject: "mesh.seat.mesh-recovery.event.expiry-ended", given: "{\"act\":\"a-7\",\"at\":\"2026-11-11T10:00:00Z\",\"removed\":0}", wire: "{\"act\":\"a-7\",\"at\":\"2026-11-11T10:00:00Z\",\"removed\":0}",
|
||||
decode: func(b []byte) (any, error) { var v ExpiryEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-recovery\\.event\\.expiry-ended$")},
|
||||
{name: "snapshot-kept takes the point of a kept data before its form changes", subject: "", given: "{\"catch-up\":{\"arguments\":{\"since\":\"0\"},\"tool\":\"knowledge_catch_up\"},\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"why\":\"form change of mesh-knowledge's ledger to 0123456789ab\"}", wire: "{\"catch-up\":{\"arguments\":{\"since\":\"0\"},\"tool\":\"knowledge_catch_up\"},\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"why\":\"form change of mesh-knowledge's ledger to 0123456789ab\"}",
|
||||
decode: func(b []byte) (any, error) { var v SnapshotKeptInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "snapshot-kept answers accepted with the act", subject: "", given: "{\"accepted\":\"a-20261111t100000\"}", wire: "{\"accepted\":\"a-20261111t100000\"}",
|
||||
decode: func(b []byte) (any, error) { var v SnapshotKeptAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "restore-kept answers busy with the act that runs", subject: "", given: "{\"busy\":\"a-20261111t100000\"}", wire: "{\"busy\":\"a-20261111t100000\"}",
|
||||
decode: func(b []byte) (any, error) { var v RestoreKeptAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "restore-kept takes a point and asks the backup for its directory", subject: "", given: "{\"fingerprint\":\"0123456789ab\",\"form\":2,\"from-backup\":true,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"snapshot\":\"2-0123456789ab\",\"why\":\"revert of mesh-knowledge's ledger from 0123456789ab\"}", wire: "{\"fingerprint\":\"0123456789ab\",\"form\":2,\"from-backup\":true,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"snapshot\":\"2-0123456789ab\",\"why\":\"revert of mesh-knowledge's ledger from 0123456789ab\"}",
|
||||
decode: func(b []byte) (any, error) { var v RestoreKeptInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "restore-kept answers an error naming why", subject: "", given: "{\"error\":\"aside-only is the operator's hand tool alone\"}", wire: "{\"error\":\"aside-only is the operator's hand tool alone\"}",
|
||||
decode: func(b []byte) (any, error) { var v RestoreKeptAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "kept-points lists a point, an aside and the act that runs", subject: "", given: "{\"asides\":[{\"id\":\"20261111T121200Z\",\"name\":\"mesh-knowledge_ledger\",\"size\":4096}],\"points\":[{\"at\":\"2026-11-11T10:00:00Z\",\"catch-up\":{\"tool\":\"knowledge_catch_up\"},\"consumers\":[{\"name\":\"c1\",\"sequence\":42,\"stream\":\"KV_x\"}],\"digests\":[{\"digest\":\"sha256:00\",\"name\":\"KV_x\"}],\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"size\":2048,\"snapshot\":\"2-0123456789ab\"}],\"running\":{\"act\":\"a-1\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"waiting on the dump\",\"waits-on\":\"item-ended of the bus item\"}}", wire: "{\"asides\":[{\"id\":\"20261111T121200Z\",\"name\":\"mesh-knowledge_ledger\",\"size\":4096}],\"points\":[{\"at\":\"2026-11-11T10:00:00Z\",\"catch-up\":{\"tool\":\"knowledge_catch_up\"},\"consumers\":[{\"name\":\"c1\",\"sequence\":42,\"stream\":\"KV_x\"}],\"digests\":[{\"digest\":\"sha256:00\",\"name\":\"KV_x\"}],\"fingerprint\":\"0123456789ab\",\"form\":2,\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"size\":2048,\"snapshot\":\"2-0123456789ab\"}],\"running\":{\"act\":\"a-1\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"waiting on the dump\",\"waits-on\":\"item-ended of the bus item\"}}",
|
||||
decode: func(b []byte) (any, error) { var v KeptPointsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "restore-aside names the aside", subject: "", given: "{\"id\":\"20261111T121200Z\",\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"why\":\"an aside left at removed\"}", wire: "{\"id\":\"20261111T121200Z\",\"kept\":\"ledger\",\"module\":\"mesh-knowledge\",\"why\":\"an aside left at removed\"}",
|
||||
decode: func(b []byte) (any, error) { var v RestoreAsideInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "cancel names the act", subject: "", given: "{\"act\":\"a-1\",\"why\":\"the dump waits too long\"}", wire: "{\"act\":\"a-1\",\"why\":\"the dump waits too long\"}",
|
||||
decode: func(b []byte) (any, error) { var v CancelInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "cancel answers an error naming the step an act has begun", subject: "", given: "{\"error\":\"the act has begun: step restoring\"}", wire: "{\"error\":\"the act has begun: step restoring\"}",
|
||||
decode: func(b []byte) (any, error) { var v CancelAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not decode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
back, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Errorf("%s: does not encode: %v", c.name, err)
|
||||
continue
|
||||
}
|
||||
var got, want any
|
||||
if err := json.Unmarshal(back, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(c.wire), &want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("%s: reads back as %s, want %s", c.name, back, c.wire)
|
||||
}
|
||||
if c.subjectIs != nil && !c.subjectIs.MatchString(c.subject) {
|
||||
t.Errorf("%s: subject %q is not the contract's %s", c.name, c.subject, c.subjectIs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module git.novox.be/novox/mesh-contracts/go/seats/meshrecovery
|
||||
|
||||
go 1.26.0
|
||||
+111
-25
@@ -28,6 +28,8 @@ const (
|
||||
VerbManifestReads = "manifest-reads"
|
||||
VerbSeatList = "seat-list"
|
||||
VerbSeatListAgain = "seat-list-again"
|
||||
VerbFirstViews = "first-views"
|
||||
VerbFirstViewAgain = "first-view-again"
|
||||
VerbProcessBuilds = "process-builds"
|
||||
)
|
||||
|
||||
@@ -38,17 +40,18 @@ const (
|
||||
EventManifestReadsChanged = "manifest-reads-changed"
|
||||
EventComposed = "composed"
|
||||
EventRunning = "running"
|
||||
EventCapabilitiesChanged = "capabilities-changed"
|
||||
EventWaitHeld = "wait-held"
|
||||
EventWaitEnded = "wait-ended"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
func Verbs() []string {
|
||||
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbManifestReads, VerbSeatList, VerbSeatListAgain, VerbProcessBuilds}
|
||||
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbManifestReads, VerbSeatList, VerbSeatListAgain, VerbFirstViews, VerbFirstViewAgain, VerbProcessBuilds}
|
||||
}
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventAsked, EventDone, EventRefused, EventManifestReadsChanged, EventComposed, EventRunning, EventCapabilitiesChanged}
|
||||
return []string{EventAsked, EventDone, EventRefused, EventManifestReadsChanged, EventComposed, EventRunning, EventWaitHeld, EventWaitEnded}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
@@ -88,13 +91,27 @@ func SubjectRunning(node, module string) string {
|
||||
// SubjectRunningAll hears every running.
|
||||
const SubjectRunningAll = "mesh.seat.node-engine.event.running.*.*"
|
||||
|
||||
// SubjectCapabilitiesChanged is where capabilities-changed is published, on mesh.seat.node-engine.event.capabilities-changed.<node>.
|
||||
func SubjectCapabilitiesChanged(node string) string {
|
||||
return "mesh.seat.node-engine.event.capabilities-changed." + node
|
||||
// StreamWaitHeld is the stream wait-held is kept in.
|
||||
const StreamWaitHeld = "SEAT_node-engine_wait-held"
|
||||
|
||||
// SubjectWaitHeld is where wait-held is published, on mesh.seat.node-engine.event.wait-held.<node>.<module>.
|
||||
func SubjectWaitHeld(node, module string) string {
|
||||
return "mesh.seat.node-engine.event.wait-held." + node + "." + module
|
||||
}
|
||||
|
||||
// SubjectCapabilitiesChangedAll hears every capabilities-changed.
|
||||
const SubjectCapabilitiesChangedAll = "mesh.seat.node-engine.event.capabilities-changed.*"
|
||||
// SubjectWaitHeldAll hears every wait-held.
|
||||
const SubjectWaitHeldAll = "mesh.seat.node-engine.event.wait-held.*.*"
|
||||
|
||||
// StreamWaitEnded is the stream wait-ended is kept in.
|
||||
const StreamWaitEnded = "SEAT_node-engine_wait-ended"
|
||||
|
||||
// SubjectWaitEnded is where wait-ended is published, on mesh.seat.node-engine.event.wait-ended.<node>.<module>.
|
||||
func SubjectWaitEnded(node, module string) string {
|
||||
return "mesh.seat.node-engine.event.wait-ended." + node + "." + module
|
||||
}
|
||||
|
||||
// SubjectWaitEndedAll hears every wait-ended.
|
||||
const SubjectWaitEndedAll = "mesh.seat.node-engine.event.wait-ended.*.*"
|
||||
|
||||
const (
|
||||
StateHave = "have"
|
||||
@@ -552,18 +569,6 @@ type CandidateRefusal struct {
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// CapabilitiesChanged is a node's capabilities changed, which the assigner places by.
|
||||
type CapabilitiesChanged struct {
|
||||
// when, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
// its capabilities now
|
||||
Capabilities []string `json:"capabilities"`
|
||||
// the node
|
||||
Node string `json:"node"`
|
||||
// the layout's version
|
||||
V int64 `json:"v"`
|
||||
}
|
||||
|
||||
// Composed is an entry composed on a node (design 47 *What each step says*).
|
||||
type Composed struct {
|
||||
// when, RFC 3339
|
||||
@@ -885,19 +890,21 @@ type RootVerdict struct {
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// Running is a module running healthy on a node at a build: what the node-engines of other nodes and the grants part decide a seat's pass from (ADR 0660 §2), kept last per subject with no age limit.
|
||||
// Running is a module's build running on a node, and its health: what the node-engines of other nodes and the grants part decide a seat's pass from (ADR 0660 §2), and the assigner reads a provider's and a canary's health and a put's end from (design 47 G 520); said again on the same subject each time healthy changes and for every module the have holds at the engine's start, kept last per subject with no age limit.
|
||||
type Running struct {
|
||||
// when it was seen running healthy, RFC 3339
|
||||
// when it was said, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
// the fingerprint of the build it runs; none with stopped
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// the health of the build that runs, as the node-engine's have holds it from the module's declared check (ADR 0240); a running with none, said by a node-engine of before design 47 G, reads as healthy (design 47 G 520). SeatsToPassOn reads none of it: an unhealthy holder still holds its seat
|
||||
Healthy *bool `json:"healthy,omitempty"`
|
||||
// the digest of that build's manifest, the indexer's manifest of it, read by digest from the catalog half
|
||||
Manifest string `json:"manifest,omitempty"`
|
||||
// the module
|
||||
Module string `json:"module"`
|
||||
// the node
|
||||
Node string `json:"node"`
|
||||
// true when the module no longer runs on the node: no fingerprint or manifest then (design 47 G 365, 404); mesh-sdk's SeatsToPassOn counts such a module as not running there
|
||||
// true when the have no longer holds the module on the node, never for an unhealthy build: no fingerprint or manifest then (design 47 G 365, 404, 520); mesh-sdk's SeatsToPassOn counts such a module as not running there
|
||||
Stopped bool `json:"stopped,omitempty"`
|
||||
// the layout's version
|
||||
V int64 `json:"v"`
|
||||
@@ -987,6 +994,37 @@ type UserListWritten struct {
|
||||
Wanted string `json:"wanted"`
|
||||
}
|
||||
|
||||
// Wait is a hold or wait a node-engine says, the fields of the seat mesh-wants' wait (design 47 G 498, 512, 629); in the have it also carries ask-at and act (design 47 G 560).
|
||||
type Wait struct {
|
||||
// in the have: the act the recovery module answered accepted with (design 47 G 560)
|
||||
Act string `json:"act,omitempty"`
|
||||
// in the have: the time of the first ask of the recovery module for this wait (design 47 G 560)
|
||||
AskAt *time.Time `json:"ask-at,omitempty"`
|
||||
// a duration such as 30m
|
||||
Bound string `json:"bound,omitempty"`
|
||||
// the time the bound counts from (design 47 G 266, 269, 498)
|
||||
ClockFrom *time.Time `json:"clock-from,omitempty"`
|
||||
// the node waited on: the build node for a kept build; the subject's <node> is the node that says it
|
||||
Node string `json:"node,omitempty"`
|
||||
// what it waits on
|
||||
On string `json:"on,omitempty"`
|
||||
// the seat holding the wait: node-engine
|
||||
Owner string `json:"owner,omitempty"`
|
||||
// why it holds, such as restore point missing, entry-missing, parts-differ, node away or build no longer kept (design 47 G 541, 499)
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Since time.Time `json:"since"`
|
||||
// compose: a hold of the composition (a restore point missing, an entry missing, parts that differ, a recovery that waits); kept-build: waiting for a kept build (design 47 G 499, 541)
|
||||
Step string `json:"step"`
|
||||
// the wait said in words
|
||||
Words string `json:"words,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Wait's step.
|
||||
const (
|
||||
WaitStepCompose = "compose"
|
||||
WaitStepKeptBuild = "kept-build"
|
||||
)
|
||||
|
||||
// HandOverInput is what hand-over takes.
|
||||
type HandOverInput struct {
|
||||
// the directory's absolute path, as the engine states it
|
||||
@@ -1324,6 +1362,50 @@ type SeatListAgainInput struct {
|
||||
// SeatListAgainAnswer is what seat-list-again answers.
|
||||
type SeatListAgainAnswer = SeatList
|
||||
|
||||
// FirstViewsInput is what first-views takes.
|
||||
type FirstViewsInput struct {
|
||||
}
|
||||
|
||||
// FirstViewsAnswer is what first-views answers.
|
||||
type FirstViewsAnswer struct {
|
||||
// one status per owner and state followed, as mesh-sdk's views runtime keeps it
|
||||
FirstViews []map[string]any `json:"first-views"`
|
||||
}
|
||||
|
||||
// FirstViewAgainInput is what first-view-again takes.
|
||||
type FirstViewAgainInput struct {
|
||||
// the owner module, as first-views names it
|
||||
Owner string `json:"owner"`
|
||||
// the owner's state, as first-views names it
|
||||
State string `json:"state"`
|
||||
// why it is asked again, in plain words
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// FirstViewAgainAnswerAsked is the hand ask written.
|
||||
type FirstViewAgainAnswerAsked struct {
|
||||
// when it was asked, RFC 3339
|
||||
At time.Time `json:"at"`
|
||||
// the caller the runtime proved
|
||||
By string `json:"by"`
|
||||
// the owner module
|
||||
Owner string `json:"owner"`
|
||||
// the owner's state
|
||||
State string `json:"state"`
|
||||
// the layout's version
|
||||
V int64 `json:"v"`
|
||||
// why it was asked again
|
||||
Why string `json:"why"`
|
||||
}
|
||||
|
||||
// FirstViewAgainAnswer is what first-view-again answers.
|
||||
type FirstViewAgainAnswer struct {
|
||||
// the hand ask written
|
||||
Asked FirstViewAgainAnswerAsked `json:"asked"`
|
||||
// the verb that reads the status, first-views
|
||||
Read string `json:"read"`
|
||||
}
|
||||
|
||||
// ProcessBuildsInput is what process-builds takes.
|
||||
type ProcessBuildsInput struct {
|
||||
// only this module's processes
|
||||
@@ -1351,8 +1433,11 @@ type ComposedEvent = Composed
|
||||
// RunningEvent is the body of the event running.
|
||||
type RunningEvent = Running
|
||||
|
||||
// CapabilitiesChangedEvent is the body of the event capabilities-changed.
|
||||
type CapabilitiesChangedEvent = CapabilitiesChanged
|
||||
// WaitHeldEvent is the body of the event wait-held.
|
||||
type WaitHeldEvent = Wait
|
||||
|
||||
// WaitEndedEvent is the body of the event wait-ended.
|
||||
type WaitEndedEvent = Wait
|
||||
|
||||
// HaveByNodeModuleValue is one value of the state have under the key <node>.<module>.
|
||||
type HaveByNodeModuleValue struct {
|
||||
@@ -1380,6 +1465,7 @@ type HaveByNodeModuleValue struct {
|
||||
SealedAt map[string]string `json:"sealed-at,omitempty"`
|
||||
UserList *UserListWritten `json:"user-list,omitempty"`
|
||||
V int64 `json:"v"`
|
||||
Wait *Wait `json:"wait,omitempty"`
|
||||
// in phase waiting-yes, the digest of the composition that waits
|
||||
Waits string `json:"waits,omitempty"`
|
||||
}
|
||||
|
||||
@@ -39,14 +39,16 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v ComposedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.composed\\.[^.*> ]+$")},
|
||||
{name: "running is said, its node and module in its subject", subject: "mesh.seat.node-engine.event.running.ace.nats", given: "{\"at\":\"2026-11-09T20:01:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-09T20:01:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v RunningEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.running\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "running says the build that runs is healthy", subject: "mesh.seat.node-engine.event.running.ace.nats", given: "{\"at\":\"2026-11-11T09:00:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"healthy\":true,\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-11T09:00:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"healthy\":true,\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v RunningEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.running\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "running says the build that runs turned unhealthy, still running", subject: "mesh.seat.node-engine.event.running.ace.nats", given: "{\"at\":\"2026-11-11T09:05:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"healthy\":false,\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-11T09:05:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"healthy\":false,\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v RunningEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.running\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "running says a module stopped on the node, with no fingerprint", subject: "mesh.seat.node-engine.event.running.ace.nats", given: "{\"at\":\"2026-11-11T09:00:00Z\",\"module\":\"nats\",\"node\":\"ace\",\"stopped\":true,\"v\":1}", wire: "{\"at\":\"2026-11-11T09:00:00Z\",\"module\":\"nats\",\"node\":\"ace\",\"stopped\":true,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v RunningEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.running\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "capabilities-changed is said", subject: "mesh.seat.node-engine.event.capabilities-changed.ace", given: "{\"at\":\"2026-11-09T20:02:00Z\",\"capabilities\":[\"build-go\",\"docker\"],\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-09T20:02:00Z\",\"capabilities\":[\"build-go\",\"docker\"],\"node\":\"ace\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v CapabilitiesChangedEvent
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.capabilities-changed\\.[^.*> ]+$")},
|
||||
{name: "a kept build's wait is held", subject: "mesh.seat.node-engine.event.wait-held.ace.postgres", given: "{\"bound\":\"60m\",\"clock-from\":\"2026-11-11T10:00:00Z\",\"node\":\"ace\",\"on\":\"the kept build of postgres\",\"owner\":\"node-engine\",\"reason\":\"node away\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"kept-build\",\"words\":\"waiting for the kept build of postgres on ace\"}", wire: "{\"bound\":\"60m\",\"clock-from\":\"2026-11-11T10:00:00Z\",\"node\":\"ace\",\"on\":\"the kept build of postgres\",\"owner\":\"node-engine\",\"reason\":\"node away\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"kept-build\",\"words\":\"waiting for the kept build of postgres on ace\"}",
|
||||
decode: func(b []byte) (any, error) { var v WaitHeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.wait-held\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "a hold of the composition ends", subject: "mesh.seat.node-engine.event.wait-ended.ace.postgres", given: "{\"owner\":\"node-engine\",\"reason\":\"restore point missing\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"compose\"}", wire: "{\"owner\":\"node-engine\",\"reason\":\"restore point missing\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"compose\"}",
|
||||
decode: func(b []byte) (any, error) { var v WaitEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.wait-ended\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "the finding of a seat list unread, as the engine writes it", subject: "", given: "{\"at\":\"2026-11-05T14:00:00Z\",\"key\":\"machine.ace.seat-list-unread\",\"kind\":\"seat-list-unread\",\"observer\":\"mesh-host\",\"severity\":\"warning\",\"source\":\"node-engine.seat-list\",\"subject\":{\"id\":\"ace\",\"machine\":\"ace\",\"scope\":\"machine\"},\"summary\":\"the seat list has not been read for five minutes\",\"v\":1}", wire: "{\"at\":\"2026-11-05T14:00:00Z\",\"key\":\"machine.ace.seat-list-unread\",\"kind\":\"seat-list-unread\",\"observer\":\"mesh-host\",\"severity\":\"warning\",\"source\":\"node-engine.seat-list\",\"subject\":{\"id\":\"ace\",\"machine\":\"ace\",\"scope\":\"machine\"},\"summary\":\"the seat list has not been read for five minutes\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v FindingsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a node's packages, present and absent (captured from ace, shortened)", subject: "", given: "{\"node\":\"ace\",\"packages\":[{\"absent\":false,\"modules\":[\"avahi\"],\"package\":\"avahi\"},{\"absent\":false,\"modules\":[\"mesh-build-go\",\"mesh-build-node\"],\"package\":\"bubblewrap\"},{\"absent\":true,\"modules\":[\"time-sync\"],\"package\":\"ntp\"}]}", wire: "{\"node\":\"ace\",\"packages\":[{\"absent\":false,\"modules\":[\"avahi\"],\"package\":\"avahi\"},{\"absent\":false,\"modules\":[\"mesh-build-go\",\"mesh-build-node\"],\"package\":\"bubblewrap\"},{\"absent\":true,\"modules\":[\"time-sync\"],\"package\":\"ntp\"}]}",
|
||||
@@ -89,6 +91,8 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v DataByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a node's health (typed from the engine's values)", subject: "", given: "{\"at\":\"2026-11-05T12:00:00Z\",\"contract\":3,\"network\":{\"parts\":[{\"part\":\"resolver\",\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"}],\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"},\"node\":\"ace\",\"peers\":[{\"addresses\":[\"198.51.100.1\"],\"key\":\"k\",\"seen\":true}],\"relogin\":[{\"account\":\"web\",\"reason\":\"password changed\",\"since\":\"2026-11-05T11:00:00Z\"}],\"units\":{\"failed\":[{\"result\":\"exit-code\",\"scope\":\"system\",\"since\":\"2026-11-05T11:00:00Z\",\"unit\":\"x.service\"}],\"state\":\"degraded\"},\"v\":1}", wire: "{\"at\":\"2026-11-05T12:00:00Z\",\"contract\":3,\"network\":{\"parts\":[{\"part\":\"resolver\",\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"}],\"since\":\"2026-11-05T10:00:00Z\",\"state\":\"running\"},\"node\":\"ace\",\"peers\":[{\"addresses\":[\"198.51.100.1\"],\"key\":\"k\",\"seen\":true}],\"relogin\":[{\"account\":\"web\",\"reason\":\"password changed\",\"since\":\"2026-11-05T11:00:00Z\"}],\"units\":{\"failed\":[{\"result\":\"exit-code\",\"scope\":\"system\",\"since\":\"2026-11-05T11:00:00Z\",\"unit\":\"x.service\"}],\"state\":\"degraded\"},\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v HealthByNodeValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a have that waits for the recovery module, with its wait, ask-at and act (design 47 G 560, 629)", subject: "", given: "{\"detail\":\"waiting for a restore point\",\"fp\":\"0123456789ab\",\"health\":\"waits\",\"phase\":\"held\",\"v\":1,\"wait\":{\"act\":\"a-20261111t100000\",\"ask-at\":\"2026-11-11T10:00:00Z\",\"bound\":\"90m\",\"clock-from\":\"2026-11-11T10:00:00Z\",\"on\":\"the recovery module's ended\",\"owner\":\"node-engine\",\"reason\":\"recovery wait\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"compose\",\"words\":\"waiting for the point of postgres's data\"}}", wire: "{\"detail\":\"waiting for a restore point\",\"fp\":\"0123456789ab\",\"health\":\"waits\",\"phase\":\"held\",\"v\":1,\"wait\":{\"act\":\"a-20261111t100000\",\"ask-at\":\"2026-11-11T10:00:00Z\",\"bound\":\"90m\",\"clock-from\":\"2026-11-11T10:00:00Z\",\"on\":\"the recovery module's ended\",\"owner\":\"node-engine\",\"reason\":\"recovery wait\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"compose\",\"words\":\"waiting for the point of postgres's data\"}}",
|
||||
decode: func(b []byte) (any, error) { var v HaveByNodeModuleValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "manifest-reads answers what the engine reads and when it stated it", subject: "", given: "{\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"stated-at\":\"2026-11-04T10:00:00Z\",\"v\":1}", wire: "{\"manifest-reads\":[\"form:seat-reference\",\"word:series\"],\"node\":\"ace\",\"stated-at\":\"2026-11-04T10:00:00Z\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v ManifestReadsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "manifest-reads of an engine built before it said them is the baseline", subject: "", given: "{\"node\":\"g14\",\"stated-at\":\"2026-11-04T10:05:00Z\",\"v\":1}", wire: "{\"node\":\"g14\",\"stated-at\":\"2026-11-04T10:05:00Z\",\"v\":1}",
|
||||
@@ -109,6 +113,8 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v ProcessBuildsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "process-builds answers an error", subject: "", given: "{\"error\":\"the service manager did not answer\",\"node\":\"ace\",\"processes\":[]}", wire: "{\"error\":\"the service manager did not answer\",\"node\":\"ace\",\"processes\":[]}",
|
||||
decode: func(b []byte) (any, error) { var v ProcessBuildsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "first-view-again names the owner, its state and why", subject: "", given: "{\"owner\":\"mesh-delivery-indexer\",\"state\":\"manifests\",\"why\":\"the picture lags the indexer by hand\"}", wire: "{\"owner\":\"mesh-delivery-indexer\",\"state\":\"manifests\",\"why\":\"the picture lags the indexer by hand\"}",
|
||||
decode: func(b []byte) (any, error) { var v FirstViewAgainInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -95,26 +95,36 @@ func DecodeConditionsByKeyValue(raw []byte) (ConditionsByKeyValue, error) {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// FindingsByKeyKey is the key of one value of the state findings under the form "<key>".
|
||||
func FindingsByKeyKey(key string) string { return key }
|
||||
// FindingsByScopeIDKindKey is the key of one value of the state findings under the form "<scope>.<id>.<kind>".
|
||||
func FindingsByScopeIDKindKey(scope, id, kind string) string { return scope + "." + id + "." + kind }
|
||||
|
||||
// ParseFindingsByKeyKey reads the parts of a key of the form "<key>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseFindingsByKeyKey(key string) (string, bool) {
|
||||
// ParseFindingsByScopeIDKindKey reads the parts of a key of the form "<scope>.<id>.<kind>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseFindingsByScopeIDKindKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, true
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeFindingsByKeyValue reads one value of the state findings under the form "<key>", refusing a field FindingsByKeyValue does not know and any text after the value.
|
||||
func DecodeFindingsByKeyValue(raw []byte) (FindingsByKeyValue, error) {
|
||||
var v FindingsByKeyValue
|
||||
// DecodeFindingsByScopeIDKindValue reads one value of the state findings under the form "<scope>.<id>.<kind>", refusing a field FindingsByScopeIDKindValue does not know and any text after the value.
|
||||
func DecodeFindingsByScopeIDKindValue(raw []byte) (FindingsByScopeIDKindValue, error) {
|
||||
var v FindingsByScopeIDKindValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
@@ -290,7 +300,7 @@ type Finding struct {
|
||||
Needs string `json:"needs,omitempty"`
|
||||
// the module that wrote it
|
||||
Observer string `json:"observer"`
|
||||
// the module the finding is about, when not the observer
|
||||
// who the finding is about, when not the observer: a session who@node (^[a-z0-9][a-z0-9._-]*@[a-z0-9][a-z0-9-]*$), a seat's name (^[a-z][a-z0-9-]*$) or a commit's author forge:<account> (^forge:[A-Za-z0-9._-]+$); another is refused (design 47 G 678)
|
||||
Owner string `json:"owner,omitempty"`
|
||||
// the key's last part when it is not the kind
|
||||
Ref string `json:"ref,omitempty"`
|
||||
@@ -308,7 +318,7 @@ type Finding struct {
|
||||
Subject Subject `json:"subject"`
|
||||
// one line
|
||||
Summary string `json:"summary"`
|
||||
// what it waits on: a person, a part or a call
|
||||
// what it waits on: a session who@node (^[a-z0-9][a-z0-9._-]*@[a-z0-9][a-z0-9-]*$), a seat's name (^[a-z][a-z0-9-]*$) or a commit's author forge:<account> (^forge:[A-Za-z0-9._-]+$); another is refused (design 47 G 678)
|
||||
WaitingOn string `json:"waiting-on,omitempty"`
|
||||
}
|
||||
|
||||
@@ -344,6 +354,8 @@ type FindingsStated struct {
|
||||
Asked string `json:"asked,omitempty"`
|
||||
At time.Time `json:"at"`
|
||||
Findings []Finding `json:"findings"`
|
||||
// the finding kinds the stating bundle owns and states, the only kinds whose keys the statement deletes and whose conditions the keeper clears when it names none of them; none covers every kind, as a part with one bundle states (design 47 G 580)
|
||||
Kinds []string `json:"kinds,omitempty"`
|
||||
// the keys the observer cannot judge yet since its start, with their since and bound
|
||||
Pending []FindingsStatedPendingItem `json:"pending"`
|
||||
}
|
||||
@@ -477,46 +489,8 @@ const (
|
||||
ConditionsByKeyValueSeverityWarning = "warning"
|
||||
)
|
||||
|
||||
// FindingsByKeyValue is one value of the state findings under the key <key>.
|
||||
type FindingsByKeyValue struct {
|
||||
Actions []Action `json:"actions,omitempty"`
|
||||
At time.Time `json:"at"`
|
||||
// newest first, at most 10
|
||||
Evidence []Evidence `json:"evidence,omitempty"`
|
||||
Explanation string `json:"explanation,omitempty"`
|
||||
Headline string `json:"headline,omitempty"`
|
||||
// scope.id.token
|
||||
Key string `json:"key"`
|
||||
Kind string `json:"kind"`
|
||||
Needs string `json:"needs,omitempty"`
|
||||
// the module that wrote it
|
||||
Observer string `json:"observer"`
|
||||
// the module the finding is about, when not the observer
|
||||
Owner string `json:"owner,omitempty"`
|
||||
// the key's last part when it is not the kind
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Resolved string `json:"resolved,omitempty"`
|
||||
// self, operator, agent or healer:<name>
|
||||
Resolver string `json:"resolver,omitempty"`
|
||||
// what of its own saw it last
|
||||
Run string `json:"run,omitempty"`
|
||||
Severity string `json:"severity"`
|
||||
// the row, probe or event that saw it
|
||||
Source string `json:"source"`
|
||||
Subject Subject `json:"subject"`
|
||||
// one line
|
||||
Summary string `json:"summary"`
|
||||
// layout version, 1
|
||||
V int64 `json:"v"`
|
||||
// what it waits on: a person, a part or a call
|
||||
WaitingOn string `json:"waiting-on,omitempty"`
|
||||
}
|
||||
|
||||
// The values of FindingsByKeyValue's severity.
|
||||
const (
|
||||
FindingsByKeyValueSeverityUrgent = "urgent"
|
||||
FindingsByKeyValueSeverityWarning = "warning"
|
||||
)
|
||||
// FindingsByScopeIDKindValue is one value of the state findings under the key <scope>.<id>.<kind>.
|
||||
type FindingsByScopeIDKindValue = Finding
|
||||
|
||||
// FindingsViewsByObserverNodeValueStated is the last findings-stated set.
|
||||
type FindingsViewsByObserverNodeValueStated struct {
|
||||
|
||||
@@ -25,10 +25,30 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v ConditionsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a condition with an empty evidence trail of headline (captured; empty fields left out)", subject: "", given: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-30T13:03:46.432309782Z\",\"said\":\"The backup of postgres failed on ace\"}],\"explanation\":\"Needs you: answer whether to back up again or go on without a backup. The backup before applying postgres at 978b5db8cb4e on ace failed: node-backup.now could not be asked: not asked: nothing on this machine answers mesh.seat.node-backup.tool.now.ace.\",\"headline\":\"The backup of postgres failed on ace\",\"key\":\"module.ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f.backup\",\"kind\":\"backup\",\"last-observed\":\"2026-10-30T13:03:46.432309782Z\",\"needs\":\"answer whether to back up again or go on without a backup\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.069818178Z\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"assigner.backup\",\"subject\":{\"id\":\"ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"The backup of postgres failed on ace\"}", wire: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-30T13:03:46.432309782Z\",\"said\":\"The backup of postgres failed on ace\"}],\"explanation\":\"Needs you: answer whether to back up again or go on without a backup. The backup before applying postgres at 978b5db8cb4e on ace failed: node-backup.now could not be asked: not asked: nothing on this machine answers mesh.seat.node-backup.tool.now.ace.\",\"headline\":\"The backup of postgres failed on ace\",\"key\":\"module.ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f.backup\",\"kind\":\"backup\",\"last-observed\":\"2026-10-30T13:03:46.432309782Z\",\"needs\":\"answer whether to back up again or go on without a backup\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.069818178Z\",\"resolver\":\"self\",\"severity\":\"warning\",\"source\":\"assigner.backup\",\"subject\":{\"id\":\"ace.postgres.978b5db8cb4ef746ce68d82cc616d1439a53266d335e60f368dc44c4cb25610f\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"The backup of postgres failed on ace\"}",
|
||||
decode: func(b []byte) (any, error) { var v ConditionsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a finding of the bus scope with its zero time (captured from the watchman; empty fields left out)", subject: "", given: "{\"at\":\"0001-01-01T00:00:00Z\",\"evidence\":[{\"at\":\"0001-01-01T00:00:00Z\",\"said\":\"1 held for EVENTS.controller (counted again every 5m0s while any is held, so one delivered again or dropped ends this within it)\"}],\"key\":\"bus.EVENTS.controller.max-deliveries\",\"kind\":\"max-deliveries\",\"needs\":\"deliver them again or drop them, from the mesh MCP server.\",\"observer\":\"mesh-delivery-watchman\",\"severity\":\"warning\",\"source\":\"S9\",\"subject\":{\"id\":\"EVENTS.controller\",\"scope\":\"bus\"},\"summary\":\"the controller's consumer on EVENTS gave up on a message; it is kept in DEAD_LETTERS until delivered again or dropped, with why\",\"v\":1}", wire: "{\"at\":\"0001-01-01T00:00:00Z\",\"evidence\":[{\"at\":\"0001-01-01T00:00:00Z\",\"said\":\"1 held for EVENTS.controller (counted again every 5m0s while any is held, so one delivered again or dropped ends this within it)\"}],\"key\":\"bus.EVENTS.controller.max-deliveries\",\"kind\":\"max-deliveries\",\"needs\":\"deliver them again or drop them, from the mesh MCP server.\",\"observer\":\"mesh-delivery-watchman\",\"severity\":\"warning\",\"source\":\"S9\",\"subject\":{\"id\":\"EVENTS.controller\",\"scope\":\"bus\"},\"summary\":\"the controller's consumer on EVENTS gave up on a message; it is kept in DEAD_LETTERS until delivered again or dropped, with why\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v FindingsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a finding of the module scope with its machine (captured from the watchman; empty fields left out)", subject: "", given: "{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: owned by 10005:10005, mode 700, as found\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator.\",\"headline\":\"mesh-build-node waits for a directory on ace\",\"key\":\"module.mesh-build-node.ace.directory-used-as-found\",\"kind\":\"directory-used-as-found\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observer\":\"mesh-delivery-watchman\",\"resolved\":\"mesh-build-node's directory on ace is the mesh's\",\"severity\":\"warning\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-build-node.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"mesh-build-node on ace uses mesh-build-node.cache as found\",\"v\":1}", wire: "{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: owned by 10005:10005, mode 700, as found\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator.\",\"headline\":\"mesh-build-node waits for a directory on ace\",\"key\":\"module.mesh-build-node.ace.directory-used-as-found\",\"kind\":\"directory-used-as-found\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observer\":\"mesh-delivery-watchman\",\"resolved\":\"mesh-build-node's directory on ace is the mesh's\",\"severity\":\"warning\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-build-node.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"mesh-build-node on ace uses mesh-build-node.cache as found\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v FindingsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a finding of the bus scope with its zero time (in the finding layout (design 47 G 660))", subject: "", given: "{\"at\":\"0001-01-01T00:00:00Z\",\"evidence\":[{\"at\":\"0001-01-01T00:00:00Z\",\"said\":\"1 held for EVENTS.controller (counted again every 5m0s while any is held, so one delivered again or dropped ends this within it)\"}],\"id\":\"EVENTS.controller\",\"kind\":\"max-deliveries\",\"needs\":\"deliver them again or drop them, from the mesh MCP server.\",\"observer\":\"mesh-delivery-watchman\",\"scope\":\"bus\",\"severity\":\"warning\",\"since\":\"0001-01-01T00:00:00Z\",\"source\":\"S9\",\"subject\":{\"id\":\"EVENTS.controller\",\"scope\":\"bus\"},\"summary\":\"the controller's consumer on EVENTS gave up on a message; it is kept in DEAD_LETTERS until delivered again or dropped, with why\"}", wire: "{\"at\":\"0001-01-01T00:00:00Z\",\"evidence\":[{\"at\":\"0001-01-01T00:00:00Z\",\"said\":\"1 held for EVENTS.controller (counted again every 5m0s while any is held, so one delivered again or dropped ends this within it)\"}],\"id\":\"EVENTS.controller\",\"kind\":\"max-deliveries\",\"needs\":\"deliver them again or drop them, from the mesh MCP server.\",\"observer\":\"mesh-delivery-watchman\",\"scope\":\"bus\",\"severity\":\"warning\",\"since\":\"0001-01-01T00:00:00Z\",\"source\":\"S9\",\"subject\":{\"id\":\"EVENTS.controller\",\"scope\":\"bus\"},\"summary\":\"the controller's consumer on EVENTS gave up on a message; it is kept in DEAD_LETTERS until delivered again or dropped, with why\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v FindingsByScopeIDKindValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a finding of the module scope with its machine (in the finding layout (design 47 G 660))", subject: "", given: "{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: owned by 10005:10005, mode 700, as found\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator.\",\"headline\":\"mesh-build-node waits for a directory on ace\",\"id\":\"mesh-build-node.ace\",\"kind\":\"directory-used-as-found\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observer\":\"mesh-delivery-watchman\",\"scope\":\"module\",\"severity\":\"warning\",\"since\":\"2026-10-26T20:19:41.474823121Z\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-build-node.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"mesh-build-node on ace uses mesh-build-node.cache as found\"}", wire: "{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: owned by 10005:10005, mode 700, as found\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator.\",\"headline\":\"mesh-build-node waits for a directory on ace\",\"id\":\"mesh-build-node.ace\",\"kind\":\"directory-used-as-found\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observer\":\"mesh-delivery-watchman\",\"scope\":\"module\",\"severity\":\"warning\",\"since\":\"2026-10-26T20:19:41.474823121Z\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-build-node.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"mesh-build-node on ace uses mesh-build-node.cache as found\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v FindingsByScopeIDKindValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a finding owned by a session and waiting on a seat (design 47 G 678)", subject: "", given: "{\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"mesh-host.ace\",\"kind\":\"directory-used-as-found\",\"observer\":\"mesh-delivery-watchman\",\"owner\":\"jochen@shanks\",\"scope\":\"module\",\"severity\":\"warning\",\"since\":\"2026-11-11T10:00:00Z\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-host.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a directory waits for a hand\",\"waiting-on\":\"node-engine\"}", wire: "{\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"mesh-host.ace\",\"kind\":\"directory-used-as-found\",\"observer\":\"mesh-delivery-watchman\",\"owner\":\"jochen@shanks\",\"scope\":\"module\",\"severity\":\"warning\",\"since\":\"2026-11-11T10:00:00Z\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-host.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a directory waits for a hand\",\"waiting-on\":\"node-engine\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v FindingsByScopeIDKindValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a finding owned by a seat and waiting on a commit's author (design 47 G 678, 540)", subject: "", given: "{\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"mesh-host.ace\",\"kind\":\"directory-used-as-found\",\"observer\":\"mesh-delivery-watchman\",\"owner\":\"node-engine\",\"scope\":\"module\",\"severity\":\"warning\",\"since\":\"2026-11-11T10:00:00Z\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-host.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a directory waits for a hand\",\"waiting-on\":\"forge:jochen\"}", wire: "{\"at\":\"2026-11-11T10:00:00Z\",\"id\":\"mesh-host.ace\",\"kind\":\"directory-used-as-found\",\"observer\":\"mesh-delivery-watchman\",\"owner\":\"node-engine\",\"scope\":\"module\",\"severity\":\"warning\",\"since\":\"2026-11-11T10:00:00Z\",\"source\":\"health-report\",\"subject\":{\"id\":\"mesh-host.ace\",\"machine\":\"ace\",\"scope\":\"module\"},\"summary\":\"a directory waits for a hand\",\"waiting-on\":\"forge:jochen\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v FindingsByScopeIDKindValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -20,7 +20,8 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
EventHealerActed = "healer-acted"
|
||||
EventHealerActed = "healer-acted"
|
||||
EventAttemptChanged = "attempt-changed"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
@@ -30,7 +31,7 @@ func Verbs() []string {
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventHealerActed}
|
||||
return []string{EventHealerActed, EventAttemptChanged}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
@@ -42,6 +43,17 @@ const (
|
||||
// SubjectHealerActed is where healer-acted is published.
|
||||
const SubjectHealerActed = "mesh.seat.mesh-healers.event.healer-acted"
|
||||
|
||||
// StreamAttemptChanged is the stream attempt-changed is kept in.
|
||||
const StreamAttemptChanged = "SEAT_mesh-healers_attempt-changed"
|
||||
|
||||
// SubjectAttemptChanged is where attempt-changed is published, on mesh.seat.mesh-healers.event.attempt-changed.<condition>.<attempt>.
|
||||
func SubjectAttemptChanged(condition, attempt string) string {
|
||||
return "mesh.seat.mesh-healers.event.attempt-changed." + condition + "." + attempt
|
||||
}
|
||||
|
||||
// SubjectAttemptChangedAll hears every attempt-changed.
|
||||
const SubjectAttemptChangedAll = "mesh.seat.mesh-healers.event.attempt-changed.*.*"
|
||||
|
||||
const (
|
||||
StateAttempts = "attempts"
|
||||
StateWould = "would"
|
||||
@@ -165,6 +177,32 @@ const (
|
||||
HealerActedEventOutcomeEscalated = "escalated"
|
||||
)
|
||||
|
||||
// AttemptChangedEvent is the body of the event attempt-changed.
|
||||
type AttemptChangedEvent struct {
|
||||
Act string `json:"act,omitempty"`
|
||||
Begun *time.Time `json:"begun,omitempty"`
|
||||
// what the budget counts by: the condition's key unless the healer says another
|
||||
BudgetKey string `json:"budget-key,omitempty"`
|
||||
Condition string `json:"condition,omitempty"`
|
||||
Finished *time.Time `json:"finished,omitempty"`
|
||||
Healer string `json:"healer,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Outcome string `json:"outcome,omitempty"`
|
||||
// true when the healer ends the attempt
|
||||
Removed bool `json:"removed,omitempty"`
|
||||
Said string `json:"said,omitempty"`
|
||||
// the end of that key's lifetime, which the keeper drops the attempt at on its own clock
|
||||
Until *time.Time `json:"until,omitempty"`
|
||||
}
|
||||
|
||||
// The values of AttemptChangedEvent's outcome.
|
||||
const (
|
||||
AttemptChangedEventOutcomeActing = "acting"
|
||||
AttemptChangedEventOutcomeActed = "acted"
|
||||
AttemptChangedEventOutcomeFailed = "failed"
|
||||
AttemptChangedEventOutcomeEscalated = "escalated"
|
||||
)
|
||||
|
||||
// AttemptsByAttemptValue is one value of the state attempts under the key <attempt>.
|
||||
type AttemptsByAttemptValue struct {
|
||||
Act string `json:"act"`
|
||||
|
||||
@@ -17,6 +17,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode func([]byte) (any, error)
|
||||
subjectIs *regexp.Regexp
|
||||
}{
|
||||
{name: "an attempt begun, said with its end (design 47 G 665)", subject: "mesh.seat.mesh-healers.event.attempt-changed.c-1.a-1", given: "{\"act\":\"hand-over\",\"begun\":\"2026-11-11T10:00:00Z\",\"budget-key\":\"module.mesh-host.ace.directory-used-as-found\",\"condition\":\"module.mesh-host.ace.directory-used-as-found\",\"healer\":\"directory\",\"kind\":\"directory-used-as-found\",\"outcome\":\"acting\",\"until\":\"2026-11-11T12:00:00Z\"}", wire: "{\"act\":\"hand-over\",\"begun\":\"2026-11-11T10:00:00Z\",\"budget-key\":\"module.mesh-host.ace.directory-used-as-found\",\"condition\":\"module.mesh-host.ace.directory-used-as-found\",\"healer\":\"directory\",\"kind\":\"directory-used-as-found\",\"outcome\":\"acting\",\"until\":\"2026-11-11T12:00:00Z\"}",
|
||||
decode: func(b []byte) (any, error) { var v AttemptChangedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-healers\\.event\\.attempt-changed\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "an attempt ended by its healer", subject: "mesh.seat.mesh-healers.event.attempt-changed.c-1.a-1", given: "{\"condition\":\"module.mesh-host.ace.directory-used-as-found\",\"removed\":true}", wire: "{\"condition\":\"module.mesh-host.ace.directory-used-as-found\",\"removed\":true}",
|
||||
decode: func(b []byte) (any, error) { var v AttemptChangedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-healers\\.event\\.attempt-changed\\.[^.*> ]+\\.[^.*> ]+$")},
|
||||
{name: "an act a healer would have made, kept from the controller's time (captured)", subject: "", given: "{\"act\":\"assert the module's buckets, logs and streams again through the grants part (grants_assert)\",\"at\":\"2026-11-02T08:15:56.49507336Z\",\"condition\":\"bus.EVENTS.4fs9xzyDY7CD2Cf1wVFYDK_1.consumer-lost\",\"healer\":\"H3\",\"kind\":\"consumer-lost\",\"why\":\"the controller's lease names a controller (or none is written, so one may take it): every act is written as would\"}", wire: "{\"act\":\"assert the module's buckets, logs and streams again through the grants part (grants_assert)\",\"at\":\"2026-11-02T08:15:56.49507336Z\",\"condition\":\"bus.EVENTS.4fs9xzyDY7CD2Cf1wVFYDK_1.consumer-lost\",\"healer\":\"H3\",\"kind\":\"consumer-lost\",\"why\":\"the controller's lease names a controller (or none is written, so one may take it): every act is written as would\"}",
|
||||
decode: func(b []byte) (any, error) { var v WouldByWouldValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
|
||||
+10
-40
@@ -41,11 +41,11 @@ export declare function conditionsByKeyKey(key: string): string;
|
||||
/** The parts of a key of the form "<key>", or undefined for a key of another form. */
|
||||
export declare function parseConditionsByKeyKey(_key: string): { key: string } | undefined;
|
||||
|
||||
/** The key of one value of the state findings under the form "<key>". */
|
||||
export declare function findingsByKeyKey(key: string): string;
|
||||
/** The key of one value of the state findings under the form "<scope>.<id>.<kind>". */
|
||||
export declare function findingsByScopeIDKindKey(scope: string, id: string, kind: string): string;
|
||||
|
||||
/** The parts of a key of the form "<key>", or undefined for a key of another form. */
|
||||
export declare function parseFindingsByKeyKey(_key: string): { key: string } | undefined;
|
||||
/** The parts of a key of the form "<scope>.<id>.<kind>", or undefined for a key of another form. */
|
||||
export declare function parseFindingsByScopeIDKindKey(_key: string): { scope: string; id: string; kind: string } | undefined;
|
||||
|
||||
/** The key of one value of the state findings-views under the form "<observer>.<node>". */
|
||||
export declare function findingsViewsByObserverNodeKey(observer: string, node: string): string;
|
||||
@@ -154,7 +154,7 @@ export interface Finding {
|
||||
"needs"?: string;
|
||||
/** the module that wrote it */
|
||||
"observer": string;
|
||||
/** the module the finding is about, when not the observer */
|
||||
/** who the finding is about, when not the observer: a session who@node (^[a-z0-9][a-z0-9._-]*@[a-z0-9][a-z0-9-]*$), a seat's name (^[a-z][a-z0-9-]*$) or a commit's author forge:<account> (^forge:[A-Za-z0-9._-]+$); another is refused (design 47 G 678) */
|
||||
"owner"?: string;
|
||||
/** the key's last part when it is not the kind */
|
||||
"ref"?: string;
|
||||
@@ -172,7 +172,7 @@ export interface Finding {
|
||||
"subject": Subject;
|
||||
/** one line */
|
||||
"summary": string;
|
||||
/** what it waits on: a person, a part or a call */
|
||||
/** what it waits on: a session who@node (^[a-z0-9][a-z0-9._-]*@[a-z0-9][a-z0-9-]*$), a seat's name (^[a-z][a-z0-9-]*$) or a commit's author forge:<account> (^forge:[A-Za-z0-9._-]+$); another is refused (design 47 G 678) */
|
||||
"waiting-on"?: string;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,8 @@ export interface FindingsStated {
|
||||
"asked"?: string;
|
||||
"at": string;
|
||||
"findings": Finding[];
|
||||
/** the finding kinds the stating bundle owns and states, the only kinds whose keys the statement deletes and whose conditions the keeper clears when it names none of them; none covers every kind, as a part with one bundle states (design 47 G 580) */
|
||||
"kinds"?: string[];
|
||||
/** the keys the observer cannot judge yet since its start, with their since and bound */
|
||||
"pending": FindingsStatedPendingItem[];
|
||||
}
|
||||
@@ -318,40 +320,8 @@ export interface ConditionsByKeyValue {
|
||||
"tried"?: ConditionsByKeyValueTriedItem[];
|
||||
}
|
||||
|
||||
/** is one value of the state findings under the key <key>. */
|
||||
export interface FindingsByKeyValue {
|
||||
"actions"?: Action[];
|
||||
"at": string;
|
||||
/** newest first, at most 10 */
|
||||
"evidence"?: Evidence[];
|
||||
"explanation"?: string;
|
||||
"headline"?: string;
|
||||
/** scope.id.token */
|
||||
"key": string;
|
||||
"kind": string;
|
||||
"needs"?: string;
|
||||
/** the module that wrote it */
|
||||
"observer": string;
|
||||
/** the module the finding is about, when not the observer */
|
||||
"owner"?: string;
|
||||
/** the key's last part when it is not the kind */
|
||||
"ref"?: string;
|
||||
"resolved"?: string;
|
||||
/** self, operator, agent or healer:<name> */
|
||||
"resolver"?: string;
|
||||
/** what of its own saw it last */
|
||||
"run"?: string;
|
||||
"severity": "urgent" | "warning";
|
||||
/** the row, probe or event that saw it */
|
||||
"source": string;
|
||||
"subject": Subject;
|
||||
/** one line */
|
||||
"summary": string;
|
||||
/** layout version, 1 */
|
||||
"v": number;
|
||||
/** what it waits on: a person, a part or a call */
|
||||
"waiting-on"?: string;
|
||||
}
|
||||
/** is one value of the state findings under the key <scope>.<id>.<kind>. */
|
||||
export type FindingsByScopeIDKindValue = Finding;
|
||||
|
||||
/** the last findings-stated set */
|
||||
export interface FindingsViewsByObserverNodeValueStated {
|
||||
|
||||
@@ -50,19 +50,27 @@ export function parseConditionsByKeyKey(_key) {
|
||||
return { key };
|
||||
}
|
||||
|
||||
/** The key of one value of the state findings under the form "<key>". */
|
||||
export function findingsByKeyKey(key) {
|
||||
return key;
|
||||
/** The key of one value of the state findings under the form "<scope>.<id>.<kind>". */
|
||||
export function findingsByScopeIDKindKey(scope, id, kind) {
|
||||
return scope + "." + id + "." + kind;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<key>", or undefined for a key of another form. */
|
||||
export function parseFindingsByKeyKey(_key) {
|
||||
/** The parts of a key of the form "<scope>.<id>.<kind>", or undefined for a key of another form. */
|
||||
export function parseFindingsByScopeIDKindKey(_key) {
|
||||
let _rest = _key;
|
||||
const _at0 = _rest.indexOf(".");
|
||||
if (_at0 <= 0) return undefined;
|
||||
const scope = _rest.slice(0, _at0);
|
||||
_rest = _rest.slice(_at0 + 1);
|
||||
const _at2 = _rest.indexOf(".");
|
||||
if (_at2 <= 0) return undefined;
|
||||
const id = _rest.slice(0, _at2);
|
||||
_rest = _rest.slice(_at2 + 1);
|
||||
if (_rest === "") return undefined;
|
||||
const key = _rest;
|
||||
const kind = _rest;
|
||||
_rest = "";
|
||||
if (_rest !== "") return undefined;
|
||||
return { key };
|
||||
return { scope, id, kind };
|
||||
}
|
||||
|
||||
/** The key of one value of the state findings-views under the form "<observer>.<node>". */
|
||||
|
||||
@@ -12,12 +12,19 @@ export declare const verbs: {
|
||||
|
||||
export declare const events: {
|
||||
readonly healerActed: "healer-acted";
|
||||
readonly attemptChanged: "attempt-changed";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "mesh-healers.events"; readonly maxAgeDays: 7 };
|
||||
|
||||
export declare const healerActedSubject: "mesh.seat.mesh-healers.event.healer-acted";
|
||||
|
||||
export declare const attemptChangedStream: "SEAT_mesh-healers_attempt-changed";
|
||||
|
||||
export declare function attemptChangedSubject(condition: string, attempt: string): string;
|
||||
|
||||
export declare const attemptChangedSubjectAll: "mesh.seat.mesh-healers.event.attempt-changed.*.*";
|
||||
|
||||
export declare const states: {
|
||||
readonly attempts: "attempts";
|
||||
readonly would: "would";
|
||||
@@ -83,6 +90,24 @@ export interface HealerActedEvent {
|
||||
"show"?: string;
|
||||
}
|
||||
|
||||
/** is the body of the event attempt-changed. */
|
||||
export interface AttemptChangedEvent {
|
||||
"act"?: string;
|
||||
"begun"?: string;
|
||||
/** what the budget counts by: the condition's key unless the healer says another */
|
||||
"budget-key"?: string;
|
||||
"condition"?: string;
|
||||
"finished"?: string;
|
||||
"healer"?: string;
|
||||
"kind"?: string;
|
||||
"outcome"?: "acting" | "acted" | "failed" | "escalated";
|
||||
/** true when the healer ends the attempt */
|
||||
"removed"?: boolean;
|
||||
"said"?: string;
|
||||
/** the end of that key's lifetime, which the keeper drops the attempt at on its own clock */
|
||||
"until"?: string;
|
||||
}
|
||||
|
||||
/** is one value of the state attempts under the key <attempt>. */
|
||||
export interface AttemptsByAttemptValue {
|
||||
"act": string;
|
||||
|
||||
@@ -12,12 +12,21 @@ export const verbs = {
|
||||
|
||||
export const events = {
|
||||
healerActed: "healer-acted",
|
||||
attemptChanged: "attempt-changed",
|
||||
};
|
||||
|
||||
export const family = { name: "mesh-healers.events", maxAgeDays: 7 };
|
||||
|
||||
export const healerActedSubject = "mesh.seat.mesh-healers.event.healer-acted";
|
||||
|
||||
export const attemptChangedStream = "SEAT_mesh-healers_attempt-changed";
|
||||
|
||||
export function attemptChangedSubject(condition, attempt) {
|
||||
return "mesh.seat.mesh-healers.event.attempt-changed." + condition + "." + attempt;
|
||||
}
|
||||
|
||||
export const attemptChangedSubjectAll = "mesh.seat.mesh-healers.event.attempt-changed.*.*";
|
||||
|
||||
export const states = {
|
||||
attempts: "attempts",
|
||||
would: "would",
|
||||
|
||||
@@ -389,6 +389,8 @@ type HistoryAnswer = map[string]any
|
||||
type NotifyInput struct {
|
||||
// it is over
|
||||
Clear bool `json:"clear,omitempty"`
|
||||
// send it on every channel's send, as a break-glass act is announced, not on the operator's chosen channel alone (design 47 G 514)
|
||||
EveryChannel bool `json:"every-channel,omitempty"`
|
||||
// one or two plain sentences
|
||||
Explanation string `json:"explanation,omitempty"`
|
||||
// plain words
|
||||
@@ -524,6 +526,8 @@ type AsksByAskerIDValue struct {
|
||||
Asker string `json:"asker"`
|
||||
// its asker has been told how it ended
|
||||
AskerTold bool `json:"asker_told,omitempty"`
|
||||
// when it ended, RFC 3339; present once it ended, a refused ask's refusal time (design 47 G 570, 638)
|
||||
At string `json:"at,omitempty"`
|
||||
// when it ends unanswered, RFC 3339
|
||||
Expires string `json:"expires"`
|
||||
// its headline
|
||||
|
||||
@@ -37,6 +37,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a fourth ask, the asker's oldest held back behind newer ones", subject: "", given: "{\"asker\":\"mesh-delivery-teller\",\"expires\":\"2026-11-11T10:00:00Z\",\"headline\":\"Check failed - merge into main\",\"held-since\":\"2026-11-10T10:30:00Z\",\"key\":\"mesh-delivery-teller.b-1\",\"opened\":\"2026-11-10T10:00:00Z\",\"state\":\"held-back\"}", wire: "{\"asker\":\"mesh-delivery-teller\",\"expires\":\"2026-11-11T10:00:00Z\",\"headline\":\"Check failed - merge into main\",\"held-since\":\"2026-11-10T10:30:00Z\",\"key\":\"mesh-delivery-teller.b-1\",\"opened\":\"2026-11-10T10:00:00Z\",\"state\":\"held-back\"}",
|
||||
decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "an ask the router refused at take-in, closed (design 47 G 570)", subject: "", given: "{\"answer\":\"refused: its expiry passes the bound of its highest option level\",\"asker\":\"nats\",\"asker_told\":true,\"at\":\"2026-11-11T10:00:01Z\",\"expires\":\"2026-11-12T10:00:00Z\",\"headline\":\"Recreate a bus entry\",\"key\":\"nats.r-1\",\"opened\":\"2026-11-11T10:00:00Z\",\"state\":\"refused\"}", wire: "{\"answer\":\"refused: its expiry passes the bound of its highest option level\",\"asker\":\"nats\",\"asker_told\":true,\"at\":\"2026-11-11T10:00:01Z\",\"expires\":\"2026-11-12T10:00:00Z\",\"headline\":\"Recreate a bus entry\",\"key\":\"nats.r-1\",\"opened\":\"2026-11-11T10:00:00Z\",\"state\":\"refused\"}",
|
||||
decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "notify asks the message sent on every channel", subject: "", given: "{\"every-channel\":true,\"key\":\"widens.mesh-delivery\",\"severity\":\"warning\",\"subject\":\"mesh-delivery\",\"summary\":\"a change widens what a module may do\"}", wire: "{\"every-channel\":true,\"key\":\"widens.mesh-delivery\",\"severity\":\"warning\",\"subject\":\"mesh-delivery\",\"summary\":\"a change widens what a module may do\"}",
|
||||
decode: func(b []byte) (any, error) { var v NotifyInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -305,6 +305,8 @@ export type HistoryAnswer = Record<string, unknown>;
|
||||
export interface NotifyInput {
|
||||
/** it is over */
|
||||
"clear"?: boolean;
|
||||
/** send it on every channel's send, as a break-glass act is announced, not on the operator's chosen channel alone (design 47 G 514) */
|
||||
"every-channel"?: boolean;
|
||||
/** one or two plain sentences */
|
||||
"explanation"?: string;
|
||||
/** plain words */
|
||||
@@ -424,6 +426,8 @@ export interface AsksByAskerIDValue {
|
||||
"asker": string;
|
||||
/** its asker has been told how it ended */
|
||||
"asker_told"?: boolean;
|
||||
/** when it ended, RFC 3339; present once it ended, a refused ask's refusal time (design 47 G 570, 638) */
|
||||
"at"?: string;
|
||||
/** when it ends unanswered, RFC 3339 */
|
||||
"expires": string;
|
||||
/** its headline */
|
||||
|
||||
Vendored
+9
@@ -183,8 +183,17 @@ export interface ListInput {
|
||||
"module"?: string;
|
||||
}
|
||||
|
||||
/** the sum of the bus's caps against its limits, in MiB; an absent limit is unlimited (design 47 G 547, 661) */
|
||||
export interface ListAnswerCaps {
|
||||
"account-limit-mib"?: number;
|
||||
"server-limit-mib"?: number;
|
||||
"sum-mib": number;
|
||||
}
|
||||
|
||||
/** is what list answers. */
|
||||
export interface ListAnswer {
|
||||
/** the sum of the bus's caps against its limits, in MiB; an absent limit is unlimited (design 47 G 547, 661) */
|
||||
"caps"?: ListAnswerCaps;
|
||||
/** every entry */
|
||||
"entries": Held[];
|
||||
}
|
||||
|
||||
Vendored
+330
@@ -0,0 +1,330 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "mesh-recovery", defined whole (seats/mesh-recovery/seat.json): Takes, sets aside and restores a module's kept data on the live bus, each act in the hand-act log (design 47 G 554). Its verbs answer at once and the result is the event.
|
||||
|
||||
export declare const seat: "mesh-recovery";
|
||||
|
||||
export declare const verbs: {
|
||||
readonly snapshotKept: "snapshot-kept";
|
||||
readonly restoreKept: "restore-kept";
|
||||
readonly keptPoints: "kept-points";
|
||||
readonly restoreAside: "restore-aside";
|
||||
readonly cancel: "cancel";
|
||||
};
|
||||
|
||||
export declare const events: {
|
||||
readonly ended: "ended";
|
||||
readonly restoreStarted: "restore-started";
|
||||
readonly restored: "restored";
|
||||
readonly setAside: "set-aside";
|
||||
readonly asideDiscarded: "aside-discarded";
|
||||
readonly pointRemoved: "point-removed";
|
||||
readonly expiryEnded: "expiry-ended";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "mesh-recovery.events"; readonly maxAgeDays: 7 };
|
||||
|
||||
export declare const endedStream: "SEAT_mesh-recovery_ended";
|
||||
|
||||
export declare function endedSubject(module: string, kept: string): string;
|
||||
|
||||
export declare const endedSubjectAll: "mesh.seat.mesh-recovery.event.ended.*.*";
|
||||
|
||||
export declare const restoreStartedStream: "SEAT_mesh-recovery_restore-started";
|
||||
|
||||
export declare function restoreStartedSubject(module: string, kept: string): string;
|
||||
|
||||
export declare const restoreStartedSubjectAll: "mesh.seat.mesh-recovery.event.restore-started.*.*";
|
||||
|
||||
export declare const restoredStream: "SEAT_mesh-recovery_restored";
|
||||
|
||||
export declare function restoredSubject(module: string, kept: string): string;
|
||||
|
||||
export declare const restoredSubjectAll: "mesh.seat.mesh-recovery.event.restored.*.*";
|
||||
|
||||
export declare const setAsideStream: "SEAT_mesh-recovery_set-aside";
|
||||
|
||||
export declare function setAsideSubject(module: string, kept: string): string;
|
||||
|
||||
export declare const setAsideSubjectAll: "mesh.seat.mesh-recovery.event.set-aside.*.*";
|
||||
|
||||
export declare const asideDiscardedStream: "SEAT_mesh-recovery_aside-discarded";
|
||||
|
||||
export declare function asideDiscardedSubject(module: string, kept: string): string;
|
||||
|
||||
export declare const asideDiscardedSubjectAll: "mesh.seat.mesh-recovery.event.aside-discarded.*.*";
|
||||
|
||||
export declare const pointRemovedStream: "SEAT_mesh-recovery_point-removed";
|
||||
|
||||
export declare function pointRemovedSubject(module: string, kept: string): string;
|
||||
|
||||
export declare const pointRemovedSubjectAll: "mesh.seat.mesh-recovery.event.point-removed.*.*";
|
||||
|
||||
export declare const expiryEndedStream: "SEAT_mesh-recovery_expiry-ended";
|
||||
|
||||
export declare const expiryEndedSubject: "mesh.seat.mesh-recovery.event.expiry-ended";
|
||||
|
||||
/** the live data set aside before a restore: a directory under aside/ in the recovery module's directory, nothing of it on the bus (design 47 G 516) */
|
||||
export interface Aside {
|
||||
/** the directory's name */
|
||||
"id": string;
|
||||
/** the bus name it was live under */
|
||||
"name": string;
|
||||
/** its bytes on disk */
|
||||
"size": number;
|
||||
}
|
||||
|
||||
/** the catch-up of the newer manifest: a tool and its arguments, never text (design 47 G 451, 516, 631) */
|
||||
export interface CatchUp {
|
||||
"arguments"?: Record<string, unknown>;
|
||||
"tool": string;
|
||||
}
|
||||
|
||||
/** one durable consumer of the kept data and the sequence it had acknowledged */
|
||||
export interface Consumer {
|
||||
"name": string;
|
||||
"sequence": number;
|
||||
"stream": string;
|
||||
}
|
||||
|
||||
/** the digest of one object of a point's bytes */
|
||||
export interface Digest {
|
||||
"digest": string;
|
||||
"name": string;
|
||||
}
|
||||
|
||||
/** a restore point: the kept data whole and its consumers' positions, in the recovery module's points directory (design 47 G 516, 560) */
|
||||
export interface Point {
|
||||
/** when it was taken, RFC 3339 */
|
||||
"at": string;
|
||||
"catch-up"?: CatchUp;
|
||||
"consumers"?: Consumer[];
|
||||
/** the digest of each object's bytes, checked before a restore (design 47 G 567) */
|
||||
"digests"?: Digest[];
|
||||
/** the fingerprint it was taken before */
|
||||
"fingerprint": string;
|
||||
/** its form, the number the holder raises when it changes how it writes it (0 when undeclared) */
|
||||
"form": number;
|
||||
/** the kept data, its local name in the module's manifest */
|
||||
"kept": string;
|
||||
/** the module */
|
||||
"module": string;
|
||||
/** its bytes */
|
||||
"size": number;
|
||||
/** the point's name, also its directory's */
|
||||
"snapshot": string;
|
||||
}
|
||||
|
||||
/** the act that runs, since when and what it waits on (design 47 G 517, 554) */
|
||||
export interface Running {
|
||||
"act": string;
|
||||
"since": string;
|
||||
"step"?: string;
|
||||
/** what it waits on, when it waits */
|
||||
"waits-on"?: string;
|
||||
}
|
||||
|
||||
/** is what snapshot-kept takes. */
|
||||
export interface SnapshotKeptInput {
|
||||
"catch-up"?: CatchUp;
|
||||
/** the fingerprint the restore point is taken before, or reverted from */
|
||||
"fingerprint": string;
|
||||
/** its form, the number the holder raises when it changes how it writes it (0 when undeclared) */
|
||||
"form": number;
|
||||
/** the kept data, its local name in the module's manifest */
|
||||
"kept": string;
|
||||
/** the module */
|
||||
"module": string;
|
||||
/** why, which the hand-act log keeps */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what snapshot-kept answers. */
|
||||
export interface SnapshotKeptAnswer {
|
||||
/** accepted: the act's id, which the ending event carries */
|
||||
"accepted"?: string;
|
||||
/** busy: the id of the act that runs; it keeps no queue, so the caller asks again on that act's ending event */
|
||||
"busy"?: string;
|
||||
/** why it was not taken; set alone */
|
||||
"error"?: string;
|
||||
}
|
||||
|
||||
/** is what restore-kept takes. */
|
||||
export interface RestoreKeptInput {
|
||||
/** the fingerprint the restore point is taken before, or reverted from */
|
||||
"fingerprint": string;
|
||||
/** its form, the number the holder raises when it changes how it writes it (0 when undeclared) */
|
||||
"form": number;
|
||||
/** ask the backup holder for the points directory when the point is missing there (design 47 G 516) */
|
||||
"from-backup"?: boolean;
|
||||
/** the kept data, its local name in the module's manifest */
|
||||
"kept": string;
|
||||
/** the module */
|
||||
"module": string;
|
||||
/** the restore point's name */
|
||||
"snapshot"?: string;
|
||||
/** why, which the hand-act log keeps */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what restore-kept answers. */
|
||||
export interface RestoreKeptAnswer {
|
||||
/** accepted: the act's id, which the ending event carries */
|
||||
"accepted"?: string;
|
||||
/** busy: the id of the act that runs; it keeps no queue, so the caller asks again on that act's ending event */
|
||||
"busy"?: string;
|
||||
/** why it was not taken; set alone */
|
||||
"error"?: string;
|
||||
}
|
||||
|
||||
/** is what kept-points takes. */
|
||||
export interface KeptPointsInput {
|
||||
/** one kept data (optional) */
|
||||
"kept"?: string;
|
||||
/** one module (optional) */
|
||||
"module"?: string;
|
||||
}
|
||||
|
||||
/** is what kept-points answers. */
|
||||
export interface KeptPointsAnswer {
|
||||
"asides": Aside[];
|
||||
"points": Point[];
|
||||
"running"?: Running;
|
||||
}
|
||||
|
||||
/** is what restore-aside takes. */
|
||||
export interface RestoreAsideInput {
|
||||
/** the aside's id */
|
||||
"id": string;
|
||||
/** the kept data, its local name in the module's manifest */
|
||||
"kept": string;
|
||||
/** the module */
|
||||
"module": string;
|
||||
/** why, which the hand-act log keeps */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what restore-aside answers. */
|
||||
export interface RestoreAsideAnswer {
|
||||
/** accepted: the act's id, which the ending event carries */
|
||||
"accepted"?: string;
|
||||
/** busy: the id of the act that runs; it keeps no queue, so the caller asks again on that act's ending event */
|
||||
"busy"?: string;
|
||||
/** why it was not taken; set alone */
|
||||
"error"?: string;
|
||||
}
|
||||
|
||||
/** is what cancel takes. */
|
||||
export interface CancelInput {
|
||||
/** the act's id */
|
||||
"act": string;
|
||||
/** why, which the hand-act log keeps */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what cancel answers. */
|
||||
export interface CancelAnswer {
|
||||
/** the act ended cancelled */
|
||||
"cancelled"?: boolean;
|
||||
/** why not, naming the step an act has begun; set alone */
|
||||
"error"?: string;
|
||||
}
|
||||
|
||||
/** is the body of the event ended. */
|
||||
export interface EndedEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
"catch-up"?: CatchUp;
|
||||
"consumers"?: Consumer[];
|
||||
"digests"?: Digest[];
|
||||
/** when failed */
|
||||
"error"?: string;
|
||||
"fingerprint"?: string;
|
||||
"form"?: number;
|
||||
"kept": string;
|
||||
"module": string;
|
||||
/** how the act ended */
|
||||
"outcome": "done" | "failed";
|
||||
"size"?: number;
|
||||
"snapshot"?: string;
|
||||
}
|
||||
|
||||
/** is the body of the event restore-started. */
|
||||
export interface RestoreStartedEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
"fingerprint"?: string;
|
||||
"form"?: number;
|
||||
"kept": string;
|
||||
"module": string;
|
||||
"snapshot"?: string;
|
||||
}
|
||||
|
||||
/** is the body of the event restored. */
|
||||
export interface RestoredEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
"aside"?: Aside;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
"error"?: string;
|
||||
"fingerprint"?: string;
|
||||
"form"?: number;
|
||||
"kept": string;
|
||||
/** the point is not in the recovery module's directory */
|
||||
"missing"?: boolean;
|
||||
"module": string;
|
||||
/** how the act ended */
|
||||
"outcome": "done" | "failed";
|
||||
"snapshot"?: string;
|
||||
}
|
||||
|
||||
/** is the body of the event set-aside. */
|
||||
export interface SetAsideEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
"id": string;
|
||||
"name": string;
|
||||
"size": number;
|
||||
}
|
||||
|
||||
/** is the body of the event aside-discarded. */
|
||||
export interface AsideDiscardedEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
"id": string;
|
||||
"name": string;
|
||||
"size": number;
|
||||
}
|
||||
|
||||
/** is the body of the event point-removed. */
|
||||
export interface PointRemovedEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
"fingerprint"?: string;
|
||||
"form"?: number;
|
||||
"kept": string;
|
||||
"module": string;
|
||||
"snapshot"?: string;
|
||||
}
|
||||
|
||||
/** is the body of the event expiry-ended. */
|
||||
export interface ExpiryEndedEvent {
|
||||
/** the id the act was answered accepted with, or a hand act's own id (design 47 G 560) */
|
||||
"act": string;
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
/** the number of points the run removed, 0 included */
|
||||
"removed": number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
// The contract of the seat "mesh-recovery", defined whole (seats/mesh-recovery/seat.json): Takes, sets aside and restores a module's kept data on the live bus, each act in the hand-act log (design 47 G 554). Its verbs answer at once and the result is the event.
|
||||
|
||||
export const seat = "mesh-recovery";
|
||||
|
||||
export const verbs = {
|
||||
snapshotKept: "snapshot-kept",
|
||||
restoreKept: "restore-kept",
|
||||
keptPoints: "kept-points",
|
||||
restoreAside: "restore-aside",
|
||||
cancel: "cancel",
|
||||
};
|
||||
|
||||
export const events = {
|
||||
ended: "ended",
|
||||
restoreStarted: "restore-started",
|
||||
restored: "restored",
|
||||
setAside: "set-aside",
|
||||
asideDiscarded: "aside-discarded",
|
||||
pointRemoved: "point-removed",
|
||||
expiryEnded: "expiry-ended",
|
||||
};
|
||||
|
||||
export const family = { name: "mesh-recovery.events", maxAgeDays: 7 };
|
||||
|
||||
export const endedStream = "SEAT_mesh-recovery_ended";
|
||||
|
||||
export function endedSubject(module, kept) {
|
||||
return "mesh.seat.mesh-recovery.event.ended." + module + "." + kept;
|
||||
}
|
||||
|
||||
export const endedSubjectAll = "mesh.seat.mesh-recovery.event.ended.*.*";
|
||||
|
||||
export const restoreStartedStream = "SEAT_mesh-recovery_restore-started";
|
||||
|
||||
export function restoreStartedSubject(module, kept) {
|
||||
return "mesh.seat.mesh-recovery.event.restore-started." + module + "." + kept;
|
||||
}
|
||||
|
||||
export const restoreStartedSubjectAll = "mesh.seat.mesh-recovery.event.restore-started.*.*";
|
||||
|
||||
export const restoredStream = "SEAT_mesh-recovery_restored";
|
||||
|
||||
export function restoredSubject(module, kept) {
|
||||
return "mesh.seat.mesh-recovery.event.restored." + module + "." + kept;
|
||||
}
|
||||
|
||||
export const restoredSubjectAll = "mesh.seat.mesh-recovery.event.restored.*.*";
|
||||
|
||||
export const setAsideStream = "SEAT_mesh-recovery_set-aside";
|
||||
|
||||
export function setAsideSubject(module, kept) {
|
||||
return "mesh.seat.mesh-recovery.event.set-aside." + module + "." + kept;
|
||||
}
|
||||
|
||||
export const setAsideSubjectAll = "mesh.seat.mesh-recovery.event.set-aside.*.*";
|
||||
|
||||
export const asideDiscardedStream = "SEAT_mesh-recovery_aside-discarded";
|
||||
|
||||
export function asideDiscardedSubject(module, kept) {
|
||||
return "mesh.seat.mesh-recovery.event.aside-discarded." + module + "." + kept;
|
||||
}
|
||||
|
||||
export const asideDiscardedSubjectAll = "mesh.seat.mesh-recovery.event.aside-discarded.*.*";
|
||||
|
||||
export const pointRemovedStream = "SEAT_mesh-recovery_point-removed";
|
||||
|
||||
export function pointRemovedSubject(module, kept) {
|
||||
return "mesh.seat.mesh-recovery.event.point-removed." + module + "." + kept;
|
||||
}
|
||||
|
||||
export const pointRemovedSubjectAll = "mesh.seat.mesh-recovery.event.point-removed.*.*";
|
||||
|
||||
export const expiryEndedStream = "SEAT_mesh-recovery_expiry-ended";
|
||||
|
||||
export const expiryEndedSubject = "mesh.seat.mesh-recovery.event.expiry-ended";
|
||||
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"lockfileVersion": 3,
|
||||
"name": "@novox/mesh-contracts-mesh-recovery",
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@novox/mesh-contracts-mesh-recovery",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
"requires": true,
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"description": "The contract of the seat mesh-recovery, generated from its definition.",
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./index.js",
|
||||
"types": "./index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"parts"
|
||||
],
|
||||
"name": "@novox/mesh-contracts-mesh-recovery",
|
||||
"type": "module",
|
||||
"types": "./index.d.ts",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
Vendored
+94
-20
@@ -18,6 +18,8 @@ export declare const verbs: {
|
||||
readonly manifestReads: "manifest-reads";
|
||||
readonly seatList: "seat-list";
|
||||
readonly seatListAgain: "seat-list-again";
|
||||
readonly firstViews: "first-views";
|
||||
readonly firstViewAgain: "first-view-again";
|
||||
readonly processBuilds: "process-builds";
|
||||
};
|
||||
|
||||
@@ -28,7 +30,8 @@ export declare const events: {
|
||||
readonly manifestReadsChanged: "manifest-reads-changed";
|
||||
readonly composed: "composed";
|
||||
readonly running: "running";
|
||||
readonly capabilitiesChanged: "capabilities-changed";
|
||||
readonly waitHeld: "wait-held";
|
||||
readonly waitEnded: "wait-ended";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "node-engine.events"; readonly maxAgeDays: 7 };
|
||||
@@ -51,9 +54,17 @@ export declare function runningSubject(node: string, module: string): string;
|
||||
|
||||
export declare const runningSubjectAll: "mesh.seat.node-engine.event.running.*.*";
|
||||
|
||||
export declare function capabilitiesChangedSubject(node: string): string;
|
||||
export declare const waitHeldStream: "SEAT_node-engine_wait-held";
|
||||
|
||||
export declare const capabilitiesChangedSubjectAll: "mesh.seat.node-engine.event.capabilities-changed.*";
|
||||
export declare function waitHeldSubject(node: string, module: string): string;
|
||||
|
||||
export declare const waitHeldSubjectAll: "mesh.seat.node-engine.event.wait-held.*.*";
|
||||
|
||||
export declare const waitEndedStream: "SEAT_node-engine_wait-ended";
|
||||
|
||||
export declare function waitEndedSubject(node: string, module: string): string;
|
||||
|
||||
export declare const waitEndedSubjectAll: "mesh.seat.node-engine.event.wait-ended.*.*";
|
||||
|
||||
export declare const states: {
|
||||
readonly have: "have";
|
||||
@@ -267,18 +278,6 @@ export interface CandidateRefusal {
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** a node's capabilities changed, which the assigner places by */
|
||||
export interface CapabilitiesChanged {
|
||||
/** when, RFC 3339 */
|
||||
"at": string;
|
||||
/** its capabilities now */
|
||||
"capabilities": string[];
|
||||
/** the node */
|
||||
"node": string;
|
||||
/** the layout's version */
|
||||
"v": number;
|
||||
}
|
||||
|
||||
/** an entry composed on a node (design 47 *What each step says*) */
|
||||
export interface Composed {
|
||||
/** when, RFC 3339 */
|
||||
@@ -575,19 +574,21 @@ export interface RootVerdict {
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** a module running healthy on a node at a build: what the node-engines of other nodes and the grants part decide a seat's pass from (ADR 0660 §2), kept last per subject with no age limit */
|
||||
/** a module's build running on a node, and its health: what the node-engines of other nodes and the grants part decide a seat's pass from (ADR 0660 §2), and the assigner reads a provider's and a canary's health and a put's end from (design 47 G 520); said again on the same subject each time healthy changes and for every module the have holds at the engine's start, kept last per subject with no age limit */
|
||||
export interface Running {
|
||||
/** when it was seen running healthy, RFC 3339 */
|
||||
/** when it was said, RFC 3339 */
|
||||
"at": string;
|
||||
/** the fingerprint of the build it runs; none with stopped */
|
||||
"fingerprint"?: string;
|
||||
/** the health of the build that runs, as the node-engine's have holds it from the module's declared check (ADR 0240); a running with none, said by a node-engine of before design 47 G, reads as healthy (design 47 G 520). SeatsToPassOn reads none of it: an unhealthy holder still holds its seat */
|
||||
"healthy"?: boolean | null;
|
||||
/** the digest of that build's manifest, the indexer's manifest of it, read by digest from the catalog half */
|
||||
"manifest"?: string;
|
||||
/** the module */
|
||||
"module": string;
|
||||
/** the node */
|
||||
"node": string;
|
||||
/** true when the module no longer runs on the node: no fingerprint or manifest then (design 47 G 365, 404); mesh-sdk's SeatsToPassOn counts such a module as not running there */
|
||||
/** true when the have no longer holds the module on the node, never for an unhealthy build: no fingerprint or manifest then (design 47 G 365, 404, 520); mesh-sdk's SeatsToPassOn counts such a module as not running there */
|
||||
"stopped"?: boolean;
|
||||
/** the layout's version */
|
||||
"v": number;
|
||||
@@ -669,6 +670,31 @@ export interface UserListWritten {
|
||||
"wanted": string;
|
||||
}
|
||||
|
||||
/** a hold or wait a node-engine says, the fields of the seat mesh-wants' wait (design 47 G 498, 512, 629); in the have it also carries ask-at and act (design 47 G 560) */
|
||||
export interface Wait {
|
||||
/** in the have: the act the recovery module answered accepted with (design 47 G 560) */
|
||||
"act"?: string;
|
||||
/** in the have: the time of the first ask of the recovery module for this wait (design 47 G 560) */
|
||||
"ask-at"?: string;
|
||||
/** a duration such as 30m */
|
||||
"bound"?: string;
|
||||
/** the time the bound counts from (design 47 G 266, 269, 498) */
|
||||
"clock-from"?: string;
|
||||
/** the node waited on: the build node for a kept build; the subject's <node> is the node that says it */
|
||||
"node"?: string;
|
||||
/** what it waits on */
|
||||
"on"?: string;
|
||||
/** the seat holding the wait: node-engine */
|
||||
"owner"?: string;
|
||||
/** why it holds, such as restore point missing, entry-missing, parts-differ, node away or build no longer kept (design 47 G 541, 499) */
|
||||
"reason"?: string;
|
||||
"since": string;
|
||||
/** compose: a hold of the composition (a restore point missing, an entry missing, parts that differ, a recovery that waits); kept-build: waiting for a kept build (design 47 G 499, 541) */
|
||||
"step": "compose" | "kept-build";
|
||||
/** the wait said in words */
|
||||
"words"?: string;
|
||||
}
|
||||
|
||||
/** is what hand-over takes. */
|
||||
export interface HandOverInput {
|
||||
/** the directory's absolute path, as the engine states it */
|
||||
@@ -991,6 +1017,50 @@ export interface SeatListAgainInput {
|
||||
/** is what seat-list-again answers. */
|
||||
export type SeatListAgainAnswer = SeatList;
|
||||
|
||||
/** is what first-views takes. */
|
||||
export interface FirstViewsInput {
|
||||
}
|
||||
|
||||
/** is what first-views answers. */
|
||||
export interface FirstViewsAnswer {
|
||||
/** one status per owner and state followed, as mesh-sdk's views runtime keeps it */
|
||||
"first-views": Record<string, unknown>[];
|
||||
}
|
||||
|
||||
/** is what first-view-again takes. */
|
||||
export interface FirstViewAgainInput {
|
||||
/** the owner module, as first-views names it */
|
||||
"owner": string;
|
||||
/** the owner's state, as first-views names it */
|
||||
"state": string;
|
||||
/** why it is asked again, in plain words */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** the hand ask written */
|
||||
export interface FirstViewAgainAnswerAsked {
|
||||
/** when it was asked, RFC 3339 */
|
||||
"at": string;
|
||||
/** the caller the runtime proved */
|
||||
"by": string;
|
||||
/** the owner module */
|
||||
"owner": string;
|
||||
/** the owner's state */
|
||||
"state": string;
|
||||
/** the layout's version */
|
||||
"v": number;
|
||||
/** why it was asked again */
|
||||
"why": string;
|
||||
}
|
||||
|
||||
/** is what first-view-again answers. */
|
||||
export interface FirstViewAgainAnswer {
|
||||
/** the hand ask written */
|
||||
"asked": FirstViewAgainAnswerAsked;
|
||||
/** the verb that reads the status, first-views */
|
||||
"read": string;
|
||||
}
|
||||
|
||||
/** is what process-builds takes. */
|
||||
export interface ProcessBuildsInput {
|
||||
/** only this module's processes */
|
||||
@@ -1018,8 +1088,11 @@ export type ComposedEvent = Composed;
|
||||
/** is the body of the event running. */
|
||||
export type RunningEvent = Running;
|
||||
|
||||
/** is the body of the event capabilities-changed. */
|
||||
export type CapabilitiesChangedEvent = CapabilitiesChanged;
|
||||
/** is the body of the event wait-held. */
|
||||
export type WaitHeldEvent = Wait;
|
||||
|
||||
/** is the body of the event wait-ended. */
|
||||
export type WaitEndedEvent = Wait;
|
||||
|
||||
/** is one value of the state have under the key <node>.<module>. */
|
||||
export interface HaveByNodeModuleValue {
|
||||
@@ -1047,6 +1120,7 @@ export interface HaveByNodeModuleValue {
|
||||
"sealed-at"?: Record<string, string>;
|
||||
"user-list"?: UserListWritten;
|
||||
"v": number;
|
||||
"wait"?: Wait;
|
||||
/** in phase waiting-yes, the digest of the composition that waits */
|
||||
"waits"?: string;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ export const verbs = {
|
||||
manifestReads: "manifest-reads",
|
||||
seatList: "seat-list",
|
||||
seatListAgain: "seat-list-again",
|
||||
firstViews: "first-views",
|
||||
firstViewAgain: "first-view-again",
|
||||
processBuilds: "process-builds",
|
||||
};
|
||||
|
||||
@@ -28,7 +30,8 @@ export const events = {
|
||||
manifestReadsChanged: "manifest-reads-changed",
|
||||
composed: "composed",
|
||||
running: "running",
|
||||
capabilitiesChanged: "capabilities-changed",
|
||||
waitHeld: "wait-held",
|
||||
waitEnded: "wait-ended",
|
||||
};
|
||||
|
||||
export const family = { name: "node-engine.events", maxAgeDays: 7 };
|
||||
@@ -63,11 +66,21 @@ export function runningSubject(node, module) {
|
||||
|
||||
export const runningSubjectAll = "mesh.seat.node-engine.event.running.*.*";
|
||||
|
||||
export function capabilitiesChangedSubject(node) {
|
||||
return "mesh.seat.node-engine.event.capabilities-changed." + node;
|
||||
export const waitHeldStream = "SEAT_node-engine_wait-held";
|
||||
|
||||
export function waitHeldSubject(node, module) {
|
||||
return "mesh.seat.node-engine.event.wait-held." + node + "." + module;
|
||||
}
|
||||
|
||||
export const capabilitiesChangedSubjectAll = "mesh.seat.node-engine.event.capabilities-changed.*";
|
||||
export const waitHeldSubjectAll = "mesh.seat.node-engine.event.wait-held.*.*";
|
||||
|
||||
export const waitEndedStream = "SEAT_node-engine_wait-ended";
|
||||
|
||||
export function waitEndedSubject(node, module) {
|
||||
return "mesh.seat.node-engine.event.wait-ended." + node + "." + module;
|
||||
}
|
||||
|
||||
export const waitEndedSubjectAll = "mesh.seat.node-engine.event.wait-ended.*.*";
|
||||
|
||||
export const states = {
|
||||
have: "have",
|
||||
|
||||
Reference in New Issue
Block a user