Node-engine: composition comparison, seals and differing paths are the engine's own package, internal/compositions, with the tests moved from mesh-sdk's delivery (H-1, ADR 0634 §1; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 14:53:06 +01:00
parent dcbdc70919
commit df44d21c82
15 changed files with 967 additions and 44 deletions
+6 -5
View File
@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/novox/mesh-host/internal/compositions"
"slices"
"sort"
"strings"
@@ -68,7 +69,7 @@ type Have struct {
// 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
Sealed compositions.Seals
// PullWait is, on a failed have, the pull from a store its apply failed on and waits on the holder of (issue 1966
// ENG-9, delivery.Have.PullWait): read through the have, as node_haves shows it.
PullWait *delivery.PullWait
@@ -702,15 +703,15 @@ func (a *Agent) KeptNow(ctx context.Context, kept map[string][]string) {
}
}
func (a *Agent) sealedOf(ctx context.Context, module, fp, composed string) delivery.Seals {
func (a *Agent) sealedOf(ctx context.Context, module, fp, composed string) compositions.Seals {
a.mu.Lock()
if c, ok := a.applied[module]; ok && c.Err == nil && c.Dig == composed {
a.mu.Unlock()
return delivery.SealsIn(c.C)
return compositions.SealsIn(c.C)
}
if c, ok := a.compositionFor(module, fp); ok && c.Err == nil && c.Dig == composed {
a.mu.Unlock()
return delivery.SealsIn(c.C)
return compositions.SealsIn(c.C)
}
a.mu.Unlock()
if h, ok := a.env.Haves.Read(ctx, module); ok && h.Composed == composed {
@@ -1771,7 +1772,7 @@ func (a *Agent) waitsForYes(ctx context.Context, module string, w wantNow, comp
// for a yes nobody needs (issue 774, 2026-10-31: four providers stayed waiting-yes once their consumers composed).
if h, ok := a.env.Haves.Read(ctx, module); ok && h.Phase == string(delivery.PhaseWaitingYes) {
// Its sealed values only when they are of what runs; else writeHave reads them (sealedOf).
var sealed delivery.Seals
var sealed compositions.Seals
if h.Composed == r.Composed {
sealed = h.Sealed
}
+5 -4
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/novox/mesh-host/internal/compositions"
"maps"
"slices"
"strings"
@@ -1583,7 +1584,7 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
// agent's start (the switch and every delivery of the node-engine restart it), so what runs is what its have said.
idleFP, idleDig := fpOf("c"), "sha256:"+strings.Repeat("9", 64)
b.haves.Write(context.Background(), Have{Module: "idle", FP: idleFP, Composed: idleDig, Phase: string(delivery.PhaseRunning),
Sealed: delivery.Seals{"idle.key#sealed": `{"box":"idle"}`}})
Sealed: compositions.Seals{"idle.key#sealed": `{"box":"idle"}`}})
want("idle", idleFP, "")
eventually(t, "idle's want was not taken", func() bool {
b.ag.mu.Lock()
@@ -1742,7 +1743,7 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
// what runs (fingerprint, digest, sealed values) rather than naming nothing (review of L2, round 3).
recFP, recDig := fpOf("d"), "sha256:"+strings.Repeat("8", 64)
b.haves.Write(context.Background(), Have{Module: "rec", FP: recFP, Composed: recDig, Phase: string(delivery.PhaseRunning),
Sealed: delivery.Seals{"rec.key#sealed": `{"box":"rec"}`}})
Sealed: compositions.Seals{"rec.key#sealed": `{"box":"rec"}`}})
rk, rraw, rdig := unsigned("rec", fpB, container("rec@sha256:"+strings.Repeat("3", 64)))
b.comps.put(dk2, draw2)
b.comps.put(rk, rraw)
@@ -1751,9 +1752,9 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
applied := len(b.appliedList())
for _, row := range []struct {
module, fp, composed, waits string
sealed delivery.Seals
sealed compositions.Seals
alone bool
}{{"db", fp, "", ddig2, nil, false}, {"rec", recFP, recDig, rdig, delivery.Seals{"rec.key#sealed": `{"box":"rec"}`}, true}} {
}{{"db", fp, "", ddig2, nil, false}, {"rec", recFP, recDig, rdig, compositions.Seals{"rec.key#sealed": `{"box":"rec"}`}, true}} {
eventually(t, row.module+"'s move did not wait for a yes", func() bool {
h := b.haves.last(row.module)
return h.Phase == string(delivery.PhaseWaitingYes) && h.Waits == row.waits