compose-candidate reads the indexer's candidate by its group key, not the check's key
mesh/delivery merged: source.main moved to novox/mesh-host@27a956448c0a
mesh/check checking 0bbdae1d9dfb on 27a956448c0a
mesh/delivery merged: source.main moved to novox/mesh-host@27a956448c0a
mesh/check checking 0bbdae1d9dfb on 27a956448c0a
The checker asks by the composition key, <group>.<heads hash> (ADR 0593 §7), but the indexer keys a candidate by its group alone (delivery.CandidatesKey). The node-engine read the ask's key as the bucket key, so every check's composition was refused on every node with "the indexer holds no candidate" (mesh-catalog #599, 2026-11-02). It now parses the ask, reads CandidatesKey(group), and answers unready when the group's candidate has moved to other heads. Both tests' fixtures had captured the wrong key; they now key by group, with a slash in the group name. Quick fix: this lookup. Thorough fix (a shared SDK reader that takes a composition key and returns the candidate at those heads) not taken: one reader, one repository. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -16,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
// **A check's candidate, composed in shadow on a call** (novox/hq ADR 0593 §7): the node seat's verb compose-candidate.
|
||||
// Given the key of the indexer's candidate (delivery.CandidatesBucket, `<group>.<heads hash>`), the node composes itself
|
||||
// Given the check's composition key (`<group>.<heads hash>`), it reads the group's candidate from the indexer
|
||||
// (delivery.CandidatesBucket, keyed by the group alone, delivery.CandidatesKey) at those heads, and composes itself
|
||||
// twice from what its shadow holds (placement, records, the indexer's modules, its config values), with its own sealed
|
||||
// values: once as it is, and once with the candidate's modules laid over the indexer's (each at the candidate's
|
||||
// fingerprint, its manifest by digest and the indexer's verdict on it, ADR 0578 §3; a module the candidate removes left
|
||||
@@ -47,7 +48,14 @@ func (s *Shadow) ComposeCandidate(ctx context.Context, ask delivery.ComposeCandi
|
||||
a.Verdict = delivery.ComposeCandidateUnready
|
||||
return a
|
||||
}
|
||||
if _, _, err := delivery.ParseCompositionKey(ask.Candidate); err != nil {
|
||||
group, heads, err := delivery.ParseCompositionKey(ask.Candidate)
|
||||
if err != nil {
|
||||
return refuse(err.Error())
|
||||
}
|
||||
// The ask names the check (the composition key, `<group>.<heads hash>`); the indexer keys the group's candidate by
|
||||
// the group alone (delivery.CandidatesKey) and holds its heads hash in the value.
|
||||
ckey, err := delivery.CandidatesKey(group)
|
||||
if err != nil {
|
||||
return refuse(err.Error())
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, delivery.ComposeAnswerBound)
|
||||
@@ -56,7 +64,7 @@ func (s *Shadow) ComposeCandidate(ctx context.Context, ask delivery.ComposeCandi
|
||||
return unready(errNotReady.Error())
|
||||
}
|
||||
base := c.scratch()
|
||||
raw, err := base.get(ctx, delivery.CandidatesBucket, ask.Candidate)
|
||||
raw, err := base.get(ctx, delivery.CandidatesBucket, ckey)
|
||||
if err != nil {
|
||||
return unready(fmt.Sprintf("the indexer's candidate %s cannot be read: %v", ask.Candidate, err))
|
||||
}
|
||||
@@ -67,6 +75,11 @@ func (s *Shadow) ComposeCandidate(ctx context.Context, ask delivery.ComposeCandi
|
||||
if err != nil {
|
||||
return refuse(fmt.Sprintf("the indexer's candidate %s cannot be read: %v", ask.Candidate, err))
|
||||
}
|
||||
if cand.HeadsHash != heads {
|
||||
// The group moved to other heads: the check asked is replaced, and this node composes not a candidate it was
|
||||
// not asked about. No answer (ADR 0593 §9), never a verdict on the wrong heads.
|
||||
return unready(fmt.Sprintf("the indexer's candidate of the group %s is at heads %s, not %s: the check asked is replaced", group, cand.HeadsHash, heads))
|
||||
}
|
||||
laid := c.scratch()
|
||||
touched, err := laid.layCandidate(ctx, cand)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user