From 796fb69d17f0f288b7123f480f23e417ad599144 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 22:47:56 +0100 Subject: [PATCH] Gate ignores a group's end on FORGE before decoding; keeper row for take-in-waits (issue 1585, ADR 0633) Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn --- .../cmd/mesh-conditions/keeper_test.go | 11 +++++++++++ .../cmd/mesh-delivery-gate/holder_test.go | 5 +++++ .../cmd/mesh-delivery-gate/merges.go | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go b/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go index c2d6b3bb2..c36bfb218 100644 --- a/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go +++ b/modules/mesh-conditions/cmd/mesh-conditions/keeper_test.go @@ -240,6 +240,14 @@ func TestTheKeeperCarriesNoControllerConditionAndClearsWhatItCarriedOnce(t *test if err := r.k.Finding(watchman, key, raw, false); err != nil { t.Fatal(err) } + // take-in-waits (issue 1585) is an observer's kind with no row in Homes: its finding is raised and EndCarried leaves it (T26). + takeKey, takeRaw := finding(t, watchman, health.ScopeMachine, "g14", "take-in-waits", "take-in", health.Warning, r.now) + if err := r.k.Finding(watchman, takeKey, takeRaw, false); err != nil { + t.Fatal(err) + } + if _, open := r.open(takeKey); !open { + t.Fatal("a take-in-waits finding raised no condition") + } r.said = nil // Carried while the controller ran: two of kinds that ended with it, still open in conditions. for _, c := range []health.Condition{ @@ -257,6 +265,9 @@ func TestTheKeeperCarriesNoControllerConditionAndClearsWhatItCarriedOnce(t *test if _, open := r.open(key); !open { t.Error("a condition a finding holds was cleared") } + if _, open := r.open(takeKey); !open { + t.Error("EndCarried cleared a take-in-waits condition") + } if n, err := r.k.EndCarried(); err != nil || n != 0 || r.changes() != "" { t.Errorf("a second EndCarried cleared %d (%v)", n, err) } diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go index 0c5d84dde..fd87a0172 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go @@ -251,6 +251,11 @@ func TestAMergedGroupEndsMergedWithSourceMainMoved(t *testing.T) { if len(w.h.refusals) != 0 { t.Errorf("the gate refused a merged message with no trees: %v", w.h.refusals) } + // A group's end is the indexer's: the gate ignores it before decoding, and says no refusal (1585/T25). + w.h.Forged("mesh-delivery-forge-bridge.forge.group.merged", []byte(`{"v":1,"group":"feat/y"}`)) + if len(w.h.refusals) != 0 { + t.Errorf("the gate refused a group's end: %v", w.h.refusals) + } } // ADR 0419 ยง2, issue 978: a description's `after:` line is not read, and says so once on its pull request. The diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/merges.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/merges.go index d2634300b..f8628af46 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/merges.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/merges.go @@ -312,8 +312,17 @@ func (h *Holder) mergeValue(name, key string, approved map[string]string, target return m, len(m.Pulls) > 0 && state == sdk.MergeMerged } +// forgeGroupEnd ends the subject of a group end on FORGE: the four-part form of a pull request's message has no such tail. +const forgeGroupEnd = ".forge.group.merged" + // Forged takes a message of the forge bridge on FORGE: the gate follows a merge and the refusal of a merge, and nothing else. +// +// The end of a group's merges (`forge.group.merged`, a body this gate has no use for: the gate wrote the merges itself) is +// the indexer's, and is ignored before it is decoded, so no refusal is said for it (issue 1585, ADR 0633 clause 4). func (h *Holder) Forged(event string, body []byte) { + if strings.HasSuffix(event, forgeGroupEnd) { + return + } f, err := sdk.DecodeForge(body) if err != nil { h.Refuse(fmt.Errorf("a message of the forge bridge is not readable: %v", err))