view_tiers names each wait by its edge, a verb, a reader capability or bus state, and every verb edge left out (ADR 0614 §2-§5; issue 774)
The words of a want held on a verb or a reader capability are written and read in one place, the module graph's package: the assigner writes them, the view names the edge from them. A bus-state wait is named bus state, and each module's verb edges the graph leaves out on a cycle are listed beside its waits. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
@@ -1,30 +1,24 @@
|
||||
package assign
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/graph"
|
||||
)
|
||||
|
||||
// verbWaiting says why the module's want on node waits for a verb it invokes (novox/hq ADR 0614 §2): the first seat verb or
|
||||
// module tool of Invokes (the module graph's verb edges, those it leaves out on a cycle not among them, §3) that no
|
||||
// running healthy build serves. Nil Served (nothing read of what runs) waits for none, and a verb a running build serves
|
||||
// already adds no wait. It holds under the provisions' rule (RuleProvisions): no reader knows a rule number of its own
|
||||
// yet, so the words name the edge.
|
||||
// yet, so the words name the edge (graph.VerbWaitWords, which the delivery view reads).
|
||||
func (m Module) verbWaiting(node string) (string, bool) {
|
||||
if m.Served == nil {
|
||||
return "", false
|
||||
}
|
||||
for _, v := range slices.Sorted(slices.Values(m.Invokes)) {
|
||||
if !m.Served(v) {
|
||||
return VerbWaitWords(v, m.Name, node), true
|
||||
return graph.VerbWaitWords(v, m.Name, node), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// VerbWaitWords are the words of a want held for a verb it invokes (ADR 0614 §2): they open with "the verb", so a reader
|
||||
// of the want's words (the delivery view) names the edge.
|
||||
func VerbWaitWords(verb, module, node string) string {
|
||||
return fmt.Sprintf("the verb %s %s invokes is served by no running build: its want on %s waits until a build serving it runs healthy (ADR 0614 §2)",
|
||||
verb, module, node)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/graph"
|
||||
)
|
||||
|
||||
// Module is what the six rules read of one module (ADR 0429 §6, ADR 0528 clause 3): its newest fingerprint F at the
|
||||
@@ -175,8 +177,7 @@ func (m Module) readerLacking(node string, n Nodes) (string, bool) {
|
||||
if len(missing) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return fmt.Sprintf("the node-engine of %s does not read %s, which the manifest of %s at %s needs: the want moves once that engine states it",
|
||||
node, strings.Join(missing, ", "), m.Name, short(m.FP)), true
|
||||
return graph.ReaderWaitWords(node, missing, m.Name, short(m.FP)), true // the words the delivery view names the edge by
|
||||
}
|
||||
|
||||
// provisionWaiting says why the module's want on node waits for a provision (ADR 0570 §4): a provision it requires that has
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/provisions"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/graph"
|
||||
)
|
||||
|
||||
func fpOf(c byte) string { return delivery.SourcePrefix2 + strings.Repeat(string(c), 64) }
|
||||
@@ -169,7 +171,7 @@ func TestEachRuleHoldsTheWantBack(t *testing.T) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if out.Wants["g14"].FP == fNew || out.Rules["g14"].Rule != delivery.RuleProvisions || out.Rules["g14"].Words != VerbWaitWords(c.holds, "mail", "g14") {
|
||||
if out.Wants["g14"].FP == fNew || out.Rules["g14"].Rule != delivery.RuleProvisions || out.Rules["g14"].Words != graph.VerbWaitWords(c.holds, "mail", "g14") {
|
||||
t.Errorf("%s: want %+v, held by %+v", c.name, out.Wants["g14"], out.Rules["g14"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,11 @@ func Tools(r view.Reader, u view.UsageReader) []stdio.Tool {
|
||||
}},
|
||||
{Name: view.TiersTool,
|
||||
Description: "A source revision's tiers, worked out now and never stored (novox/hq ADR 0603 §6): the modules the revision moves " +
|
||||
"(each whose fingerprint last changed at it), ordered by the module graph (a module after the bases and the providers " +
|
||||
"of the provisions it awaits among them), and for each module what it waits on now and why: its build key, on each node " +
|
||||
"that wants it its want (the assigner's rule and words when held back), its have, and each provision it awaits. Nodes " +
|
||||
"(each whose fingerprint last changed at it), ordered by the module graph (a module after the bases, the providers " +
|
||||
"of the provisions and bus state it awaits and the modules serving the verbs it invokes, among them; novox/hq ADR 0614), " +
|
||||
"and for each module what it waits on now and why, named by its edge: its build key, on each node that wants it its want " +
|
||||
"(the assigner's rule and words when held back: a verb no running build serves, a reader capability the node-engine " +
|
||||
"does not state), its have, each provision and its bus state; and the verb edges the graph leaves out on a cycle. Nodes " +
|
||||
"the watchman says are offline are named away and left out. At most 500 modules, said when cut short; said when the " +
|
||||
"module graph cannot be read. Give revision for an older one, none for the gate's newest. Read-only.",
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{
|
||||
|
||||
@@ -26,6 +26,9 @@ func encoded(t *testing.T, v []byte, err error) string {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// short is a fingerprint as the assigner's words shorten it.
|
||||
func short(fp string) string { return fp[:len("src2:")+12] }
|
||||
|
||||
// tiersToolSaysEachModulesWait: view_tiers orders a source revision's modules by the module graph and names each one's
|
||||
// wait (ADR 0603 §6): a consumer after its provider, held by the assigner's provision rule and its provision not provided;
|
||||
// a module whose build waits for a package; a provider with no wait; a node offline named away; older modules left out.
|
||||
@@ -104,6 +107,31 @@ func tiersToolSaysEachModulesWait(t *testing.T) {
|
||||
if out, err := run(t, r, view.TiersTool, map[string]any{}); err != nil || out.(view.Tiers).Revision != 9 || out.(view.Tiers).Modules != 3 {
|
||||
t.Errorf("view_tiers of the gate's newest = %+v, %v", out, err)
|
||||
}
|
||||
// ADR 0614 §2-§5: a want held on a verb or a reader capability is named by its edge, a wait for bus state as bus state,
|
||||
// and each verb edge the graph leaves out on a cycle is named.
|
||||
r.GraphState = fake{
|
||||
"db": node(graph.Node{FP: fp("a"), Repository: "novox/mesh-catalog", Provides: []string{"database"}}),
|
||||
"web": node(graph.Node{FP: fp("b"), Repository: "novox/mesh-catalog", Requires: []string{"database", graph.BusState}, Awaits: []string{"database", graph.BusState},
|
||||
Invokes: []string{"seat:x.v", "db.loop"}, LeftOut: []string{"db.loop"}}),
|
||||
"lib": node(graph.Node{FP: fp("c"), Repository: "novox/mesh-catalog"}),
|
||||
}.reader().State
|
||||
for words, edge := range map[string]string{
|
||||
graph.VerbWaitWords("seat:x.v", "web", "ace"): "verb seat:x.v",
|
||||
graph.ReaderWaitWords("ace", []string{"tool-runner"}, "web", short(fp("b"))): "reader capability tool-runner",
|
||||
"the provision ace.web.database of web on ace is asked, not provided": "want",
|
||||
} {
|
||||
r.WantsState = fake{key(delivery.WantKey("ace", "web")): want(delivery.Want{Self: true, FP: fp("f"), Newest: fp("b"), Rule: delivery.RuleProvisions, Words: words})}.reader().State
|
||||
out, err := run(t, r, view.TiersTool, map[string]any{"revision": float64(9)})
|
||||
got, _ := out.(view.Tiers)
|
||||
if err != nil || len(got.Tiers) != 2 {
|
||||
t.Fatalf("view_tiers 9 = %+v, %v", out, err)
|
||||
}
|
||||
web := got.Tiers[1][0]
|
||||
if len(web.Waits) != 3 || web.Waits[0].Edge != edge || web.Waits[1].Edge != "bus state" || !strings.Contains(web.Waits[1].Words, "no key of bus-state") ||
|
||||
!reflect.DeepEqual(web.LeftOut, []string{"db.loop"}) {
|
||||
t.Errorf("web held by %q = %+v, want its want named %s, a bus state wait and db.loop left out", words, web, edge)
|
||||
}
|
||||
}
|
||||
// The graph not readable: every module in the first tier, said; past 500 modules the answer says it is cut short.
|
||||
r.GraphState = view.StateRead{Keys: func() ([]string, error) { return nil, errors.New("no bucket") }, Get: r.GraphState.Get}
|
||||
if out, err := run(t, r, view.TiersTool, map[string]any{"revision": float64(9)}); err != nil || len(out.(view.Tiers).Tiers) != 1 ||
|
||||
|
||||
@@ -16,9 +16,11 @@ import (
|
||||
)
|
||||
|
||||
// The tiers of a source revision are a view, worked out at each call and never stored (novox/hq ADR 0603 §1, §6): the
|
||||
// modules the revision moves, ordered by the module graph's ordering edges among them (a base, a provision awaited from
|
||||
// another of them), and for each module what it waits on now: its build key, each node's want and have, and each
|
||||
// provision it awaits on each node, with the rule and the words of the part that holds it. Nothing in the mesh waits on
|
||||
// modules the revision moves, ordered by the module graph's ordering edges among them (a base, a provision or bus state
|
||||
// awaited from another of them, a verb another of them serves whose edge the graph keeps, ADR 0614 §1), and for each module
|
||||
// what it waits on now: its build key, each node's want (named by its edge: a verb, a reader capability) and have, and
|
||||
// each provision it awaits on each node, with the rule and the words of the part that holds it, and the verb edges the
|
||||
// graph leaves out. Nothing in the mesh waits on
|
||||
// a tier; the tiers say where the revision stands. A node the watchman says is offline is named away and left out of
|
||||
// what the revision waits on (ADR 0603 §7).
|
||||
|
||||
@@ -43,18 +45,22 @@ type Tiers struct {
|
||||
Away []string `json:"away,omitempty"`
|
||||
}
|
||||
|
||||
// TierModule is one module of a tier: its fingerprint, the modules of the same revision it waits for (After), and each
|
||||
// wait of its own now; no wait is a module that reached every node present.
|
||||
// TierModule is one module of a tier: its fingerprint, the modules of the same revision it waits for (After), each
|
||||
// wait of its own now (no wait is a module that reached every node present), and the verbs it invokes whose edge the
|
||||
// module graph leaves out on a cycle, so nothing waits on them (LeftOut, ADR 0614 §3).
|
||||
type TierModule struct {
|
||||
Module string `json:"module"`
|
||||
FP string `json:"fp,omitempty"`
|
||||
None string `json:"none,omitempty"`
|
||||
After []string `json:"after,omitempty"`
|
||||
Waits []TierWait `json:"waits,omitempty"`
|
||||
Module string `json:"module"`
|
||||
FP string `json:"fp,omitempty"`
|
||||
None string `json:"none,omitempty"`
|
||||
After []string `json:"after,omitempty"`
|
||||
Waits []TierWait `json:"waits,omitempty"`
|
||||
LeftOut []string `json:"left-out,omitempty"`
|
||||
}
|
||||
|
||||
// TierWait is one thing a module waits on: the edge (build, want, have, or provision <name>), the node it waits on (none
|
||||
// for its build), the assigner's rule for a want held back, the part's words and since when, where the part says it.
|
||||
// TierWait is one thing a module waits on: the edge (build, have, provision <name>, bus state, or for a want held back
|
||||
// want, verb <verb> or reader capability <capabilities> as the assigner's words name it, ADR 0614 §2, §4, §5), the node
|
||||
// it waits on (none for its build), the assigner's rule for a want held back, the part's words and since when, where the
|
||||
// part says it.
|
||||
type TierWait struct {
|
||||
Edge string `json:"edge"`
|
||||
Node string `json:"node,omitempty"`
|
||||
@@ -169,7 +175,7 @@ func (r Reader) Tiers(revision uint64) (Tiers, error) {
|
||||
out.Tiers = append(out.Tiers, nil)
|
||||
}
|
||||
m := moved[n]
|
||||
tm := TierModule{Module: n, FP: m.FP2, None: m.None2, After: after[n]}
|
||||
tm := TierModule{Module: n, FP: m.FP2, None: m.None2, After: after[n], LeftOut: gr[n].LeftOut}
|
||||
tm.Waits = r.waitsOf(n, m, gr[n], wants, provided, perr, away)
|
||||
out.Tiers[t] = append(out.Tiers[t], tm)
|
||||
}
|
||||
@@ -234,7 +240,11 @@ func (r Reader) waitsOf(module string, m delivery.Module, g graph.Node, wants, p
|
||||
if words == "" {
|
||||
words = "the want names " + short(w.FP) + ", not " + short(m.FP2)
|
||||
}
|
||||
out = append(out, TierWait{Edge: "want", Node: node, Rule: w.Rule, Words: words, Since: r.since(r.WantsState, mustWantKey(node, module))})
|
||||
edge := graph.EdgeOfWords(w.Words)
|
||||
if edge == "" {
|
||||
edge = "want"
|
||||
}
|
||||
out = append(out, TierWait{Edge: edge, Node: node, Rule: w.Rule, Words: words, Since: r.since(r.WantsState, mustWantKey(node, module))})
|
||||
} else if hw, ok := r.haveWait(node, module, m.FP2); ok {
|
||||
out = append(out, hw)
|
||||
}
|
||||
@@ -313,6 +323,9 @@ func (r Reader) haveWait(node, module, fp string) (TierWait, bool) {
|
||||
// provisionWaits are the waits of the provision a that module awaits on node: none once every key of it is provided.
|
||||
func provisionWaits(node, module, a string, provided map[string]json.RawMessage, perr error) []TierWait {
|
||||
edge := "provision " + a
|
||||
if a == graph.BusState {
|
||||
edge = "bus state" // a bus-state entry of the manifest (ADR 0614 §5)
|
||||
}
|
||||
if perr != nil {
|
||||
return []TierWait{{Edge: edge, Node: node, Words: "the provisioner's keys cannot be read: " + perr.Error()}}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user