Serve a ledger owner its repairs rebuild, reindex, snapshot and lag as tools, its own ledgers only, marked acts or reads (issue 1966 T4)

This commit is contained in:
2026-11-03 13:46:54 +01:00
parent 63c1e3faa2
commit 8fe7ef3b82
6 changed files with 656 additions and 3 deletions
+63 -1
View File
@@ -52,6 +52,8 @@ type ListedTool struct {
Description string `json:"description"`
Input json.RawMessage `json:"input"`
Subjects []string `json:"subjects,omitempty"`
// Effect is the tool's mark where the runtime knows it (novox/hq ADR 0387 §1): its own tools, the ledger repairs.
Effect string `json:"effect,omitempty"`
}
// NodeRuntimeModule is the module that is a machine's runtime, serving every module on it.
@@ -133,6 +135,60 @@ type registration struct {
module string // the module's own name, or a seat's
owner string // the module whose bundle made it
tools []launch.Tool
// repairs says it is the runtime's own ledger repairs for the module, not a bundle's (ledgers.go).
repairs bool
}
// withRepairs is regs with the module's ledger repairs as its binding now says: added when it binds a ledger, removed
// when it binds none.
func withRepairs(regs []registration, repairs *registration) []registration {
out := make([]registration, 0, len(regs)+1)
for _, r := range regs {
if !r.repairs {
out = append(out, r)
}
}
if repairs != nil {
out = append(out, *repairs)
}
return out
}
// repairsOf is a module's ledger repairs as a registration, nil when its membership binds no ledger.
func (r *runner) repairsOf(module string) *registration {
if !hasLedger(r.conn, module) {
return nil
}
return &registration{module: module, owner: module, tools: r.events.repairTools(module), repairs: true}
}
// refreshRepairs serves each module's ledger repairs as its membership binds a ledger now, re-serving its tools where
// that changed (a binding is written when the bus module created the ledger, design 25 § 12.2).
func (r *runner) refreshRepairs() {
r.mu.Lock()
defer r.mu.Unlock()
changed := false
for _, m := range r.modules {
l := r.of[m]
served := false
for _, reg := range l.own {
served = served || reg.repairs
}
repairs := r.repairsOf(m)
if served == (repairs != nil) {
continue
}
for i := len(l.serving) - 1; i >= 0; i-- {
l.serving[i]()
}
l.serving, l.names = nil, nil
l.own, l.regs = withRepairs(l.own, repairs), withRepairs(l.regs, repairs)
r.serve(l)
changed = true
}
if changed {
r.bindVerbs()
}
}
// loadedModule is one served module as its bundles were last loaded: what they registered, what of it
@@ -230,6 +286,7 @@ func Run(conn *bus.Conn, served []Served, envs map[string]map[string]string, log
line += fmt.Sprintf("; not serving %s, whose bundle(s) failed to load", strings.Join(failedNames, ", "))
}
logf("%s", line)
conn.OnMembership(func(bus.Membership) { go func() { r.refreshRepairs(); conn.Flush() }() })
stopSeats, again := serveSeats(conn, r.modules, r.registrations, logf)
r.seatsAgain = again
r.stops = append(r.stops, stopSeats)
@@ -341,6 +398,9 @@ func (r *runner) load(s Served) *loadedModule {
}
}
}
if repairs := r.repairsOf(s.Module); repairs != nil {
l.regs, l.own = append(l.regs, *repairs), append(l.own, *repairs)
}
return l
}
@@ -411,7 +471,7 @@ func (r *runner) bindVerbs() {
answer := ToolsAnswer{Module: module, Tools: []ListedTool{}, Failed: why}
for _, t := range tools {
answer.Tools = append(answer.Tools, ListedTool{Name: t.Name, Description: t.Description,
Input: t.Input, Subjects: subjectsOf(r.conn, module, t.Name)})
Input: t.Input, Subjects: subjectsOf(r.conn, module, t.Name), Effect: t.Effect})
}
return answer, nil
})
@@ -682,6 +742,8 @@ type consumers struct {
seatBound map[string]func()
// provisioned is every watch a module's code holds of its values at runtime (provision.go).
provisioned provisionWatches
// ledgers is how a module's ledger is opened (ledgers.go).
ledgers ledgerSource
}
type moduleEvents struct {