From ce600df98afcd50bb0ed868b5d1fa4555f6c9554 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 26 Oct 2026 13:37:31 +0100 Subject: [PATCH] Say the held-list state on a part's first pass, so a warning raised before a restart is cleared --- modules/mesh-grants/grants_test.go | 9 +++++++++ modules/mesh-grants/internal/grants/part.go | 9 ++++++--- modules/mesh-grants/internal/grants/sealed.go | 4 ++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index eb98079..77842e3 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -1004,6 +1004,15 @@ func TestGrants(t *testing.T) { if last := said[len(said)-1]; last.Key != grants.HeldCondition || last.State != asks.StateCleared { t.Errorf("the held warning is not cleared once nothing is held: %+v", last) } + // A part started while the warning stood (the said state is in memory only) clears it on its first pass. + var fresh []asks.Condition + fp := partOn(b) + fp.Objects = p.Objects + fp.Asker, fp.Say = fa, func(c asks.Condition) error { fresh = append(fresh, c); return nil } + fp.Compose() + if len(fresh) == 0 || fresh[0].Key != grants.HeldCondition || fresh[0].State != asks.StateCleared { + t.Errorf("a fresh part's first pass does not say the held warning is cleared: %+v", fresh) + } } }) } diff --git a/modules/mesh-grants/internal/grants/part.go b/modules/mesh-grants/internal/grants/part.go index 29850f5..38457d7 100644 --- a/modules/mesh-grants/internal/grants/part.go +++ b/modules/mesh-grants/internal/grants/part.go @@ -143,9 +143,12 @@ type Part struct { made creations // askSeen is the revision of the last ask to compose (KeyAsked) this part acted on. askSeen uint64 - // heldSaid is the reason of the held-list condition last said (empty: none raised); holdMu guards it. - holdMu sync.Mutex - heldSaid string + // heldSaid is the reason of the held-list condition last said (empty: none raised); heldKnown is whether one was said + // since the start, so the first pass after a start always says its state (a condition raised before a restart is cleared + // by it). holdMu guards both. + holdMu sync.Mutex + heldSaid string + heldKnown bool } // Ask is the value of KeyAsked: a tool's ask to compose now, and, from grants_assert, to create one module's missing diff --git a/modules/mesh-grants/internal/grants/sealed.go b/modules/mesh-grants/internal/grants/sealed.go index 7244d0e..6a584cc 100644 --- a/modules/mesh-grants/internal/grants/sealed.go +++ b/modules/mesh-grants/internal/grants/sealed.go @@ -155,7 +155,7 @@ func (p *Part) sayHeld(why string) { } p.holdMu.Lock() defer p.holdMu.Unlock() - if why == p.heldSaid { + if p.heldKnown && why == p.heldSaid { return } c := asks.Condition{Key: HeldCondition, Subject: asks.Scope{Scope: "module", ID: "mesh-grants"}} @@ -169,7 +169,7 @@ func (p *Part) sayHeld(why string) { c.Resolved = "a pass finds nothing to hold back and the list advances" } if p.Say(c) == nil { - p.heldSaid = why + p.heldSaid, p.heldKnown = why, true } }