Fixtures captured from the holders' own read tools for the states that had none (conditions, findings, presence, stuck, would, resolved, asks, packages); the packages item requires absent (ADR 0634 §6; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -25,6 +25,18 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v RetiringHolderEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.stream\\.[a-z0-9-]+\\.event\\.retiring$")},
|
||||
{name: "retired", subject: "mesh.stream.mesh-config.event.retired", given: "{\"ask\":\"a1\",\"module\":\"messenger\",\"name\":\"old-key\",\"node\":\"ace\"}", wire: "{\"ask\":\"a1\",\"module\":\"messenger\",\"name\":\"old-key\",\"node\":\"ace\"}",
|
||||
decode: func(b []byte) (any, error) { var v RetiredHolderEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.stream\\.[a-z0-9-]+\\.event\\.retired$")},
|
||||
{name: "a module's value that falls back to a predefined key (captured)", subject: "", given: "{\"key\":\"font-size\",\"module\":\"dunst\",\"node\":\"g14\",\"predefined\":\"font-size\",\"source\":\"module\",\"value\":13}", wire: "{\"key\":\"font-size\",\"module\":\"dunst\",\"node\":\"g14\",\"predefined\":\"font-size\",\"source\":\"module\",\"value\":13}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v ResolvedByNodeModuleKeyValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a module's own value (captured)", subject: "", given: "{\"key\":\"width\",\"module\":\"dunst\",\"node\":\"g14\",\"source\":\"module\",\"value\":500}", wire: "{\"key\":\"width\",\"module\":\"dunst\",\"node\":\"g14\",\"source\":\"module\",\"value\":500}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v ResolvedByNodeModuleKeyValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "renames answers the open windows", subject: "", given: "{\"renames\":[{\"at\":\"2026-11-04T12:00:00Z\",\"from\":\"old-key\",\"module\":\"messenger\",\"open\":[\"g14\"],\"to\":\"new-key\"}]}", wire: "{\"renames\":[{\"at\":\"2026-11-04T12:00:00Z\",\"from\":\"old-key\",\"module\":\"messenger\",\"open\":[\"g14\"],\"to\":\"new-key\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v RenamesAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
|
||||
@@ -978,9 +978,9 @@ type DataByNodeModuleValue struct {
|
||||
|
||||
// PackagesByNodeValuePackagesItem is a type of the contract.
|
||||
type PackagesByNodeValuePackagesItem struct {
|
||||
Absent bool `json:"absent,omitempty"`
|
||||
Modules []string `json:"modules,omitempty"`
|
||||
Package string `json:"package,omitempty"`
|
||||
Absent bool `json:"absent"`
|
||||
Modules []string `json:"modules"`
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// PackagesByNodeValue is one value of the state packages under the key <node>.
|
||||
|
||||
@@ -37,6 +37,8 @@ func TestFixtures(t *testing.T) {
|
||||
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.manifest-reads-changed\\.[A-Za-z0-9_-]+$")},
|
||||
{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\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v PackagesByNodeValue; 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}",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshconditions
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-conditions/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: "a condition raised from a finding, as the keeper holds it (captured; empty fields left out)", subject: "", given: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator; the details name it and the line to type. A directory of mesh-build-node was already on ace, with another owner or mode than mesh-build-node declares.\",\"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\",\"last-observed\":\"2026-10-26T20:19:41.474823121Z\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.076843727Z\",\"resolved\":\"mesh-build-node's directory on ace is the mesh's\",\"resolver\":\"self\",\"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: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}", wire: "{\"count\":1,\"evidence\":[{\"at\":\"2026-10-26T20:19:41.474823121Z\",\"said\":\"mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}],\"explanation\":\"Needs you: hand the directory over from the control-node, as the operator; the details name it and the line to type. A directory of mesh-build-node was already on ace, with another owner or mode than mesh-build-node declares.\",\"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\",\"last-observed\":\"2026-10-26T20:19:41.474823121Z\",\"needs\":\"hand the directory over from the control-node, as the operator; the details name it and the line to type.\",\"observations\":1,\"raised\":\"2026-10-31T08:19:57.076843727Z\",\"resolved\":\"mesh-build-node's directory on ace is the mesh's\",\"resolver\":\"self\",\"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: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it — `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh\"}",
|
||||
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},
|
||||
}
|
||||
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,48 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshhealers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-healers/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: "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},
|
||||
}
|
||||
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,56 @@
|
||||
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
|
||||
|
||||
package meshwatchdogs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestFixtures reads every fixture of seats/mesh-watchdogs/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: "a node's agent online (captured)", subject: "", given: "{\"online\":true,\"v\":1}", wire: "{\"online\":true,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v PresenceByNodeValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a delivery part's instance online (captured)", subject: "", given: "{\"online\":true,\"v\":1}", wire: "{\"online\":true,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v PresenceByPartPartInstanceValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a thing that has not moved since a time, with what it waited for (captured)", subject: "", given: "{\"bucket\":\"mesh-delivery-watchman_presence\",\"key\":\"part.mesh-delivery-builder.novox\",\"since\":\"2026-10-25T11:52:58.051397201+01:00\",\"v\":1,\"waited\":\"mesh-delivery-builder on novox to come back online\"}", wire: "{\"bucket\":\"mesh-delivery-watchman_presence\",\"key\":\"part.mesh-delivery-builder.novox\",\"since\":\"2026-10-25T11:52:58.051397201+01:00\",\"v\":1,\"waited\":\"mesh-delivery-builder on novox to come back online\"}",
|
||||
decode: func(b []byte) (any, error) { var v StuckByBucketKeyValue; 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,117 @@
|
||||
[
|
||||
{
|
||||
"name": "a condition raised from a finding, as the keeper holds it (captured; empty fields left out)",
|
||||
"of": "state:conditions/<key>",
|
||||
"given": {
|
||||
"count": 1,
|
||||
"evidence": [
|
||||
{
|
||||
"at": "2026-10-26T20:19:41.474823121Z",
|
||||
"said": "mesh-build-node on ace uses mesh-build-node.cache as found: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it \u2014 `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh"
|
||||
}
|
||||
],
|
||||
"explanation": "Needs you: hand the directory over from the control-node, as the operator; the details name it and the line to type. A directory of mesh-build-node was already on ace, with another owner or mode than mesh-build-node declares.",
|
||||
"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",
|
||||
"last-observed": "2026-10-26T20:19:41.474823121Z",
|
||||
"needs": "hand the directory over from the control-node, as the operator; the details name it and the line to type.",
|
||||
"observations": 1,
|
||||
"raised": "2026-10-31T08:19:57.076843727Z",
|
||||
"resolved": "mesh-build-node's directory on ace is the mesh's",
|
||||
"resolver": "self",
|
||||
"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: used as found: owned by 10005:10005, mode 700, as found; mesh-build-node, mode 0711 was declared and not given it \u2014 `ace/node-engine.hand-over` with the directory's path, then your yes on your channel, with its path, hands it to the mesh"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a condition with an empty evidence trail of headline (captured; empty fields left out)",
|
||||
"of": "state:conditions/<key>",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a finding of the bus scope with its zero time (captured from the watchman; empty fields left out)",
|
||||
"of": "state:findings/<key>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"key": "bus.EVENTS.controller.max-deliveries",
|
||||
"kind": "max-deliveries",
|
||||
"subject": {
|
||||
"id": "EVENTS.controller",
|
||||
"scope": "bus"
|
||||
},
|
||||
"severity": "warning",
|
||||
"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",
|
||||
"needs": "deliver them again or drop them, from the mesh MCP server.",
|
||||
"observer": "mesh-delivery-watchman",
|
||||
"source": "S9",
|
||||
"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)"
|
||||
}
|
||||
],
|
||||
"at": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a finding of the module scope with its machine (captured from the watchman; empty fields left out)",
|
||||
"of": "state:findings/<key>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"key": "module.mesh-build-node.ace.directory-used-as-found",
|
||||
"kind": "directory-used-as-found",
|
||||
"subject": {
|
||||
"id": "mesh-build-node.ace",
|
||||
"machine": "ace",
|
||||
"scope": "module"
|
||||
},
|
||||
"severity": "warning",
|
||||
"summary": "mesh-build-node on ace uses mesh-build-node.cache 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",
|
||||
"needs": "hand the directory over from the control-node, as the operator; the details name it and the line to type.",
|
||||
"resolved": "mesh-build-node's directory on ace is the mesh's",
|
||||
"observer": "mesh-delivery-watchman",
|
||||
"source": "health-report",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"at": "2026-10-26T20:19:41.474823121Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"name": "an act a healer would have made, kept from the controller's time (captured)",
|
||||
"of": "state:would/<would>",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,29 @@
|
||||
[
|
||||
{
|
||||
"name": "a node's agent online (captured)",
|
||||
"of": "state:presence/<node>",
|
||||
"given": {
|
||||
"online": true,
|
||||
"v": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a delivery part's instance online (captured)",
|
||||
"of": "state:presence/part.<part>.<instance>",
|
||||
"given": {
|
||||
"online": true,
|
||||
"v": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a thing that has not moved since a time, with what it waited for (captured)",
|
||||
"of": "state:stuck/<bucket>.<key>",
|
||||
"given": {
|
||||
"bucket": "mesh-delivery-watchman_presence",
|
||||
"key": "part.mesh-delivery-builder.novox",
|
||||
"since": "2026-10-25T11:52:58.051397201+01:00",
|
||||
"v": 1,
|
||||
"waited": "mesh-delivery-builder on novox to come back online"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -31,6 +31,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v CancelAccept; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.operator-channel\\.accept\\.cancel\\.[A-Za-z0-9_-]+$")},
|
||||
{name: "a condition by its publisher", subject: "mesh.seat.operator-channel.accept.condition.mesh-watcher", given: "{\"key\":\"backup.ace.failed\",\"severity\":\"warning\",\"state\":\"raised\",\"subject\":{\"id\":\"ace\",\"scope\":\"node\"},\"v\":1}", wire: "{\"key\":\"backup.ace.failed\",\"severity\":\"warning\",\"state\":\"raised\",\"subject\":{\"id\":\"ace\",\"scope\":\"node\"},\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v ConditionAccept; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.operator-channel\\.accept\\.condition\\.[A-Za-z0-9_-]+$")},
|
||||
{name: "an ask chosen on a channel, as the router's tool states it (captured)", subject: "", given: "{\"answer\":\"the operator, via telegram (user id verified), chose Approve\",\"asker\":\"mesh-knowledge\",\"asker_told\":true,\"expires\":\"2026-11-06T12:17:43Z\",\"headline\":\"Parts wait for the seat list before acting\",\"key\":\"mesh-knowledge.kb-decision-636-1bcf1a2c\",\"offered_on\":[\"telegram\"],\"opened\":\"2026-11-05T12:27:44Z\",\"state\":\"chosen\"}", wire: "{\"answer\":\"the operator, via telegram (user id verified), chose Approve\",\"asker\":\"mesh-knowledge\",\"asker_told\":true,\"expires\":\"2026-11-06T12:17:43Z\",\"headline\":\"Parts wait for the seat list before acting\",\"key\":\"mesh-knowledge.kb-decision-636-1bcf1a2c\",\"offered_on\":[\"telegram\"],\"opened\":\"2026-11-05T12:27:44Z\",\"state\":\"chosen\"}",
|
||||
decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "an ask its asker took back (captured)", subject: "", given: "{\"answer\":\"cancelled: its asker took it back\",\"asker\":\"mesh-delivery-teller\",\"asker_told\":true,\"expires\":\"2026-11-06T10:04:01Z\",\"headline\":\"Check failed - merge into main\",\"key\":\"mesh-delivery-teller.merge-feat_registry_milestone-3a038b9f8b4d-4-4da6ee3cd9d5\",\"offered_on\":[\"telegram\"],\"opened\":\"2026-11-05T10:14:04Z\",\"state\":\"cancelled\"}", wire: "{\"answer\":\"cancelled: its asker took it back\",\"asker\":\"mesh-delivery-teller\",\"asker_told\":true,\"expires\":\"2026-11-06T10:04:01Z\",\"headline\":\"Check failed - merge into main\",\"key\":\"mesh-delivery-teller.merge-feat_registry_milestone-3a038b9f8b4d-4-4da6ee3cd9d5\",\"offered_on\":[\"telegram\"],\"opened\":\"2026-11-05T10:14:04Z\",\"state\":\"cancelled\"}",
|
||||
decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
[
|
||||
{
|
||||
"name": "an ask chosen on a channel, as the router's tool states it (captured)",
|
||||
"of": "state:asks/<asker>.<id>",
|
||||
"given": {
|
||||
"answer": "the operator, via telegram (user id verified), chose Approve",
|
||||
"asker": "mesh-knowledge",
|
||||
"asker_told": true,
|
||||
"expires": "2026-11-06T12:17:43Z",
|
||||
"headline": "Parts wait for the seat list before acting",
|
||||
"key": "mesh-knowledge.kb-decision-636-1bcf1a2c",
|
||||
"offered_on": [
|
||||
"telegram"
|
||||
],
|
||||
"opened": "2026-11-05T12:27:44Z",
|
||||
"state": "chosen"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an ask its asker took back (captured)",
|
||||
"of": "state:asks/<asker>.<id>",
|
||||
"given": {
|
||||
"answer": "cancelled: its asker took it back",
|
||||
"asker": "mesh-delivery-teller",
|
||||
"asker_told": true,
|
||||
"expires": "2026-11-06T10:04:01Z",
|
||||
"headline": "Check failed - merge into main",
|
||||
"key": "mesh-delivery-teller.merge-feat_registry_milestone-3a038b9f8b4d-4-4da6ee3cd9d5",
|
||||
"offered_on": [
|
||||
"telegram"
|
||||
],
|
||||
"opened": "2026-11-05T10:14:04Z",
|
||||
"state": "cancelled"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,25 @@
|
||||
[
|
||||
{
|
||||
"name": "a module's value that falls back to a predefined key (captured)",
|
||||
"of": "state:resolved/<node>.<module>.<key>",
|
||||
"given": {
|
||||
"node": "g14",
|
||||
"module": "dunst",
|
||||
"key": "font-size",
|
||||
"predefined": "font-size",
|
||||
"source": "module",
|
||||
"value": 13
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a module's own value (captured)",
|
||||
"of": "state:resolved/<node>.<module>.<key>",
|
||||
"given": {
|
||||
"node": "g14",
|
||||
"module": "dunst",
|
||||
"key": "width",
|
||||
"source": "module",
|
||||
"value": 500
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -6,12 +6,47 @@
|
||||
"v": 1,
|
||||
"key": "machine.ace.seat-list-unread",
|
||||
"kind": "seat-list-unread",
|
||||
"subject": {"scope": "machine", "id": "ace", "machine": "ace"},
|
||||
"subject": {
|
||||
"scope": "machine",
|
||||
"id": "ace",
|
||||
"machine": "ace"
|
||||
},
|
||||
"severity": "warning",
|
||||
"summary": "the seat list has not been read for five minutes",
|
||||
"source": "node-engine.seat-list",
|
||||
"observer": "mesh-host",
|
||||
"at": "2026-11-05T14:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's packages, present and absent (captured from ace, shortened)",
|
||||
"of": "state:packages/<node>",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1012,7 +1012,12 @@
|
||||
"absent": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"modules",
|
||||
"absent"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -782,9 +782,9 @@ export interface DataByNodeModuleValue {
|
||||
}
|
||||
|
||||
export interface PackagesByNodeValuePackagesItem {
|
||||
"absent"?: boolean;
|
||||
"modules"?: string[];
|
||||
"package"?: string;
|
||||
"absent": boolean;
|
||||
"modules": string[];
|
||||
"package": string;
|
||||
}
|
||||
|
||||
/** is one value of the state packages under the key <node>. */
|
||||
|
||||
Reference in New Issue
Block a user