package main import ( "errors" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/conditions" "github.com/novox/mesh-controller/internal/link" ) // **Every user's slow consumer is S9's, not the controller's alone** (novox/hq ADR 0357, issue 270). On // 2026-10-07 the bus closed two clients on the laptop as slow consumers and nothing raised a condition: // the controller heard only its own connection. Said by the bus module and read back, a user the bus // closes is one condition naming the module and its machine, raised at the first look and cleared an hour // after the last closure. func TestAUserTheBusClosedAsASlowConsumerIsOneConditionNamingItsModuleAndMachine(t *testing.T) { closed := time.Date(2026, 10, 14, 12, 0, 0, 0, time.UTC) now := closed.Add(20 * time.Second) store := conditions.NewInMemory() k := conditions.NewKeeper(t.Context(), conditions.Options{Store: store, History: store, Teller: &conditions.Told{}, Now: func() time.Time { return now }}) t.Cleanup(func() { k.Close(t.Context()) }) advisories := link.BusClosures([]link.BusClosure{ {User: "laptop.claude-code", CID: 10, Reason: "Slow Consumer (Write Deadline)", Opened: closed.Add(-time.Hour), Closed: closed.Add(-10 * time.Minute)}, {User: "laptop.claude-code", CID: 44, Reason: "Slow Consumer (Write Deadline)", Opened: closed.Add(-time.Minute), Closed: closed}, {User: "anchor.desk", CID: 7, Reason: "Maximum Message Payload Exceeded", Opened: closed.Add(-time.Hour), Closed: closed}, }, []string{link.AdvisorySlowConsumer, link.AdvisorySlowConsumer, link.AdvisoryConnectionClosed}, closed.Add(-time.Hour)) look := func() { t.Helper() f := &signalFacts{now: now, host: "anchor", advisories: advisories} if err := k.Reconcile(t.Context(), "S9", watchAdvisories(f)); err != nil { t.Fatal(err) } } look() slow, found, err := conditions.ReadOne(t.Context(), store, "bus.laptop.claude-code.slow-consumer") if err != nil || !found { t.Fatalf("no slow-consumer condition for the user: %v %v", found, err) } if slow.Subject.Machine != "laptop" || slow.Observations != 2 || !slow.LastObserved.Equal(closed) { t.Fatalf("the slow consumer is said on %q, %d closure(s), last %s: %+v", slow.Subject.Machine, slow.Observations, slow.LastObserved, slow) } if !strings.Contains(slow.Summary, "claude-code on laptop") || strings.Contains(slow.Summary, "laptop.claude-code") { t.Fatalf("the summary does not name the module in words, or carries the dotted user: %q", slow.Summary) } if !strings.Contains(slow.Headline, "Claude-code on laptop") { t.Fatalf("the headline is %q", slow.Headline) } cut, found, err := conditions.ReadOne(t.Context(), store, "bus.anchor.desk.connection-closed") if err != nil || !found || cut.Subject.Machine != "anchor" || !strings.Contains(cut.Summary, "maximum message payload exceeded") { t.Fatalf("the user cut off for breaking the bus's rules: %v %v %+v", found, err, cut) } // An hour after the newest closure without another, both clear. now = closed.Add(time.Hour + time.Minute) look() for _, key := range []string{"bus.laptop.claude-code.slow-consumer", "bus.anchor.desk.connection-closed"} { if _, found, _ := k.Get(t.Context(), key); found { t.Fatalf("%s is still open an hour after the last closure", key) } } } // **A read of the bus module's events that fails does not blind S9** (review of #326): the controller's own // signals are still said, and the failed read is a condition of its own. func TestABusModuleReadThatFailsKeepsTheControllersOwnSignals(t *testing.T) { at := time.Date(2026, 10, 14, 12, 0, 0, 0, time.UTC) f := &signalFacts{now: at.Add(time.Minute), host: "anchor", advisories: []link.Advisory{{Kind: link.AdvisorySlowConsumer, ID: "controller", Said: "the controller fell behind", First: at, Last: at, Count: 1}}, busFaultsErr: errors.New("nats: timeout")} keys := map[string]bool{} for _, o := range watchAdvisories(f) { keys[o.Key()] = true } if !keys["bus.controller.slow-consumer"] || !keys["bus.bus-module.bus-faults-unread"] { t.Fatalf("observed %v: the controller's own slow consumer and the unread events, both", keys) } for _, row := range signalsTable { if row.Row == "S9" && row.needs(f) != nil { t.Fatal("the S9 row went blind for a read of the bus module's events") } } }