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
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:
@@ -171,6 +171,10 @@ type Scope struct {
|
||||
// on the node that its manifest's verdict or its decoder holds, into compose.Input.Holds, never into what the node composes: the
|
||||
// check's composer, judging the change's manifest itself, composes it at that build (issue 1921).
|
||||
takeInHeld bool
|
||||
// Holds is told, after Compose read the input, what holds each module it holds by its manifest's verdict or its
|
||||
// decoder (compose.Input.Holds), with the build it would compose it at, so the node says why it holds it (issue 1921,
|
||||
// the composition verb). Nil: said nowhere.
|
||||
Holds func(map[string]compose.Hold)
|
||||
}
|
||||
|
||||
// InOwn is, of the modules p places on its node, those whose resources the node's own composition it runs
|
||||
@@ -590,6 +594,16 @@ func Compose(ctx context.Context, r Reader, mesh Mesh, s Scope, fps map[string]s
|
||||
if err != nil {
|
||||
return nil, nil, held, err
|
||||
}
|
||||
if s.Holds != nil {
|
||||
holds := map[string]compose.Hold{}
|
||||
for name := range held {
|
||||
if h, said := in.Holds[name]; said {
|
||||
h.Build = fps[name]
|
||||
holds[name] = h
|
||||
}
|
||||
}
|
||||
s.Holds(holds)
|
||||
}
|
||||
// The pair credentials its composition reads, asked of the vault sealed to it (ADR 0550 §5).
|
||||
asConsumer, asProvider, err := compose.CredentialsNeeded(in, s.Node)
|
||||
if err != nil {
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -508,6 +508,12 @@ func TestTheNodeComposesItsWantsFromTheBus(t *testing.T) {
|
||||
if len(*told4) != 1 || !strings.HasPrefix((*told4)[0], "held power: ") || !strings.Contains((*told4)[0], `"shows" is none of them`) {
|
||||
t.Errorf("the agent was told %q, want power held alone, naming its item", *told4)
|
||||
}
|
||||
// Issue 1921: the composition verb's held says what holds power and the build it would be composed at, as the input a
|
||||
// check's composer reads says it.
|
||||
if why := c4.LastHeld()["power"]; !strings.Contains(why, `"shows" is none of them`) ||
|
||||
!strings.Contains(why, "held by its manifest's verdict, at the build "+powerFP) {
|
||||
t.Errorf("the last pass held power for %q, want its verdict and its build named", why)
|
||||
}
|
||||
// Review of issue 774 (the kept read): mesh-wireguard, which the mesh provides and which never has a fingerprint, is
|
||||
// folded in the node's own and composed from the manifest mesh-sdk provides (overlay.ProvidedManifest), not from
|
||||
// what runs nor the kept declaration: with nothing running (the kept read retired, Running empty) the node's own
|
||||
|
||||
Reference in New Issue
Block a user