AM-S3, WirePattern: a per-machine state reaches its machine's keys below it; buscompose exports WirePattern

The per-machine form gains the keys below the machine's own, <node>.>, for every
per-machine state and a membership's too (PerMachineKeys), every module's key keeping
its form; buscompose exports WirePattern, which a reader uses to turn a stream's events
into the bus's subjects (the tool runner's marks).

Builds: design 46 7-approval-mark §13, §5

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 04:59:15 +01:00
parent 18c9ae04a0
commit bc5d6c03ea
5 changed files with 35 additions and 5 deletions
+14
View File
@@ -3,6 +3,7 @@ package buscompose
import (
"fmt"
"sort"
"strings"
)
// moduleStreamPrefix starts every subject a module's own stream takes.
@@ -14,6 +15,19 @@ func ModuleStreamSubject(module, event string) string {
return moduleStreamPrefix + module + ".event." + event
}
// WirePattern is a stream's local event pattern as the bus spells it: `**` is `>`, `*` stays `*`. A reader turns a
// stream's events into the bus's subjects with it (design 46 7-approval-mark §5): the grants part composing a stream, and
// the tool runner deriving a restricted stream's marked subjects.
func WirePattern(local string) string {
parts := strings.Split(local, ".")
for i, p := range parts {
if p == "**" {
parts[i] = ">"
}
}
return strings.Join(parts, ".")
}
// A ModuleStream is one module's declared stream as the bus holds it.
type ModuleStream struct {
Module string
+10
View File
@@ -33,6 +33,16 @@ func TestTheBusNamesAgree(t *testing.T) {
if (Bucket{}).MaxBytes() != -1 || (Bucket{MaxMiB: 2}).MaxBytes() != 2<<20 {
t.Fatal("a bucket's cap is not its max-mib in bytes")
}
// Design 46 7-approval-mark §5: a stream's local event pattern as the bus spells it.
for local, wire := range map[string]string{"mail.**": "mail.>", "mail.*.message": "mail.*.message", "sent": "sent"} {
if got := WirePattern(local); got != wire {
t.Errorf("WirePattern(%q) = %q, want %q", local, got, wire)
}
}
// Design 46 7-approval-mark §13: a per-machine state reaches its machine's key and the keys below it, no other machine's.
if got := PerMachineKeys("ace"); len(got) != 2 || got[0] != "ace" || got[1] != "ace.>" {
t.Errorf("a per-machine state reaches %v on ace", got)
}
// A seat's family is the shared events stream filtered to its subjects: a definition says the stream's age.
for _, st := range MeshStreams() {
if st.Name == EventsStream && int64(st.MaxAge) != seatdef.SharedEventsMaxAgeDays*24*60*60 {
+2 -2
View File
@@ -163,8 +163,8 @@ type Principal struct {
// instances and read by whoever declares it.
State []string
Reads []string
// PerMachine is the local names of its state keyed by machine, reaching the key named for Node and
// no other; KeyedReads the keys of others' state it reads one key at a time (novox/hq ADR 0260).
// PerMachine is the local names of its state keyed by machine, reaching the keys of Node (PerMachineKeys) and
// no other machine's; KeyedReads the keys of others' state it reads one key at a time (novox/hq ADR 0260).
PerMachine []string
KeyedReads []KeyedRead
// Logs is the local names of the logs this principal's module keeps (novox/hq ADR 0297).
+7 -2
View File
@@ -18,8 +18,8 @@ type Bucket struct {
History int
// TTLSeconds is how long a value lives; zero is until replaced or deleted.
TTLSeconds int
// PerMachine says each machine's instance writes only the key named for its machine (novox/hq ADR
// 0260), and is granted that key and no other.
// PerMachine says each machine's instance writes only the keys of its machine (novox/hq ADR 0260; design 46
// 7-approval-mark §13): the key named for its machine and the keys below it, PerMachineKeys, and no other machine's.
PerMachine bool
// KeyTTL says the bucket allows a time to live per key (novox/hq ADR 0433 §1): its owner writes each key with its
// own, and the server writes a delete marker, kept KeyTTLMarkerTTL, when it ends.
@@ -36,6 +36,11 @@ func (b Bucket) MaxBytes() int64 {
return int64(b.MaxMiB) * 1024 * 1024
}
// PerMachineKeys are the keys a machine's instance of a per-machine state reaches (design 46 7-approval-mark §13): the key
// named for its machine, as every module's per-machine key keeps its form, and the keys below it, `<node>.>`; no other
// machine's. A membership's per-machine state (membership.StateIssued's Key) reaches the same.
func PerMachineKeys(node string) []string { return []string{node, node + ".>"} }
// BucketName is the bucket a module's state lives in: the module and the local name joined by an
// underscore, which neither may contain, so two modules can never derive one bucket.
func BucketName(module, name string) string { return module + "_" + name }
+2 -1
View File
@@ -112,7 +112,8 @@ type SeatServed struct {
}
// StateIssued is one bucket an assignment may reach, by the name its module uses for it (novox/hq ADR 0201).
// Key, when set, is the one key it may reach (novox/hq ADR 0260): this machine's.
// Key, when set, names the machine whose keys it may reach (novox/hq ADR 0260; design 46 7-approval-mark §13): that key and the
// keys below it (buscompose.PerMachineKeys), no other machine's.
//
// PerNode says the state is kept one bucket per node (novox/hq ADR 0593 §6: `composed-<node>`): Bucket is then the
// buckets' prefix, each bucket the prefix and a node's name, and a key's first part names its node and so its bucket.