A kept data's form below 0 is refused where it is read, once for every kept kind
mesh/delivery merged: source.main moved to novox/mesh-sdk@366c4ab5073d
mesh/delivery-group group fix/manifest-form-reader merged: every member is merged
mesh/check checking 9582e01eef5f on 366c4ab5073d | novox/mesh-sdk#180 in group fix/manifest-form-reader

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-09 21:19:56 +01:00
parent 841e08070f
commit 9582e01eef
7 changed files with 43 additions and 0 deletions
+3
View File
@@ -179,6 +179,9 @@ func readBusStateEntry(raw json.RawMessage) (BusStateEntry, []string, error) {
var e BusStateEntry
err := strict(&e)
if err == nil {
if err := e.KeptLayout.refuse(fmt.Sprintf("requires entry %q", e.Name)); err != nil {
return BusStateEntry{}, nil, err
}
return e, aside, nil
}
unknown := asUnknownField(err)
+11
View File
@@ -1,6 +1,7 @@
package catalogue
import (
"fmt"
"sort"
)
@@ -21,6 +22,16 @@ type KeptLayout struct {
CatchUp string `json:"catch-up,omitempty"`
}
// refuse is the one reading of a layout's form every kept kind's decoder calls: a form is a whole number from 0, and one
// below it is refused here, naming the declaration (`what` is its kind and name, such as `state "seen"`).
func (k KeptLayout) refuse(what string) error {
if k.Form < 0 {
return fmt.Errorf("%s: its form is %d; a form is a whole number from 0, which its holder raises when it changes how "+
"it writes it", what, k.Form)
}
return nil
}
// The kinds of kept data, by the template each is on (ADR 0595 §2): the five a restore point is taken of.
const (
KeptKindState = "state"
+17
View File
@@ -2,6 +2,7 @@ package catalogue
import (
"reflect"
"strings"
"testing"
)
@@ -70,3 +71,19 @@ func TestAKeptDataSaysItsFormAndItsCatchUp(t *testing.T) {
t.Errorf("a manifest applied over itself changes %+v", got)
}
}
// A form below 0 is refused where it is read, in every kept kind's declaration, naming it.
func TestAKeptDataFormBelowZeroIsRefused(t *testing.T) {
for _, c := range []struct{ name, raw, says string }{
{"state", `"state":[{"name":"seen","form":-3}]`, `state "seen": its form is -3`},
{"log", `"logs":[{"name":"acts","form":-1}]`, `log "acts": its form is -1`},
{"stream", `"streams":[{"name":"forge","events":["merged"],"form":-2}]`, `stream "forge": its form is -2`},
{"objects", `"objects":[{"name":"revisions","max-mib":64,"form":-1}]`, `object bucket "revisions": its form is -1`},
{"requires", `"requires":[{"name":"holds","storage":"key-value","template":"state","form":-1}]`, `requires entry "holds": its form is -1`},
} {
_, err := decodeForTest([]byte(`{"module":"x","version":"1",` + c.raw + `}`))
if err == nil || !strings.Contains(err.Error(), c.says) {
t.Errorf("%s: read as %v, want a refusal holding %q", c.name, err, c.says)
}
}
}
+3
View File
@@ -55,6 +55,9 @@ func (l *LogDeclaration) UnmarshalJSON(raw []byte) error {
if err := dec.Decode(&full); err != nil {
return fmt.Errorf("a log is either a name or {name, max-mib, max-age-days, form, catch-up}: %w", typedUnknown(err))
}
if err := full.KeptLayout.refuse(fmt.Sprintf("log %q", full.Name)); err != nil {
return err
}
l.Name = full.Name
l.KeptLayout = full.KeptLayout
l.MaxMiB = 0
+3
View File
@@ -49,6 +49,9 @@ func (o *ObjectsDeclaration) UnmarshalJSON(raw []byte) error {
if err := dec.Decode(&p); err != nil {
return fmt.Errorf("an object bucket is {name, max-mib, grant, form, catch-up}: %w", typedUnknown(err))
}
if err := p.KeptLayout.refuse(fmt.Sprintf("object bucket %q", p.Name)); err != nil {
return err
}
*o = ObjectsDeclaration(p)
return nil
}
+3
View File
@@ -52,6 +52,9 @@ func (s *StateDeclaration) UnmarshalJSON(raw []byte) error {
if err := dec.Decode(&full); err != nil {
return fmt.Errorf("a state is either a name or {name, history, ttl-seconds, per-machine, key-ttl, read-only, form, catch-up}: %w", typedUnknown(err))
}
if err := full.KeptLayout.refuse(fmt.Sprintf("state %q", full.Name)); err != nil {
return err
}
*s = StateDeclaration(full)
return nil
}
+3
View File
@@ -88,6 +88,9 @@ func (s *StreamDeclaration) UnmarshalJSON(raw []byte) error {
return fmt.Errorf("a stream is {name, events, max-age-days, max-mib, grant, retention, max-deliveries, "+
"duplicates, form, catch-up}: %w", typedUnknown(err))
}
if err := p.KeptLayout.refuse(fmt.Sprintf("stream %q", p.Name)); err != nil {
return err
}
*s = StreamDeclaration(p)
return nil
}