COND6 (partial, not landed): the keeper's sources, holder and clears from the catalog half and the assignment half

The fixed Observers list and the engineModule constant are removed; sources are the modules whose catalog entry declares the
finding events, a module in no branch's revision is cleared (module left the catalog), a stopped module no longer placed is
cleared (module left the node), Keeper.Holder is derived from the entry's claims. The per-branch catalog followers in main.go
wait on a design question (how a part adopts one catalog view per registered branch); the whole integration test is red until then.

Builds: design 47 G §365, §196, §200, §364, §384

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 12:28:45 +01:00
parent 076f63f932
commit a24f7809a6
8 changed files with 329 additions and 82 deletions
@@ -0,0 +1,231 @@
package main
// The keeper finds its sources in the catalog (design 47 G §365): the modules whose catalog entry declares the finding
// events of §383, in any watched branch's newest revision. A module in no branch's revision has its conditions cleared with
// `module left the catalog`; a module's conditions on a node are cleared with `module left the node` once the node-engine
// says the module stopped there and the newest assignment graph revision no longer places it. No module is named here (§7).
import (
"encoding/json"
"slices"
"sort"
"strings"
catseat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
wants "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
engine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
)
// The reasons a catalog or an assignment takes a module's conditions away (design 47 G §365).
const (
whyLeftCatalog = "module left the catalog"
whyLeftNode = "module left the node"
)
// declaresFindings says whether the entry declares the finding events of §383 in its streams.
func declaresFindings(e catseat.CatalogEntry) bool {
if slices.ContainsFunc(e.Streams, func(s string) bool { return strings.Contains(s, EventFindingWritten) }) {
return true
}
streams, _ := e.Manifest["streams"].([]any)
for _, s := range streams {
m, _ := s.(map[string]any)
events, _ := m["events"].([]any)
for _, ev := range events {
if name, _ := ev.(string); strings.HasPrefix(name, EventFindingWritten) {
return true
}
}
}
return false
}
// CatalogHeld takes the newest catalog revision of one watched branch: the sources are the modules declaring the finding
// events in any branch, and the conditions of a module in no branch are cleared.
func (k *Keeper) CatalogHeld(branch string, entries map[string]catseat.CatalogEntry) error {
k.mu.Lock()
k.init()
k.halves[branch] = entries
var gone []string
for observer := range k.findings {
if observer == Module {
continue
}
if _, ok := k.entry(observer); !ok {
gone = append(gone, observer)
}
}
k.mu.Unlock()
sort.Strings(gone)
var first error
for _, observer := range gone {
k.clearObserver(observer, "", whyLeftCatalog, &first)
}
return first
}
// entry is the observer's entry in the first branch (in name order) that holds one. Called with mu held.
func (k *Keeper) entry(observer string) (catseat.CatalogEntry, bool) {
branches := make([]string, 0, len(k.halves))
for b := range k.halves {
branches = append(branches, b)
}
sort.Strings(branches)
for _, b := range branches {
if e, ok := k.halves[b][observer]; ok {
return e, true
}
}
return catseat.CatalogEntry{}, false
}
// isSource says whether the keeper takes findings from the module: its catalog entry declares the finding events.
func (k *Keeper) isSource(observer string) bool {
k.mu.Lock()
defer k.mu.Unlock()
k.init()
branches := make([]string, 0, len(k.halves))
for b := range k.halves {
branches = append(branches, b)
}
for _, b := range branches {
if e, ok := k.halves[b][observer]; ok && declaresFindings(e) {
return true
}
}
return false
}
// catalogHolder is Keeper.Holder: an observer whose catalog entry claims only seats held once for the mesh is held on the
// node the assignments name as the holder of one of them (design 47 G §384); unknown, it is taken from every node.
func (k *Keeper) catalogHolder(observer string) (string, bool) {
k.mu.Lock()
defer k.mu.Unlock()
k.init()
e, ok := k.entry(observer)
if !ok {
return "", false
}
claims, _ := e.Manifest["claims"].([]any)
if len(claims) == 0 {
return "", false
}
for _, c := range claims {
m, _ := c.(map[string]any)
if scope, _ := m["scope"].(string); scope != "mesh" {
return "", false
}
}
nodes := make([]string, 0, 1)
for hk, module := range k.holders {
if module == observer {
node, _, _ := strings.Cut(hk, ".")
nodes = append(nodes, node)
}
}
if len(nodes) == 0 {
return "", false
}
sort.Strings(nodes)
return nodes[0], true
}
// AssignmentHeld takes the newest assignment graph revision: the modules it places on each node.
func (k *Keeper) AssignmentHeld(rev wants.AssignmentRevision) error {
k.mu.Lock()
k.init()
k.placed = map[string]bool{}
for _, e := range rev.Entries {
k.placed[e.Node+"."+e.Module] = true
}
k.placedHeld = true
k.mu.Unlock()
return k.sweepStopped()
}
// EngineRunning takes a node-engine's `running` for a module on a node: stopped, it is kept until the assignment graph
// places the module there no longer.
func (k *Keeper) EngineRunning(node string, body []byte) error {
var r engine.Running
if err := json.Unmarshal(body, &r); err != nil || r.Module == "" || r.Node == "" {
k.Logf("the node-engine's running on %s is not readable: %v", node, err)
return nil
}
if node != "" && r.Node != node {
k.Logf("the node-engine's running on %s names the node %s: refused", node, r.Node)
return nil
}
k.mu.Lock()
k.init()
if r.Stopped {
k.stopped[r.Node+"."+r.Module] = true
} else {
delete(k.stopped, r.Node+"."+r.Module)
}
k.mu.Unlock()
return k.sweepStopped()
}
// sweepStopped clears the conditions of each module stopped on a node that the held assignment revision does not place
// there; before an assignment revision is held, nothing is cleared.
func (k *Keeper) sweepStopped() error {
k.mu.Lock()
k.init()
var left []string
if k.placedHeld {
for pair := range k.stopped {
if !k.placed[pair] {
left = append(left, pair)
}
}
}
for _, pair := range left {
delete(k.stopped, pair)
}
k.mu.Unlock()
sort.Strings(left)
var first error
for _, pair := range left {
node, module, _ := strings.Cut(pair, ".")
k.clearObserver(module, node, whyLeftNode, &first)
}
return first
}
// clearObserver clears every condition the module has, or has from the node when node is not empty, with the reason, and
// forgets what it heard of the module there.
func (k *Keeper) clearObserver(observer, node, why string, first *error) {
k.mu.Lock()
var keys []string
for key := range k.findings[observer] {
if node == "" || k.keyNode[observer][key] == node {
keys = append(keys, key)
}
}
for name, v := range k.views {
if v.observer == observer && (node == "" || v.node == node) {
for key := range v.Pending {
_ = k.Store.Delete(BoundsState, pendingKey(observer, v.node, key))
}
for ask := range v.Asks {
_ = k.Store.Delete(BoundsState, askedKey(observer, v.node, ask))
}
delete(k.views, name)
}
}
k.mu.Unlock()
sort.Strings(keys)
for _, key := range keys {
k.mu.Lock()
k.whys[key] = why
k.mu.Unlock()
if err := k.Finding(observer, key, nil, true); err != nil && *first == nil {
*first = err
}
k.mu.Lock()
delete(k.whys, key)
delete(k.keyNode[observer], key)
k.mu.Unlock()
}
}
@@ -14,6 +14,7 @@ import (
"strings"
"time"
engine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
cc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
watchdogs "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs"
@@ -47,15 +48,15 @@ func (k *Keeper) FindingEvent(e stdio.Envelope) error {
if _, ok := emitterOf(e.Key, watchdogs.EventMachineBack); ok && strings.HasPrefix(e.Key, watchdogs.Seat+".") {
return k.MachinePresence(e.Body, false)
}
if e.Key == engine.Seat+"."+engine.EventRunning {
return k.EngineRunning(e.Node, e.Body)
}
if observer, ok := emitterOf(e.Key, EventFindingsStated); ok {
return k.FindingsStated(observer, e.Node, e.Body)
}
return nil
}
// isSource says whether the keeper takes findings from the module.
func (k *Keeper) isSource(observer string) bool { return slices.Contains(Observers, observer) }
// FindingWritten raises or changes the condition of the finding an observer says on a node. A body that is not a finding
// the keeper can raise, or names another observer, is said in the journal and changes nothing.
func (k *Keeper) FindingWritten(observer, node string, body []byte) error {
@@ -199,7 +200,8 @@ func (k *Keeper) MachinePresence(body []byte, away bool) error {
// hear subscribes to the observers' events once the runtime serves, asked again until it takes each.
func hear(k *Keeper) {
patterns := []string{"*." + EventFindingWritten, "*." + EventFindingCleared, "*." + EventFindingsStated,
watchdogs.Seat + "." + watchdogs.EventMachineAway, watchdogs.Seat + "." + watchdogs.EventMachineBack}
watchdogs.Seat + "." + watchdogs.EventMachineAway, watchdogs.Seat + "." + watchdogs.EventMachineBack,
engine.Seat + "." + engine.EventRunning}
for _, pattern := range patterns {
go func() {
<-stdio.Served()
@@ -25,6 +25,7 @@ import (
cc "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
meshhealers "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers"
catseat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
)
// The modules whose states the keeper reads by name: the holders of the health seats (their seats' names are the
@@ -35,8 +36,6 @@ const (
healersModule = "mesh-healers"
// The holders of the assignments and the catalog, whose views and findings the keeper follows (the manifest test holds
// these equal to the manifest's `consumes` and `invokes`).
// engineModule is the node-engine, an observer of §387 (design 47 G §387 names mesh-host).
engineModule = "mesh-host"
assignerModule = "mesh-delivery-assigner"
indexerModule = "mesh-delivery-indexer"
)
@@ -55,23 +54,6 @@ const (
// HistoryKeptFor is how long a transition is kept (clause 3).
const HistoryKeptFor = 90 * 24 * time.Hour
// Observers are the modules whose `findings` the keeper reads (clause 2; ADR 0565 §8 for the assigner's; the lead's
// table of 2026-10-29 for the provisioner's and the router's; ADR 0571 clause 2 for the seat artifact-store's holder,
// which observes S25, image-missing).
var Observers = []string{watchmanModule, selfCheckModule, healersModule, assignerModule,
"mesh-provisioner", "messenger", artifactStore, configModule, vaultModule,
// The readers of the indexer's and the assigner's state say a first view unanswered past its bound in their own findings
// (ADR 0615 §6).
"mesh-watcher", "minio", "nfs-server", gitea, "mesh-build-go",
// The builder's, the indexer's (asked of its read tool and followed by its views events, ADR 0615 §1) and the other
// readers' of the indexer's and the assigner's state (the registry milestone's join).
builder, indexer, "mesh-delivery-checker", "mesh-delivery-teller", "mesh-delivery-view", enrolment,
// The tool runner's and claude-code's own first views (views.UnansweredFinding in their findings, ADR 0615 §6).
"node-tools", "claude-code",
// The node-engine's: a node that has read no seat list five minutes after its start, seat-list-unread (ADR 0636 §1;
// issue 1966: written on ace and raised by nobody).
engineModule}
// The other states it reads, as `<module>.<name>`.
const (
AttemptsRead = healersModule + "." + meshhealers.StateAttempts
@@ -158,6 +140,13 @@ type Keeper struct {
Record func(what, why, verb string) error
// away are the machines the watchman says are away (design 47 G §389).
away map[string]bool
// halves are the newest catalog revisions of the watched branches, by branch and module (catalog.go); placed are the
// `<node>.<module>` the newest assignment graph revision places, placedHeld whether one is held; stopped are the
// `<node>.<module>` the node-engines say stopped.
halves map[string]map[string]catseat.CatalogEntry
placed map[string]bool
placedHeld bool
stopped map[string]bool
}
func (k *Keeper) init() {
@@ -168,6 +157,7 @@ func (k *Keeper) init() {
map[string]string{}, map[string]held{}, map[string]clearedMemo{}
k.passed, k.outOfHold, k.holders = map[string]string{}, map[string]string{}, map[string]string{}
k.assignments = assignerclient.NewKeys()
k.halves, k.placed, k.stopped = map[string]map[string]catseat.CatalogEntry{}, map[string]bool{}, map[string]bool{}
k.views, k.keyNode, k.whys = map[string]*sourceView{}, map[string]map[string]string{}, map[string]string{}
}
if k.Now == nil {
@@ -3,10 +3,9 @@ package main
import (
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
catseat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
meshwants "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
"slices"
"sort"
"strings"
@@ -112,9 +111,27 @@ func newRig() *rig {
r.said = append(r.said, e)
return nil
}}
// The catalog half of the watched branch: the modules the tests hear findings from declare the finding events (§365);
// the self-check's holder claims a seat held once for the mesh.
r.half(watchmanModule, "node")
r.half(selfCheckModule, "mesh")
return r
}
// half puts a module in the watched branch's catalog half, declaring the finding events and claiming one seat of scope.
func (r *rig) half(module, scope string) {
r.k.mu.Lock()
r.k.init()
if r.k.halves["main"] == nil {
r.k.halves["main"] = map[string]catseat.CatalogEntry{}
}
r.k.halves["main"][module] = catseat.CatalogEntry{Module: module, Manifest: map[string]any{
"claims": []any{map[string]any{"name": module, "scope": scope}},
"streams": []any{map[string]any{"name": module + "-findings", "events": []any{"finding-written.*", "finding-cleared.*", "findings-stated.*"}}},
}}
r.k.mu.Unlock()
}
func (r *rig) changes() string {
var out []string
for _, e := range r.said {
@@ -346,10 +363,6 @@ func TestEveryControllerKindHasItsHome(t *testing.T) {
if h, ok := Homes[kind]; !ok || h.Observer != observer || h.Carried {
t.Errorf("%s's home is %+v", kind, h)
}
// The keeper reads every observer's findings that a kind's home names (the registry milestone's join).
if !slices.Contains(Observers, observer) {
t.Errorf("%s's observer %s is not read by the keeper", kind, observer)
}
}
for _, kind := range []string{"resolver-wrong", "holder-silent", "archives-missing", "archives-unheld", "binding-kept",
"binding-moved", "binding-moving", "grant-composes-nothing", "root-not-free", "cleanup-waiting", "data-held-twice"} {
@@ -370,18 +383,6 @@ func TestEveryControllerKindHasItsHome(t *testing.T) {
t.Errorf("%s's home is %+v", kind, h)
}
}
// Each module that writes a reader's unanswered first view (ADR 0615 §6), or that a delivery part's consumer stopped
// hearing, is read: the delivery parts and the tool runner by name (other repositories), and every catalogue module
// whose code writes a views finding, found in the catalogue's tree, so a new writer cannot go unobserved.
// And the node-engine, whose node writes seat-list-unread when it has read no seat list (ADR 0636 §1, issue 1966).
readers := []string{watchman, "mesh-delivery-teller", enrolment, builder, "mesh-delivery-checker", "mesh-delivery-view",
indexer, assignerModule, configModule, vaultModule, gitea, "node-tools", "mesh-host"}
readers = append(readers, viewFindingWriters(t, "../../..")...)
for _, reader := range readers {
if !slices.Contains(Observers, reader) {
t.Errorf("the reader %s's findings are not read by the keeper", reader)
}
}
// 19 of ADR 0564, the 63 of the table, plan-failed and stalled, mesh-config's four (design 32), the readers' unanswered
// first view, the owners' view too large, the builder's four, the indexer's four and the package seats' token refused.
// And the builder's three publishing kinds and the delivery parts' family not heard: 103.
@@ -393,40 +394,6 @@ func TestEveryControllerKindHasItsHome(t *testing.T) {
}
}
// viewFindingWriters are the catalogue modules (under modules) whose non-test Go code writes a views finding into its own
// findings: views.UnansweredFinding, views.TooLargeFinding, views.UnsaidFinding or views.OwnFindings.
func viewFindingWriters(t *testing.T, modules string) []string {
t.Helper()
writes := regexp.MustCompile(`(views\.(UnansweredFinding|TooLargeFinding|UnsaidFinding)|meshconditionsclient\.FindingWriter)\b`)
var out []string
entries, err := os.ReadDir(modules)
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
if !e.IsDir() || e.Name() == Module {
continue
}
found := false
_ = filepath.WalkDir(filepath.Join(modules, e.Name()), func(path string, d fs.DirEntry, err error) error {
if err != nil || found || d.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
return nil
}
if src, err := os.ReadFile(path); err == nil && writes.Match(src) {
found = true
}
return nil
})
if found {
out = append(out, e.Name())
}
}
if len(out) < 5 {
t.Fatalf("found %d catalogue modules writing views findings in %s: the tree was not read (%v)", len(out), modules, out)
}
return out
}
// ADR 0634 §1, §4 (issue 1966): the keeper reads a condition written before the seat named its layout (the controller's
// body: `silenced` null, `actions` empty, the times with an offset) and writes it back in the seat's layout.
func TestTheKeeperReadsTheOldLayoutAndWritesTheSeatsLayout(t *testing.T) {
@@ -569,6 +536,50 @@ func TestTheKeeperSetsItsViewFromWhatAnObserverStates(t *testing.T) {
}
return nil
}, []string{"machine.d.silent", "machine.g.silent"}, ""},
// Design 47 G §365: the sources are the catalog's, a module that left the catalog or the node loses its conditions.
{"a module not declaring the finding events is no source, one the catalog half declares is heard with no code change", func() error {
if err := r.k.FindingWritten("fixture", "ace", mustJSON(t, f("fixture", "z"))); err != nil {
return err
}
if _, ok := r.open("machine.z.silent"); ok {
t.Errorf("a module in no catalog half raised a condition")
}
r.half("fixture", "node")
return r.k.FindingWritten("fixture", "ace", mustJSON(t, f("fixture", "z")))
}, []string{"machine.d.silent", "machine.g.silent", "machine.z.silent"}, ""},
{"an observer claiming only mesh seats is held where the assignments place it, with no fixed name", func() error {
r.k.holders["g14.mesh-self-check"] = selfCheck
if node, once := r.k.catalogHolder(selfCheck); node != "g14" || !once {
t.Errorf("the holder of %s is %q, %v", selfCheck, node, once)
}
if _, once := r.k.catalogHolder("fixture"); once {
t.Errorf("a node seat's module is held once")
}
return nil
}, []string{"machine.d.silent", "machine.g.silent", "machine.z.silent"}, ""},
{"stopped but still placed, or placed by no revision held, clears nothing", func() error {
body := mustJSON(t, nodeengine.Running{Module: "fixture", Node: "ace", Stopped: true, At: r.now})
if err := r.k.EngineRunning("ace", body); err != nil {
return err
}
return r.k.AssignmentHeld(meshwants.AssignmentRevision{Entries: []meshwants.NodeEntryRef{{Module: "fixture", Node: "ace"}}})
}, []string{"machine.d.silent", "machine.g.silent", "machine.z.silent"}, ""},
{"stopped and no longer placed is cleared as left the node", func() error {
return r.k.AssignmentHeld(meshwants.AssignmentRevision{Revision: 2})
}, []string{"machine.d.silent", "machine.g.silent"}, whyLeftNode},
{"an assignment revision alone clears nothing, and a module that left the catalog is cleared", func() error {
if err := r.k.FindingWritten("fixture", "ace", mustJSON(t, f("fixture", "z"))); err != nil {
return err
}
r.said = nil
if err := r.k.AssignmentHeld(meshwants.AssignmentRevision{Revision: 3}); err != nil {
return err
}
if _, ok := r.open("machine.z.silent"); !ok {
t.Errorf("an assignment revision alone cleared a module's condition")
}
return r.k.CatalogHeld("main", map[string]catseat.CatalogEntry{watchmanModule: {}, selfCheckModule: {}})
}, []string{"machine.d.silent", "machine.g.silent"}, whyLeftCatalog},
}
for _, s := range steps {
if err := s.do(); err != nil {
@@ -18,6 +18,8 @@ import (
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient"
meshwants "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants"
"git.novox.be/novox/mesh-sdk/go/views"
)
@@ -59,7 +61,9 @@ func sayOnSeat(e Event) error {
// NewKeeper is the keeper over the runtime.
func NewKeeper() *Keeper {
return &Keeper{Store: runtimeStore{}, Say: sayOnSeat, Now: time.Now, Logf: logf}
k := &Keeper{Store: runtimeStore{}, Say: sayOnSeat, Now: time.Now, Logf: logf}
k.Holder = k.catalogHolder
return k
}
// watch keeps a watch of one state, said blind while it cannot be read: a state not on the bus yet (an observer not
@@ -146,6 +150,10 @@ func NewViews(k *Keeper) *views.Reader {
v := &views.Reader{Module: Module, Logf: logf, Findings: ownFindings{k}, Acts: meshhandactsclient.Recorder{Ask: stdio.Ask}}
v.Follow(assignerModule, "wants", WantsTool)
v.Follow(assignerModule, "assignments", AssignmentsTool)
// The newest assignment graph revision, which says where a module that stopped is placed no more (design 47 G §365).
if _, err := meshwantsclient.WireAssignment(v, stdio.Ask, func(rev meshwants.AssignmentRevision) { _ = k.AssignmentHeld(rev) }); err != nil {
logf("the assignment half is not followed: %v", err)
}
return v
}
@@ -36,9 +36,9 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) {
// The assigner's wants and assignments are asked once of their read tools and followed by their events (ADR 0615 §1);
// the observers' findings are heard as events and no `findings` is read (design 47 G §383).
reads := []string{HavesRead, AttemptsRead}
if !slices.Equal(m.Invokes, []string{WantsTool, AssignmentsTool, "seat:mesh-hand-acts.hand-act"}) ||
if !slices.Equal(m.Invokes, []string{WantsTool, AssignmentsTool, "seat:mesh-hand-acts.hand-act", "seat:mesh-wants.revision"}) ||
!slices.Equal(m.Consumes, []string{"mesh-delivery-assigner.views.wants", "mesh-delivery-assigner.views.assignments",
"*.finding-written", "*.finding-cleared", "*.findings-stated", "mesh-watchdogs.machine-away", "mesh-watchdogs.machine-back"}) {
"*.finding-written", "*.finding-cleared", "*.findings-stated", "mesh-watchdogs.machine-away", "mesh-watchdogs.machine-back", "mesh-wants.assignment-revision", "node-engine.running"}) {
t.Errorf("invokes %v, consumes %v", m.Invokes, m.Consumes)
}
for _, r := range m.Reads {
+2
View File
@@ -12,6 +12,8 @@ require (
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/clients/meshwantsclient v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshwants v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000
github.com/nats-io/nats.go v1.54.0
+5 -2
View File
@@ -20,7 +20,8 @@
"invokes": [
"mesh-delivery-assigner.assigner_read_wants",
"mesh-delivery-assigner.assigner_read_assignments",
"seat:mesh-hand-acts.hand-act"
"seat:mesh-hand-acts.hand-act",
"seat:mesh-wants.revision"
],
"consumes": [
"mesh-delivery-assigner.views.wants",
@@ -29,7 +30,9 @@
"*.finding-cleared",
"*.findings-stated",
"mesh-watchdogs.machine-away",
"mesh-watchdogs.machine-back"
"mesh-watchdogs.machine-back",
"mesh-wants.assignment-revision",
"node-engine.running"
],
"state": [
"conditions",