From 0b25bce23954e2e02c3a4255e128912508f4f128 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 12:50:41 +0100 Subject: [PATCH] =?UTF-8?q?ADR=200626=20=C2=A71:=20a=20process's=20bus=20m?= =?UTF-8?q?ay=20say=20keeps,=20for=20the=20bus=20module's=20keeper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A2b's judge test waits on it: the strict decoder refused the key, so the bus module's new manifest did not decode. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- go/catalogue/process_bus.go | 6 +++++- go/catalogue/stream_work_queue_test.go | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/go/catalogue/process_bus.go b/go/catalogue/process_bus.go index b5049ca..b83c2e8 100644 --- a/go/catalogue/process_bus.go +++ b/go/catalogue/process_bus.go @@ -31,6 +31,10 @@ type ProcessBus struct { // Removes are what a tools bundle's account may remove from the bus, from a closed set (BusRemovals; novox/hq ADR // 0564 Limits 4): declared alone, by the module holding mesh-broker, never by a process. Removes []string `json:"removes,omitempty"` + // Keeps says this process is the bus module's keeper (novox/hq ADR 0626 §1): the controller composes the bus's + // create, update and snapshot subjects for modules' entries into its account. Refused on any other process or + // module by the judge (ADR 0626 §3); read here so a manifest declaring it decodes. + Keeps bool `json:"keeps,omitempty"` } // What a bus account may remove (novox/hq ADR 0564 Limits 4), each its own grant: keys of the node-engine's acts @@ -93,7 +97,7 @@ func (b *ProcessBus) UnmarshalJSON(raw []byte) error { dec := json.NewDecoder(bytes.NewReader(raw)) dec.DisallowUnknownFields() if err := dec.Decode(&p); err != nil { - return fmt.Errorf("a process's bus is {emits, queues, state, asks, invokes}: %w", typedUnknown(err)) + return fmt.Errorf("a process's bus is {emits, queues, state, asks, invokes, keeps}: %w", typedUnknown(err)) } *b = ProcessBus(p) return nil diff --git a/go/catalogue/stream_work_queue_test.go b/go/catalogue/stream_work_queue_test.go index d266c96..a68ff91 100644 --- a/go/catalogue/stream_work_queue_test.go +++ b/go/catalogue/stream_work_queue_test.go @@ -28,6 +28,15 @@ func TestAWorkQueueStreamIsDeclaredAndAccepted(t *testing.T) { s.Days() != 1 || s.Cap() != 64 { t.Fatalf("the stream read as %+v", s) } + // ADR 0626 §1: the bus module's keeper declares keeps beside the rest of its bus, and it reads back. + k, err := decodeForTest([]byte(`{"module":"nats","version":"1","resources":[{"id":"keeper","type":"process",` + + `"run":["./keeper"],"bus":{"keeps":true,"state":["requests"]}}]}`)) + if err != nil { + t.Fatalf("a keeper's bus was refused: %v", err) + } + if b, ok := k.ProcessBuses()["keeper"]; !ok || !b.Keeps { + t.Fatalf("the keeper's bus read as %+v", b) + } } // 1177/AC3: a stream with no retention composes as main does — limits retention, dropping its oldest — proven