Say again what was last applied when the mesh asks (hq to-be 45 Phase 3, the report verb)

The controller's healer H1 answers a send that went unreported by asking the
machine first: a report lost on its way (issue 264) needs no second send. The
node-engine now hears mesh.node.<self>.ask.report on core NATS and enqueues a
reconcile whose account is said whether or not it is news; a delivery waiting
meanwhile is applied and reported instead. The answer is an ordinary report on
its own subject, so the node publishes nothing new and answers nobody's inbox.

The genesis lock grants the controller the healer-acted seat event, which it
now composes; the genesis test in mesh-controller holds the two equal.
This commit is contained in:
jochen
2026-10-06 14:05:02 +02:00
parent b2808549ee
commit 6e1bcdbde4
6 changed files with 214 additions and 8 deletions
+36 -7
View File
@@ -64,8 +64,11 @@ type Queue struct {
delivering bool
waiting []Declaration // delivered and not yet taken, in the order they arrived
due bool // a reconcile asked for
bus Bus // the link open now; nil while there is none
unasked *Report // a reconcile's report not yet said, made while there was no link
// asked is the mesh asking this node to say what it last applied (the `report` verb, to-be 45 §6):
// the reconcile it enqueues says its report whether or not it is news.
asked bool
bus Bus // the link open now; nil while there is none
unasked *Report // a reconcile's report not yet said, made while there was no link
// saying is held while a report is made and said, so the order they leave in is the order they
// are made in — whoever says them, the worker or a link opening.
@@ -116,6 +119,19 @@ func (q *Queue) ReconcileDue() {
q.signal()
}
// ReportAsked is the mesh asking this node to say again what it last applied (novox/hq to-be 45 §6, the
// `report` verb its healer H1 asks when a send went unreported). **An enqueue, like everything else**: a
// reconcile, whose report — the account of the declaration this node keeps, the last it applied — is
// said whether or not it is news. A delivery waiting meanwhile is applied instead and reported, which
// answers the question better. Asked again before the worker takes it is the same question.
func (q *Queue) ReportAsked() {
q.init()
q.mu.Lock()
q.due, q.asked = true, true
q.mu.Unlock()
q.signal()
}
func (q *Queue) signal() {
select {
case q.wake <- struct{}{}:
@@ -147,8 +163,8 @@ func (q *Queue) step(ctx context.Context) bool {
q.mu.Unlock()
return false
}
batch, due := q.waiting, q.due
q.waiting, q.due = nil, false
batch, due, asked := q.waiting, q.due, q.asked
q.waiting, q.due, q.asked = nil, false, false
q.delivering = len(batch) > 0
q.mu.Unlock()
defer func() {
@@ -159,7 +175,7 @@ func (q *Queue) step(ctx context.Context) bool {
}()
if len(batch) == 0 {
q.reconcile(ctx)
q.reconcile(ctx, asked)
return true
}
@@ -210,14 +226,27 @@ func (q *Queue) step(ctx context.Context) bool {
return true
}
func (q *Queue) reconcile(ctx context.Context) {
func (q *Queue) reconcile(ctx context.Context, asked bool) {
if q.Reconcile == nil {
if asked {
q.Say("asked to say what this node last applied, and it holds nothing to reconcile with")
}
return
}
report, ok := q.Reconcile(ctx)
if !ok || q.News == nil || !q.News(report) {
if !ok {
if asked {
q.Say("asked to say what this node last applied, and the reconcile could not say it")
}
return
}
if !asked && (q.News == nil || !q.News(report)) {
return
}
if asked {
q.Say(fmt.Sprintf("asked by the mesh, saying again what this node last applied: declaration %s (%s)",
short(report.Declared), report.Order.Words()))
}
q.tell(ctx, report, unasked)
}