What a node keeps of a module as it runs, for a manifest that does not yet say it, is carried on the module's have by module, so the watchman raises its condition (ADR 0601 §4; review round 1, finding 3)

This commit is contained in:
2026-11-03 14:02:01 +01:00
parent b461b005e9
commit 7da4deb3f1
9 changed files with 100 additions and 15 deletions
+41
View File
@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"slices"
"sort"
"strings"
"sync"
@@ -60,6 +61,9 @@ type Have struct {
Waits string
// BuildAlone says, in phase waiting-yes, the move changes the build alone (novox/hq ADR 0601 §1, delivery.Have.BuildAlone).
BuildAlone bool
// Kept says, in words, what the node keeps of the module as it runs because its manifest does not yet say it (ADR 0601
// §4, delivery.Have.Kept): the watchman raises a condition from it.
Kept []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).
@@ -205,6 +209,8 @@ type Agent struct {
// keptData is, for each module of the kept declaration whose manifest could be read, whether it declares data
// (Env.HoldsData): the Data of what runs of it from the kept declaration.
keptData map[string]bool
// kept is, by module, what the composer's last pass keeps of it as it runs (KeptNow, ADR 0601 §4). Under mu.
kept map[string][]string
// attempt is the last apply of a module that failed or is held, with the revisions it was made from.
attempt map[string]attempt
checked map[string]bool
@@ -579,6 +585,11 @@ func (a *Agent) NotComposedEach(ctx context.Context, why string, held map[string
// said runs, its fingerprint, digest and sealed values, rather than a have naming nothing: what runs is not lost, and
// the boxes the composer reuses after a restart stay (review of L2, rounds 2 and 3).
func (a *Agent) writeHave(ctx context.Context, h Have) {
if h.Module != NodeModule {
a.mu.Lock()
h.Kept = a.kept[h.Module]
a.mu.Unlock()
}
if h.Composed == "" && h.Module != NodeModule {
if last, ok := a.env.Haves.Read(ctx, h.Module); ok && last.Composed != "" {
h.FP, h.Composed = last.FP, last.Composed
@@ -595,6 +606,36 @@ func (a *Agent) writeHave(ctx context.Context, h Have) {
a.env.Haves.Write(ctx, h)
}
// KeptNow takes in what the composer's last pass keeps of each module as it runs because its manifest does not yet say it
// (ADR 0601 §4), and writes again the have of each module whose words changed, so it carries them; a have waiting for a yes
// takes them at its next write, which keeps what it waits for.
func (a *Agent) KeptNow(ctx context.Context, kept map[string][]string) {
a.mu.Lock()
var changed []string
for m := range a.kept {
if _, still := kept[m]; !still {
changed = append(changed, m)
}
}
for m, words := range kept {
if !slices.Equal(a.kept[m], words) {
changed = append(changed, m)
}
}
a.kept = map[string][]string{}
for m, words := range kept {
a.kept[m] = slices.Clone(words)
}
a.mu.Unlock()
slices.Sort(changed)
for _, m := range changed {
if h, ok := a.env.Haves.Read(ctx, m); ok && m != NodeModule && h.Phase != string(delivery.PhaseWaitingYes) {
h.Module = m
a.writeHave(ctx, h)
}
}
}
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 {