Say why a key stays asked when its provider's helper reads no plans of the seat, so a provider left on the controller's assignments is named instead of waited on in silence (issue 774)
mesh/delivery merged: source.main moved to novox/mesh-catalog@0e78909a75b9
mesh/check being carried to bb18c4d14fc3 on 0e78909a75b9 | from d412991c6992
mesh/delivery merged: source.main moved to novox/mesh-catalog@0e78909a75b9
mesh/check being carried to bb18c4d14fc3 on 0e78909a75b9 | from d412991c6992
This commit is contained in:
@@ -568,7 +568,7 @@ func (h *Holder) outcome(key string, p *provisions.Provision) {
|
||||
return
|
||||
}
|
||||
if e == nil {
|
||||
p.State, p.Why = provisions.Asked, ""
|
||||
p.State, p.Why = provisions.Asked, h.unread(p.Provider.Module)
|
||||
return
|
||||
}
|
||||
var en entry
|
||||
@@ -596,6 +596,26 @@ func (h *Holder) outcome(key string, p *provisions.Provision) {
|
||||
}
|
||||
}
|
||||
|
||||
// unread is why a provider whose helper keeps its outcome in its entries has written none, when its manifest says it:
|
||||
// it reads no plans of the seat (novox/hq ADR 0570 clause 2), so its helper never hears a bind and the key stays asked
|
||||
// for good (issue 774, 2026-10-31: route-proxy's helper read only the controller's assignments, and 38 routes the seat
|
||||
// keyed for modules not handed stood asked with no why while the assigner withheld their consumers' builds). "" when
|
||||
// it reads them, or its manifest cannot say: then it has not answered yet.
|
||||
func (h *Holder) unread(module string) string {
|
||||
manifest, err := h.providerManifest(module)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var decl struct {
|
||||
Reads []string `json:"reads"`
|
||||
}
|
||||
if json.Unmarshal([]byte(manifest), &decl) != nil || slices.Contains(decl.Reads, provisions.PlansRead) {
|
||||
return ""
|
||||
}
|
||||
return module + " reads no plans of the seat (its manifest's reads do not name " + provisions.PlansRead +
|
||||
"), so its helper never answers this key"
|
||||
}
|
||||
|
||||
// haveOutcome is the outcome of a provider that makes no credential per consumer: its own have on its node (the lead's
|
||||
// reading of ADR 0570 clause 3, recorded as an insight). Running with health ok at the build its want names is
|
||||
// provided; failed is failed with the have's words; anything else is asked, waiting for the provider to run.
|
||||
|
||||
@@ -443,31 +443,39 @@ func TestAProvidersOutcomeIsReadPerOffer(t *testing.T) {
|
||||
entry string
|
||||
have string // the provider's have key, written running
|
||||
state string
|
||||
why string // a part of the provision's why, when it says one
|
||||
}{
|
||||
{"gitea-read: the helper's refusal is not its outcome; gitea runs, so provided", "ace",
|
||||
map[string]catalogue.Plan{"claude-code": planOf(catalogue.PlanProvision{Provision: "gitea-read", Provider: "novox", ProviderModule: "gitea"})},
|
||||
"ace.claude-code.gitea-read", "gitea.bound", refused, "novox.gitea", provisions.Provided},
|
||||
"ace.claude-code.gitea-read", "gitea.bound", refused, "novox.gitea", provisions.Provided, ""},
|
||||
{"forge-account: still the helper's entry", "ace",
|
||||
map[string]catalogue.Plan{"mesh-build-go": planOf(catalogue.PlanProvision{Provision: "forge-account", Provider: "novox", ProviderModule: "gitea"})},
|
||||
"ace.mesh-build-go.forge-account", "gitea.bound", `{"state":"provided"}`, "", provisions.Provided},
|
||||
"ace.mesh-build-go.forge-account", "gitea.bound", `{"state":"provided"}`, "", provisions.Provided, ""},
|
||||
{"route: route-proxy's entry is read", "novox",
|
||||
map[string]catalogue.Plan{"gitea": planOf(catalogue.PlanProvision{Provision: "route", Provider: "novox", ProviderModule: "route-proxy"})},
|
||||
"novox.gitea.route", "route-proxy.bound", `{"state":"provided"}`, "", provisions.Provided},
|
||||
"novox.gitea.route", "route-proxy.bound", `{"state":"provided"}`, "", provisions.Provided, ""},
|
||||
{"mcp-endpoint: node-tools' entry is read", "ace",
|
||||
map[string]catalogue.Plan{"claude-code": planOf(catalogue.PlanProvision{Provision: "mcp-endpoint", Provider: "ace", ProviderModule: "node-tools"})},
|
||||
"ace.claude-code.mcp-endpoint", "node-tools.bound", `{"state":"provided"}`, "", provisions.Provided},
|
||||
"ace.claude-code.mcp-endpoint", "node-tools.bound", `{"state":"provided"}`, "", provisions.Provided, ""},
|
||||
// Issue 774, 2026-10-31: route-proxy's helper read the controller's assignments, never the seat's plans, so 38
|
||||
// routes the seat keys stood asked with no why while the assigner withheld their consumers' builds.
|
||||
{"route: no entry from a provider that reads no plans of the seat is said so", "ace",
|
||||
map[string]catalogue.Plan{"grafana": planOf(catalogue.PlanProvision{Provision: "route", Provider: "ace", ProviderModule: "route-proxy"})},
|
||||
"ace.grafana.route", "", "", "", provisions.Asked, "reads no plans of the seat"},
|
||||
} {
|
||||
m := newMesh()
|
||||
m.manifestOf("gitea", gitea)
|
||||
m.manifestOf("route-proxy", routes)
|
||||
m.manifestOf("node-tools", nodeTools)
|
||||
m.atWant[row.node], m.atNewest[row.node] = row.plan, row.plan
|
||||
m.s.write(t, row.bucket, row.key, row.entry)
|
||||
if row.bucket != "" {
|
||||
m.s.write(t, row.bucket, row.key, row.entry)
|
||||
}
|
||||
if row.have != "" {
|
||||
m.s.write(t, HaveRead, row.have, running)
|
||||
}
|
||||
m.compose(t, row.node)
|
||||
if p := m.state(t, row.key); p.State != row.state {
|
||||
if p := m.state(t, row.key); p.State != row.state || !strings.Contains(p.Why, row.why) {
|
||||
t.Errorf("%s: %s is %s (%s)", row.name, row.key, p.State, p.Why)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user