The indexer writes each module's verb edges into the module graph, the ones on a cycle left out over the whole graph (ADR 0614 §2, §3; issue 774)
The assigner reads which edges are left out from the graph, so one place decides the cycle; the whole graph is judged, a module past the graph's cap included, before the first GraphMost are written. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
@@ -15,8 +15,8 @@ import (
|
||||
)
|
||||
|
||||
// The module graph (novox/hq ADR 0603 §2): at every recompute the indexer writes, in its state graph
|
||||
// (graph.Bucket), one key per module it holds the inputs of, its build and provision edges as that recompute
|
||||
// computed them, and deletes the key of a module the modules state no longer holds. A key is written only when its value
|
||||
// (graph.Bucket), one key per module it holds the inputs of, its build, provision and verb edges as that recompute
|
||||
// computed them (ADR 0614 §1), the verb edges on a cycle left out over the whole graph (§3), and deletes the key of a module the modules state no longer holds. A key is written only when its value
|
||||
// changes, so its history of 5 is five changes of the module's edges. The delivery view, the checker and the seat
|
||||
// mesh-catalog's verbs read it; nothing stores an order (ADR 0603 §1).
|
||||
|
||||
@@ -89,13 +89,20 @@ func (ix *Indexer) writeGraph(ctx context.Context, edges map[string]graphEdges)
|
||||
}
|
||||
return out
|
||||
}
|
||||
// The verb edges on a cycle are left out over the whole graph (ADR 0614 §3), every module it knows, so a module past
|
||||
// GraphMost still closes the cycle it lies on.
|
||||
whole := graph.Graph{}
|
||||
for name, e := range edges {
|
||||
whole[name] = graphNode(e, providers)
|
||||
}
|
||||
whole.LeaveOut()
|
||||
for _, name := range names {
|
||||
key, err := graph.Key(name)
|
||||
if err != nil {
|
||||
ix.logf("graph.%s is not written: %v", name, err)
|
||||
continue
|
||||
}
|
||||
next := graphNode(edges[name], providers)
|
||||
next := whole[name]
|
||||
err = write(ctx, ix.Graph, key, func(value []byte, found bool) ([]byte, bool, error) {
|
||||
if found {
|
||||
if cur, err := graph.Decode(value); err == nil && graph.Same(cur, next) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -330,6 +331,24 @@ func TestBasesAreComputedFirstAndNoFingerprintTakesTheBuilder(t *testing.T) { //
|
||||
t.Errorf("%s moved with a build of build-agent: fp2 = %q, want %s", name, m.FP2, fp)
|
||||
}
|
||||
}
|
||||
// ADR 0614 §2, §3: the graph names what each module invokes and serves, and leaves out, over the whole graph, the
|
||||
// verb edges of two modules calling each other, from either side; a third calling one of them keeps its edge.
|
||||
invoking := func(name, invokes string) mod {
|
||||
raw := renamed(t, "avahi", name)
|
||||
return mod{name, strings.Replace(raw, `"version": "1",`, `"version": "1", "invokes": [`+invokes+`],`, 1)}
|
||||
}
|
||||
vx, _ := newFormIndexer(t, invoking("caller", `"callee.avahi_status"`), invoking("callee", `"caller.avahi_browse", "*"`),
|
||||
invoking("third", `"callee.avahi_status"`))
|
||||
for name, want := range map[string][2][]string{
|
||||
"caller": {{"callee.avahi_status"}, {"callee.avahi_status"}},
|
||||
"callee": {{"caller.avahi_browse"}, {"caller.avahi_browse"}},
|
||||
"third": {{"callee.avahi_status"}, nil},
|
||||
} {
|
||||
if g := graphOf(t, vx, name); !reflect.DeepEqual(g.Invokes, want[0]) || !reflect.DeepEqual(g.LeftOut, want[1]) ||
|
||||
!slices.Contains(g.Serves, name+".avahi_status") {
|
||||
t.Errorf("graph.%s = %+v, want it to invoke %v and leave out %v", name, g, want[0], want[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestABuildHeardBeforeAnyToolchainIsReadDropsNoFingerprint(t *testing.T) { // 1727 (review: a restart flapped every FP2)
|
||||
|
||||
Reference in New Issue
Block a user