Node shadow: reuse a running seal only at its own slot, lay values as the controller does, answer the newest candidate first and say how it goes (issue 1126, C1 C2, T1 T2 T3)

A box is reused by slot (resource, field) after the composition, from what runs or the
have's slots; a have written before slots is reused within its module alone. Config
values are laid by mesh-sdk's one rule (never a default). The shadow answers the
newest candidate first and hands each answer as it lands, says when an answer starts,
every minute what it waits on, and when it ends, and fits an answer to the node's key.
Needs mesh-sdk fix/1126-seed-same (16b8d8a1).
This commit is contained in:
2026-10-28 09:41:29 +01:00
parent 7271cf151b
commit a7cd0a7807
9 changed files with 355 additions and 202 deletions
+7 -32
View File
@@ -57,9 +57,10 @@ type Have struct {
// waits for a person's yes (ADR 0547 §3).
Judgement string
Waits string
// Sealed is, on a node composing its own, each value the composition it names carries sealed to the node's key
// (delivery.Have.Sealed): kept in the have so the composer reuses them after a restart (ADR 0547 §3).
Sealed []string
// Sealed is, on a node composing its own, each value the composition it names carries sealed to the node's key, by
// slot (delivery.Have.SealedAt): kept in the have so the composer reuses them after a restart (ADR 0547 §3), each at
// its own slot alone (issue 1126, C1).
Sealed delivery.Seals
}
// Haves is the node's have bucket as the agent uses it.
@@ -471,15 +472,15 @@ func (a *Agent) writeHave(ctx context.Context, h Have) {
a.env.Haves.Write(ctx, h)
}
func (a *Agent) sealedOf(ctx context.Context, module, fp, composed string) []string {
func (a *Agent) sealedOf(ctx context.Context, module, fp, composed string) delivery.Seals {
a.mu.Lock()
if c, ok := a.applied[module]; ok && c.Err == nil && c.Dig == composed {
a.mu.Unlock()
return SealedIn(c.C)
return delivery.SealsIn(c.C)
}
if c, ok := a.compositionFor(module, fp); ok && c.Err == nil && c.Dig == composed {
a.mu.Unlock()
return SealedIn(c.C)
return delivery.SealsIn(c.C)
}
a.mu.Unlock()
if h, ok := a.env.Haves.Read(ctx, module); ok && h.Composed == composed {
@@ -488,32 +489,6 @@ func (a *Agent) sealedOf(ctx context.Context, module, fp, composed string) []str
return nil
}
// SealedIn is each value a composition carries sealed to its node's key: a file's `sealed` and a vault box
// (catalogue.VaultSealedField), as their JSON text, in the order of its resources.
func SealedIn(c delivery.Composed) []string {
var out []string
for _, raw := range c.Resources {
var r map[string]json.RawMessage
if json.Unmarshal(raw, &r) != nil {
continue
}
for _, field := range []string{"sealed", catalogue.VaultSealedField} {
f, ok := r[field]
if !ok {
continue
}
var text string
if json.Unmarshal(f, &text) != nil {
text = string(f) // a vault box is carried as its JSON
}
if text != "" {
out = append(out, text)
}
}
}
return out
}
// Running is the compositions the node runs now: each module's that the agent applied or found already running, and
// the node's own. What the node judges its health by and says of itself is built from them.
func (a *Agent) Running() []delivery.Composed {