Merge branch 'feat/47-G-fix11-a3' into feat/47-G-join11

This commit is contained in:
2026-11-12 10:27:01 +01:00
38 changed files with 4303 additions and 240 deletions
+29 -2
View File
@@ -28,6 +28,7 @@ import (
"git.novox.be/novox/mesh-sdk/go/keys"
"git.novox.be/novox/mesh-sdk/go/views"
"github.com/novox/mesh-tools/node-tools/internal/approval"
"github.com/novox/mesh-tools/node-tools/internal/bus"
)
@@ -66,6 +67,10 @@ type Views struct {
cancel context.CancelFunc
wg sync.WaitGroup
deps Deps
// heard is closed and made again at every event heard, so a wait for the picture wakes on events, never on a timer.
heard chan struct{}
// onKey are the hearers of one key of an owner's state, by `<state> <key>` (OnSeatHolder).
onKey map[string][]func()
}
// FindingsState is the runner's own state the findings are written in.
@@ -120,7 +125,7 @@ func DepsOf(conn *bus.Conn, logf func(string, ...any)) Deps {
}
w, err := views.NewHeldWriter(views.WriterConfig[condseat.Finding, condseat.FindingCleared, condseat.FindingsStated, condseat.FindingsAskedEvent]{
Asked: condseat.SubjectFindingsAsked, Module: me, Node: conn.Node(), State: findingsState{conn: conn, module: me}, JS: js, Logf: logf,
Kinds: []string{views.UnansweredKind}})
Kinds: append([]string{views.UnansweredKind}, approval.Kinds...)})
if err != nil {
logf("[mesh-tools] the runner's findings are not written (%v): the first views' findings are in the log only", err)
return d
@@ -204,8 +209,10 @@ func StartWith(parent context.Context, d Deps, logf func(string, ...any)) (*View
return v, func() { stop(); cancel(); v.wg.Wait(); stopFindings() }
}
// hear applies one views event; a gap asks the first view once more.
// hear applies one views event; a gap asks the first view once more. Every event heard wakes what waits on the picture
// (heardOne).
func (v *Views) hear(env bus.Envelope) error {
defer v.heardOne()
v.mu.Lock()
hs := append([]*adoption(nil), v.hearers[env.Key]...)
v.mu.Unlock()
@@ -225,6 +232,7 @@ func (v *Views) hear(env bus.Envelope) error {
if f == nil || f.Owner != ev.Owner {
return nil
}
defer v.heardKey(ev.State, ev.Key)
if gap, why := f.Apply(ev); gap {
v.logf("[mesh-tools] a gap in %s's %s (%s): its first view %s is asked once more", f.Owner, f.State, why, f.Tool)
go func() { _ = f.Again(v.ctx, "a gap: "+why) }()
@@ -437,6 +445,25 @@ func (w wiring) Drop(owner, state string) error {
return nil
}
// Halves is the catalog half of each followed branch, by branch: its revision, with the seat definitions it was judged
// with, and its entries; mesh_wait checks an event name's emitter against them (design 34 every-event §14). An error says
// the half has no first view yet, never that no module exists.
func (v *Views) Halves() (map[string]meshcatalogclient.Half, error) {
if v.branches == nil {
return nil, fmt.Errorf("the tool runner does not follow the catalog half")
}
if err := v.branches.Ready(); err != nil {
return nil, err
}
out := map[string]meshcatalogclient.Half{}
for _, branch := range v.branches.Followed() {
if c, ok := v.branches.Catalog(branch); ok {
out[branch], _ = c.View() // a branch at none-yet is ready and empty (§614)
}
}
return out, nil
}
// Modules is every module the catalog half holds, by name, with its entry: the entries of the watched branches' newest trunk
// revisions, the branch followed first naming a module more than one hold (design 47 G §196, §594). An error says the half has
// no first view yet, never that no module exists.