An unreadable manifest holds only its module's users: the list advances for every other principal, and the condition and grants_user_list name the module unreadable (ADR 0614 §8, issue 774)

Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
2026-11-03 23:10:13 +01:00
parent 390e50d7d1
commit b83085adac
3 changed files with 22 additions and 8 deletions
+10 -3
View File
@@ -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)
+7 -1
View File
@@ -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)
}
}
}
@@ -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
}
}