From 1f68f80760fce0721e0acc985e9caee86aaa3f5c Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 11:49:56 +0100 Subject: [PATCH] CAT-N4: check and drift: a difference that only grows is applied, any other said and raised until cleared Rows of the keeper's table test: a larger cap is applied with no event; a smaller one is drifted with what differs, raised once, and drift-cleared once the manifest asks what the bus holds again; an entry the bus already holds is taken as made and its binding said, nothing made twice. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- modules/nats/internal/keeper/keeper_test.go | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/modules/nats/internal/keeper/keeper_test.go b/modules/nats/internal/keeper/keeper_test.go index e4743533..b098340c 100644 --- a/modules/nats/internal/keeper/keeper_test.go +++ b/modules/nats/internal/keeper/keeper_test.go @@ -331,6 +331,42 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) { t.Errorf("created %v, said %v", bus.created, out.events) } }}, + // CAT-N4: check and drift (ADR 0595 §5, §6): a difference that only grows is applied, said by nothing; any other + // is said and raised, never fixed quietly, and cleared once the bus holds what is asked again. + {"a held entry that grows is applied without a person", func(t *testing.T, k *Keeper, bus *fakeBus, out *said) { + k.Heard(Accepted{Module: "m", Revision: 1, BusState: []catalogue.BusStateEntry{log}}) + out.reset() + bigger := entry("log", catalogue.StorageStream, catalogue.TemplateLog, map[string]string{"max-mib": "2048"}) + k.Heard(Accepted{Module: "m", Revision: 2, BusState: []catalogue.BusStateEntry{bigger}}) + if fmt.Sprint(bus.updated) != "[m_log]" || bus.held["m_log"].MaxBytes != 2048*MiB || len(out.events)+len(out.conds) != 0 { + t.Errorf("updated %v to %d, said %v %v: grown quietly", bus.updated, bus.held["m_log"].MaxBytes, out.events, out.conds) + } + }}, + {"a held entry that would shrink is a drift, said, raised and cleared", func(t *testing.T, k *Keeper, bus *fakeBus, out *said) { + k.Heard(Accepted{Module: "m", Revision: 1, BusState: []catalogue.BusStateEntry{log}}) + out.reset() + smaller := entry("log", catalogue.StorageStream, catalogue.TemplateLog, map[string]string{"max-mib": "16"}) + k.Heard(Accepted{Module: "m", Revision: 2, BusState: []catalogue.BusStateEntry{smaller}}) + if len(bus.updated) != 0 || fmt.Sprint(out.events) != "[drifted m log]" || fmt.Sprint(out.conds) != "[raised nats.drifted.m_log]" || + !strings.Contains(out.bodies[0].Why, "max-bytes asked 16777216, held 1073741824") { + t.Fatalf("updated %v, said %v %v %+v: the drift said, nothing changed", bus.updated, out.events, out.conds, out.bodies) + } + if l := k.CheckAll(); len(l) != 1 || l[0].Drift == "" || len(out.events) != 1 { + t.Errorf("check answers %+v and said %v: the drift once", l, out.events) + } + k.Heard(Accepted{Module: "m", Revision: 3, BusState: []catalogue.BusStateEntry{log}}) + if fmt.Sprint(out.events) != "[drifted m log drift-cleared m log]" || out.conds[len(out.conds)-1] != "cleared nats.drifted.m_log" { + t.Errorf("said %v %v: cleared once asked as held", out.events, out.conds) + } + }}, + {"a bus that holds an entry the keeper never made is taken as made", func(t *testing.T, k *Keeper, bus *fakeBus, out *said) { + p, _ := PlanOf("m", drafts) + bus.held["KV_m_drafts"] = p.Objects[0].Settings + k.Heard(Accepted{Module: "m", Revision: 1, BusState: []catalogue.BusStateEntry{drafts}}) + if len(bus.created) != 0 || fmt.Sprint(out.events) != "[created m drafts]" { + t.Errorf("created %v, said %v: its binding said, nothing made twice", bus.created, out.events) + } + }}, } { t.Run(row.name, func(t *testing.T) { k, bus, _, out := newKeeper(FirstView{Revision: 1})