From 3249b30e8d1cbda6e18a4959158b07474b7ef47b Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 13:17:26 +0100 Subject: [PATCH] SD-M1: a holder's event is under its module's subjects, or its own stream's when a family keeps it The definition reader refuses any other subject, and a mesh.stream. subject no family names (A2c's finding: the checker's events in its own stream were written as mesh.mod.). Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- go/seatdef/seatdef.go | 10 +++++++++- go/seatdef/seatdef_test.go | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/go/seatdef/seatdef.go b/go/seatdef/seatdef.go index 50f0da4..f1336f3 100644 --- a/go/seatdef/seatdef.go +++ b/go/seatdef/seatdef.go @@ -389,8 +389,16 @@ func (d Definition) check() Problems { if !eventName.MatchString(e.Name) { ps = append(ps, Problem{where + ".name", "missing, or not lower-case words joined by dots"}) } - if e.Subject == "" { + // A holder's event is published under the holder's own module subjects, or, when it is kept in a stream of the + // holder's own module (its family), under that stream's (buscompose.ModuleStreamSubject). + own, stream := "mesh.mod..event."+e.Name, "mesh.stream..event."+e.Name + switch { + case e.Subject == "": ps = append(ps, Problem{where + ".subject", "missing"}) + case e.Subject == stream && e.Family == "": + ps = append(ps, Problem{where + ".subject", "a subject of the holder's own stream, and no family names that stream"}) + case e.Subject != own && e.Subject != stream: + ps = append(ps, Problem{where + ".subject", fmt.Sprintf("not %s, nor %s for an event kept in the holder's own stream", own, stream)}) } } for i, r := range d.Records { diff --git a/go/seatdef/seatdef_test.go b/go/seatdef/seatdef_test.go index 7f27a8f..e51f316 100644 --- a/go/seatdef/seatdef_test.go +++ b/go/seatdef/seatdef_test.go @@ -41,6 +41,12 @@ func TestADefinitionIsReadStrictlyNamingWhatIsWrong(t *testing.T) { `"max-age-days":30`, 1), "family.max-age-days: missing, or not 7"}, {"a family with a cap of its own", strings.Replace(whole, `"max-age-days":7`, `"max-age-days":7,"max-mib":256`, 1), "max-mib: a field the format does not know"}, + {"a holder's event on its own stream", strings.Replace(whole, `"family":`, `"holder-events":[{"name":"verdict",`+ + `"subject":"mesh.stream..event.verdict","family":"checker.asks"}],"family":`, 1), ""}, + {"a holder's event on its own stream with no family", strings.Replace(whole, `"family":`, `"holder-events":[{"name":"verdict",`+ + `"subject":"mesh.stream..event.verdict"}],"family":`, 1), "no family names that stream"}, + {"a holder's event on a subject of no module", strings.Replace(whole, `"family":`, `"holder-events":[{"name":"verdict",`+ + `"subject":"mesh.event.verdict"}],"family":`, 1), "holder-events[0].subject: not mesh.mod..event.verdict"}, {"a verb twice", strings.Replace(listGrain, `]}`, `,{"name":"add"}]}`, 1), `"add" twice`}, {"records at the seat list's grain", strings.Replace(listGrain, `"decision":"ADR 1",`, `"decision":"ADR 1","records":[{"name":"asks"}],`, 1), "grain: seat-list holds"},