From b83085adac6e3661a92542359d35a458a742da12 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 3 Nov 2026 23:10:13 +0100 Subject: [PATCH] =?UTF-8?q?An=20unreadable=20manifest=20holds=20only=20its?= =?UTF-8?q?=20module's=20users:=20the=20list=20advances=20for=20every=20ot?= =?UTF-8?q?her=20principal,=20and=20the=20condition=20and=20grants=5Fuser?= =?UTF-8?q?=5Flist=20name=20the=20module=20unreadable=20(ADR=200614=20?= =?UTF-8?q?=C2=A78,=20issue=20774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn --- modules/mesh-grants/grants_test.go | 13 ++++++++++--- modules/mesh-grants/internal/grants/held.go | 8 +++++++- modules/mesh-grants/internal/grants/inputs.go | 9 +++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/modules/mesh-grants/grants_test.go b/modules/mesh-grants/grants_test.go index 88deec86..162596c9 100644 --- a/modules/mesh-grants/grants_test.go +++ b/modules/mesh-grants/grants_test.go @@ -2822,7 +2822,8 @@ func TestGrants(t *testing.T) { t.Errorf("%s: the kept user was not said: %+v", held.name, last) } } - // A held module that runs no build the part can read declares nothing to anyone: the list in force is kept, said. + // A held module that runs no build the part can read declares nothing to anyone: only its own users stay as the list in + // force holds them, said unreadable; the others move (ADR 0614 §8). delete(b.states[grants.ReadHave], "b.mod-r") b.put(grants.ReadModules, "mod-r", delivery.Module{V: delivery.Version, FP2: "src2:" + strings.Repeat("ab", 32), Manifest: delivery.ManifestDigest([]byte(ctl)), Repository: "novox/x", Commit: "c", Verdict: delivery.VerdictRefused, @@ -2831,8 +2832,14 @@ func TestGrants(t *testing.T) { wasList, _, _ := p.Sealed() res := p.Compose() nowList, _, _ := p.Sealed() - if nowList.Digest != wasList.Digest || res.Sealed == nil || !strings.Contains(res.Sealed.Why, "running manifest of mod-r") { - t.Errorf("a held module with no running manifest did not keep the list in force: %+v", res.Sealed) + if nowList.Digest == wasList.Digest || slices.ContainsFunc(res.Errors, func(e string) bool { return strings.Contains(e, "composed whole") }) { + t.Errorf("a module with no readable manifest held the whole list: %+v", res.Sealed) + } + if kept, _, ok, _ := b.ownGet(grants.StateUserList, grants.SealedUserKey(nowList.Digest, "b.mod-r.mod-r-tools")); !ok || string(kept) != string(before) { + t.Errorf("b.mod-r.mod-r-tools did not keep its grants of the list in force: %s", kept) + } + if last := said[len(said)-1]; last.State != asks.StateRaised || !strings.Contains(last.Summary, "the manifest of mod-r is unreadable") { + t.Errorf("the unreadable manifest was not said: %+v", last) } // Accepted again: the users compose from the manifest and the condition clears. b.manifestOf("mod-r", ctl) diff --git a/modules/mesh-grants/internal/grants/held.go b/modules/mesh-grants/internal/grants/held.go index 1a283593..d5177e59 100644 --- a/modules/mesh-grants/internal/grants/held.go +++ b/modules/mesh-grants/internal/grants/held.go @@ -47,9 +47,15 @@ func heldModuleUsers(in Inputs, candidates []string) map[string]string { continue } prefix := node + "." + module + // No manifest of the module can be read at all (the trunk's is refused and no running build's is found): named + // unreadable, in the condition and in grants_user_list (ADR 0614 §8). + state := "held" + if _, readable := in.Held[module]; !readable { + state = "unreadable" + } for _, user := range candidates { if user == prefix || strings.HasPrefix(user, prefix+".") { - out[user] = fmt.Sprintf("the manifest of %s is held: %s", module, why) + out[user] = fmt.Sprintf("the manifest of %s is %s: %s", module, state, why) } } } diff --git a/modules/mesh-grants/internal/grants/inputs.go b/modules/mesh-grants/internal/grants/inputs.go index 82b6a39c..839a8f86 100644 --- a/modules/mesh-grants/internal/grants/inputs.go +++ b/modules/mesh-grants/internal/grants/inputs.go @@ -446,8 +446,9 @@ func ReadInputs(r Reader, cache *Manifests) Inputs { } in.Held = heldManifests(r, in, cache) // A held module placed on a node whose running manifest is not found declares no seat, stream or object bucket to - // anyone: composing past it would drop the grants of those who use them, so the list in force is kept (ADR 0601 - // clause 3), and the gap says which module and why. + // anyone: composing past it would drop the grants of those who use them, so the users of that module alone stay as the + // list in force holds them (held.go, named unreadable; ADR 0614 §8, ADR 0601 clause 3). The gap says which module and why, + // and holds nothing else: it is not a failed read. for _, g := range in.Refused { module, ok := strings.CutPrefix(g.Input, "manifest of ") if _, found := in.Held[module]; !ok || found { @@ -455,8 +456,8 @@ func ReadInputs(r Reader, cache *Manifests) Inputs { } for _, assigned := range in.Assigned { if slices.Contains(assigned, module) { - in.Gaps = append(in.Gaps, Gap{Input: "running manifest of " + module, Failed: true, - Why: "its manifest is held and no running build's manifest is found, so the seats and streams it declares cannot be composed: the list in force is kept"}) + in.Gaps = append(in.Gaps, Gap{Input: "running manifest of " + module, + Why: "its manifest is unreadable (held, and no running build's manifest is found), so the seats and streams it declares cannot be composed: its users stay as the list in force holds them"}) break } }