From 6b6cdcc25eb492a675c7fcd61d4c2503eb13a11d Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 12 Nov 2026 06:07:17 +0100 Subject: [PATCH] RN17 (fill): a bundle reads a state's fill through KeptState.Info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bundle's state call mesh/state.info is served by the tool runner (node-tools launch.go:425, runtime.go:1093); the module library had no wrapper for it. Builds: design 47 G §623, §366, A-214 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- go/state.go | 32 ++++++++++++++++++++++++++++++++ go/state_history_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/go/state.go b/go/state.go index d5cb4120..43b7c30d 100644 --- a/go/state.go +++ b/go/state.go @@ -126,6 +126,38 @@ func (s KeptState) Page(after string, maxBytes int, budget time.Duration) (State return p, json.Unmarshal(raw, &p) } +// StateInfo is how full a state's bucket is against its caps, as the tool runner answers `mesh/state.info` +// (design 47 G §623): a bundle's tools say fill against cap from it. A cap of -1 is none. +type StateInfo struct { + State string `json:"state"` + Bucket string `json:"bucket"` + // Bytes is what the bucket holds now; Keys how many keys it holds. + Bytes uint64 `json:"bytes"` + Keys uint64 `json:"keys"` + // MaxBytes and MaxValueBytes are the bucket's caps. + MaxBytes int64 `json:"max_bytes"` + MaxValueBytes int32 `json:"max_value_bytes"` + // History is how many values a key keeps; TTLSeconds the bucket's age for a value (0 none); KeyTTL whether a key + // may carry its own time to live. + History int64 `json:"history"` + TTLSeconds int64 `json:"ttl_seconds"` + KeyTTL bool `json:"key_ttl"` +} + +// Info is the bucket's fill against its caps: runtime verb `mesh/state.info`. A read, like Get: the owner's +// instances and its readers may ask it, and it writes nothing. +func (s KeptState) Info() (*StateInfo, error) { + raw, err := s.ask("info", map[string]any{}) + if err != nil { + return nil, err + } + var i StateInfo + if err := json.Unmarshal(raw, &i); err != nil { + return nil, err + } + return &i, nil +} + // History is every value the bucket still keeps for key, oldest first — as many as the state declared it // keeps, deletes included — and empty for a key it holds nothing for (novox/hq ADR 0307). A read, like Get: // the owner's instances and its readers may ask it, and it writes nothing. diff --git a/go/state_history_test.go b/go/state_history_test.go index 8f2f0a5e..d490531d 100644 --- a/go/state_history_test.go +++ b/go/state_history_test.go @@ -141,3 +141,37 @@ func TestHistoryIsEveryKeptValueOldestFirst(t *testing.T) { t.Fatalf("a state not issued answered %v", refused) } } + +// Info asks mesh/state.info with the state alone and hands back the bucket's fill against its caps, as the tool +// runner answers it (design 47 G §623); a refusal is the runtime's error. +func TestInfoIsTheBucketsFillAgainstItsCaps(t *testing.T) { + var got *StateInfo + var refused error + asked := historying(t, func(m message) map[string]any { + var p map[string]any + _ = json.Unmarshal(m.Params, &p) + if p["state"] != "issues" { + return map[string]any{"error": map[string]any{"code": -32000, "message": "issues was issued no state"}} + } + return map[string]any{"result": map[string]any{"state": "issues", "bucket": "issues_issues", "bytes": 300, "keys": 4, + "max_bytes": 1000, "max_value_bytes": 262144, "history": 1, "ttl_seconds": 0, "key_ttl": true}} + }, func() { + var err error + if got, err = State("issues").Info(); err != nil { + t.Errorf("info: %v", err) + } + var i *StateInfo + if i, refused = State("elsewhere.issues").Info(); i != nil { + t.Errorf("a refused info handed back %v", i) + } + }) + if len(asked) != 2 || asked[0].Method != "mesh/state.info" { + t.Fatalf("asked %+v", asked) + } + if got == nil || got.Bytes != 300 || got.MaxBytes != 1000 || got.Keys != 4 || got.Bucket != "issues_issues" || !got.KeyTTL { + t.Errorf("info is %+v", got) + } + if refused == nil { + t.Error("an info of a state the module was not issued is not refused") + } +}