The composition verb says what holds each held module and the build it would be composed at (issue 1921, milestone 47/G H4)
mesh/delivery closed: a newer head of the same pull request
mesh/delivery-group group feat/registry-milestone checking: 6 of 7 member(s) ready
mesh/check failed (4): unit: git.novox.be/novox/mesh-catalog/checks/gitignored.TestNoProgramABuildLeavesIsC…; and 3 more | 5b75e866d079 on e73aae3686a6

The node's own composition named a held module's why only; it now also says whether its manifest's verdict or this
node-engine's decoder holds it, and at which build, as the input a check's composer reads says it. Said in the held
words, so the node-engine seat's answer keeps its shape.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-07 02:38:18 +01:00
parent d0fb384f00
commit 5b75e866d0
3 changed files with 51 additions and 4 deletions
+31 -4
View File
@@ -235,6 +235,9 @@ type Pass struct {
Refused string `json:"refused,omitempty"`
// Held is each module refused alone, with why.
Held map[string]string `json:"held,omitempty"`
// holds is what holds each module Held names by its manifest's verdict or its decoder, with the build it would be
// composed at (frombus.Scope.Holds), for LastHeld's words.
holds map[string]compose.Hold
// Handed is how many compositions the pass handed the agent that differ from what it handed before.
Handed int `json:"handed"`
// Again is when the node composes again on its own, after a seat or a read that did not answer; zero when it does
@@ -247,11 +250,34 @@ type Pass struct {
Queued string `json:"queued,omitempty"`
}
// LastHeld is what the last pass held, each module with why; nil before a pass or when it held none.
// LastHeld is what the last pass held, each module with why: for one its manifest's verdict or its decoder holds, also
// that and the build it would be composed at, as the input a check's composer reads says it (compose.Input.Holds,
// issue 1921); nil before a pass or when it held none.
func (c *Composer) LastHeld() map[string]string {
c.mu.Lock()
defer c.mu.Unlock()
return maps.Clone(c.last.Held)
out := maps.Clone(c.last.Held)
for name, h := range c.last.holds {
if why, held := out[name]; held {
out[name] = why + " (" + holdWords(h) + ")"
}
}
return out
}
// holdWords is what holds a module and the build it would be composed at, in words.
func holdWords(h compose.Hold) string {
by := "held by " + h.By
switch h.By {
case compose.HeldByVerdict:
by = "held by its manifest's verdict"
case compose.HeldByDecoder:
by = "held by this node-engine's decoder"
}
if h.Build == "" {
return by + ", at no build"
}
return by + ", at the build " + h.Build
}
// askedWithin is how recent a pass answers an ask of the verb compose without a new one: the first wait after a seat
@@ -919,6 +945,7 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
// module's have, from which the watchman raises its condition.
keptRuns := map[string][]string{}
var keptMu sync.Mutex
var holds map[string]compose.Hold
scope := frombus.Scope{Node: c.env.Node, KeptAt: kept, InOwn: inOwn, Running: running, Kept: func(module, words string) {
keptMu.Lock()
defer keptMu.Unlock()
@@ -926,7 +953,7 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
keptRuns[module] = append(keptRuns[module], words)
}
c.sayOnceEach(words)
}}
}, Holds: func(h map[string]compose.Hold) { holds = h }}
c.mu.Lock()
c.dataScope = frombus.Scope{Node: c.env.Node, KeptAt: kept, InOwn: inOwn} // the data items are worked out as composed
c.mu.Unlock()
@@ -985,7 +1012,7 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
return nil
}
c.paid()
pass.Held = held
pass.Held, pass.holds = held, holds
// The provisioner client's account the node runs with, dropped by this pass, is said: every filled write is refused
// under a declaration that names none (filling.JudgeAccount), and no module is handed to it (issue 774, ace,
// 2026-11-04 01:29). A placed client keeps its account while it is held (compose.Node.RunningProvisioner).