Node agent: say what it holds today in its answer, so a seed that holds the same is the same; pin mesh-sdk b03bf4c (issue 1126)

This commit is contained in:
2026-10-28 22:15:24 +01:00
parent 4f6b389a37
commit b7159d71a6
5 changed files with 64 additions and 11 deletions
+34 -3
View File
@@ -40,6 +40,9 @@ type Shadow struct {
// ran is, by module, the digest the module's have says runs: what runs, where the agent's list holds only the copy
// from the declaration kept before the switch (issue 1126, probe #532 on shanks).
ran map[string]string
// heldNow is, by module, whether its have says the node holds it today (phase held): a candidate holding it too holds
// it as today (frombus.Scope.HeldToday, issue 1126).
heldNow map[string]bool
// answered is, by proposal, the candidate revisions and the input changes (moved) each answer was composed at: a proposal
// is answered again only when either moved (issue 1126, 2026-10-28: shanks answered probe/1126-seed-7 at revision 84
// every 13-17 s, woken by its own modules' haves whose health words changed at every look).
@@ -614,7 +617,7 @@ func (s *Shadow) answerProposal(ctx context.Context, proposal string) (delivery.
c.wants = held
c.mu.Unlock()
a := frombus.Answer(ctx, reader{c: c}, c.env.Mesh, frombus.Scope{Node: node, KeptAt: kept, Placements: given, InOwn: inOwn,
SameValue: c.sameValue}, fps, running, proposal, rev)
SameValue: c.sameValue, HeldToday: s.heldToday()}, fps, running, proposal, rev)
s.mu.Lock()
ran := make(map[string]string, len(s.ran))
for m, d := range s.ran {
@@ -624,6 +627,28 @@ func (s *Shadow) answerProposal(ctx context.Context, proposal string) (delivery.
return asHavesSay(a, ran), true
}
// heldToday is what the node holds today: each module whose have's phase is held, and what its own composer holds, the
// node's own composition ("node") among them when it composes none of its own.
func (s *Shadow) heldToday() []string {
set := map[string]bool{}
s.mu.Lock()
for m, held := range s.heldNow {
if held {
set[m] = true
}
}
s.mu.Unlock()
for _, m := range s.c.heldNow() {
set[m] = true
}
out := make([]string, 0, len(set))
for m := range set {
out = append(out, m)
}
sort.Strings(out)
return out
}
// takeRan keeps the digest one of this node's module haves says runs, and says whether it changed: a have whose health
// or words alone moved runs the same.
func (s *Shadow) takeRan(u agent.Update) bool {
@@ -635,12 +660,18 @@ func (s *Shadow) takeRan(u agent.Update) bool {
defer s.mu.Unlock()
before, had := s.ran[module]
h, err := delivery.DecodeHave(u.Value)
if s.heldNow == nil {
s.heldNow = map[string]bool{}
}
wasHeld := s.heldNow[module]
s.heldNow[module] = !u.Deleted && err == nil && h.Phase == delivery.PhaseHeld
holdMoved := wasHeld != s.heldNow[module] // a hold's start or end changes the answer too
if u.Deleted || err != nil || h.Composed == "" || h.Phase == delivery.PhaseRemoved {
delete(s.ran, module)
return had
return had || holdMoved
}
s.ran[module] = h.Composed
return !had || before != h.Composed
return !had || before != h.Composed || holdMoved
}
// asHavesSay is a with each module that differs only from the copy the agent keeps of the declaration before the switch