G A-37: views.NewStdioHeldWriter, and the held writer holds the findings a reader writes through it

A stdio module's held writer fills Publish and Listen as NewStdioWriter does; HeldWriter.WriteWire holds the finding as Write does,
so a views reader's first-view findings are in the held set and in findings-stated.

Builds: design 47 G A-37, A-29, A-16
This commit is contained in:
2026-11-11 22:08:21 +01:00
parent a59ccad8e3
commit 203b5d5bb7
4 changed files with 59 additions and 2 deletions
+10
View File
@@ -88,3 +88,13 @@ func (h *HeldWriter[F, C, S, A]) Clear(ctx context.Context, scope, id, kind, why
h.mu.Unlock()
return h.Writer.Clear(ctx, scope, id, kind, why)
}
// WriteWire is Write of a finding in its wire form, held as Write holds it (design 47 G A-37): a views reader writing through
// the held writer has its findings in the held set, so in the statement.
func (h *HeldWriter[F, C, S, A]) WriteWire(ctx context.Context, finding map[string]any) error {
var f F
if err := fillFrom(&f, finding); err != nil {
return err
}
return h.Write(ctx, f)
}
+13 -1
View File
@@ -22,6 +22,18 @@ func NewStdioWriter[F, C, S, A any](cfg WriterConfig[F, C, S, A]) (*Writer[F, C,
func newStdioWriter[F, C, S, A any](cfg WriterConfig[F, C, S, A],
emit func(string, any, ...stdio.EmitOptions) error,
subscribe func(string, func(stdio.Envelope) error) error) (*Writer[F, C, S, A], error) {
return NewWriter(withStdioPlumbing(cfg, emit, subscribe))
}
// NewStdioHeldWriter is NewHeldWriter for a stdio module: it fills `Publish` and `Listen` as NewStdioWriter does (design 47
// G A-37, after A-16).
func NewStdioHeldWriter[F, C, S, A any](cfg WriterConfig[F, C, S, A]) (*HeldWriter[F, C, S, A], error) {
return NewHeldWriter(withStdioPlumbing(cfg, stdio.Emit, stdio.SubscribeNew))
}
func withStdioPlumbing[F, C, S, A any](cfg WriterConfig[F, C, S, A],
emit func(string, any, ...stdio.EmitOptions) error,
subscribe func(string, func(stdio.Envelope) error) error) WriterConfig[F, C, S, A] {
if cfg.Publish == nil {
cfg.Publish = func(_ context.Context, subject string, data []byte, id string) error {
event, err := eventOfSubject(subject)
@@ -50,7 +62,7 @@ func newStdioWriter[F, C, S, A any](cfg WriterConfig[F, C, S, A],
return func() { stopped.Store(true) }, nil
}
}
return NewWriter(cfg)
return cfg
}
// eventOfSubject is the event of a subject of FindingSubjects: `mesh.stream.<module>.event.<event>.<node>`.
+23
View File
@@ -97,6 +97,29 @@ func subStdioWriterPublishesAndListensThroughTheModulesOwnPlumbing(t *testing.T)
if err := w.cfg.Publish(context.Background(), "obs.event", nil, ""); err == nil {
t.Fatal("a subject of no event form is refused")
}
// the held form fills the same plumbing and states what it holds (design 47 G A-37)
out = nil
h, err := NewHeldWriter(withStdioPlumbing(WriterConfig[swFinding, swCleared, swStated, swAsked]{
Module: "m", Node: "n1", State: noState{}, Asked: "mesh.seat.s.event.findings-asked",
}, func(event string, body any, o ...stdio.EmitOptions) error {
raw, _ := json.Marshal(body)
out = append(out, said{event, string(raw), o[0]})
return nil
}, func(string, func(stdio.Envelope) error) error { return nil }))
if err != nil {
t.Fatal(err)
}
if err := h.Write(context.Background(), swFinding{Kind: "k", Scope: "module", ID: "x", Observer: "m", Subject: struct {
Scope string `json:"scope"`
}{"module"}}); err != nil {
t.Fatal(err)
}
if err := h.State(context.Background(), "a"); err != nil {
t.Fatal(err)
}
if len(out) == 0 || out[len(out)-1].event != "findings-stated" || !strings.Contains(out[len(out)-1].body, `"id":"x"`) {
t.Fatalf("a stdio held writer states its findings: %+v", out)
}
}
type noState struct{}
+13 -1
View File
@@ -495,6 +495,13 @@ func subHeldWriterStatesWhatItHoldsFromItsOwnState(t *testing.T) {
if err := h.Write(ctx, testFinding{Kind: "k", ID: "no-scope"}); err == nil {
t.Fatal("a finding that breaks a rule was held")
}
// a views reader writes its findings in the wire form: they are held as well (design 47 G A-37)
var wire map[string]any
rawWire, _ := json.Marshal(finding("wired", "from a reader"))
_ = json.Unmarshal(rawWire, &wire)
if err := h.WriteWire(ctx, wire); err != nil {
t.Fatal(err)
}
if err := h.Clear(ctx, "module", "seeded", "k", "no longer"); err != nil {
t.Fatal(err)
}
@@ -504,9 +511,14 @@ func subHeldWriterStatesWhatItHoldsFromItsOwnState(t *testing.T) {
}
var stated testStated
if len(sent) != 1 || sent[0].subject != subjects.Stated || json.Unmarshal(sent[0].data, &stated) != nil ||
len(stated.Findings) != 1 || stated.Findings[0].ID != "raised" || stated.Asked != "a1" {
len(stated.Findings) != 2 || stated.Asked != "a1" {
t.Fatalf("the statement says %+v after %d events", stated, len(sent))
}
ids := []string{stated.Findings[0].ID, stated.Findings[1].ID}
slices.Sort(ids)
if ids[0] != "raised" || ids[1] != "wired" {
t.Fatalf("the statement says %v", ids)
}
}
// TestFindingSubjectsAreTheEventsStreamForm runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.