messenger: ask the root-free word fresh before taking an ask

A word kept from a moment ago, not free, refused an ask taken just after root became free again, and
the asker keeps a refusal until something changes. Taking an ask asks the controller fresh, still
before the work is taken (the lab's proof found it).
This commit is contained in:
2026-10-09 17:08:17 +02:00
parent d136806745
commit 3826741f62
4 changed files with 9 additions and 8 deletions
+4 -3
View File
@@ -313,7 +313,8 @@ func (h *Holder) rootFree(k KindInfo, cached bool) string {
// warmRootFree asks the controller's root-free word for every kind that proves its sender, outside the work,
// so what is rendered and offered inside it reads a recent word.
func (h *Holder) warmRootFree() {
// force asks even a recent word again: taking an ask, whose refusal its asker keeps until something changes.
func (h *Holder) warmRootFree(force bool) {
if h.Bench == nil {
return
}
@@ -329,7 +330,7 @@ func (h *Holder) warmRootFree() {
h.mu.Lock()
kept, ok := h.rootWord[key]
h.mu.Unlock()
if ok && now.Sub(kept.at) >= 0 && now.Sub(kept.at) < RootFreeKept/2 {
if !force && ok && now.Sub(kept.at) >= 0 && now.Sub(kept.at) < RootFreeKept/2 {
continue // recent enough: asked again when half its time has gone
}
_ = h.rootFree(k, false)
@@ -396,7 +397,7 @@ const ConditionOwner = "mesh-controller"
// grants that module alone. An error is returned only when the ask could not be recorded, so the queue
// offers it again; a refusal is said to the asker and the work is taken.
func (h *Holder) TakeAsk(verb, asker string, body json.RawMessage) error {
h.warmRootFree() // before the work: offering an approval reads the recent word inside it
h.warmRootFree(true) // fresh, before the work: an ask refused for no channel stays refused with its asker
h.work.Lock()
defer h.work.Unlock()
h.mu.Lock()
+2 -2
View File
@@ -554,7 +554,7 @@ func (h *Holder) cleared(key string, quiet bool) {
// Tick does what time asks: reminders, retries, what is held, and forgetting what cleared long
// enough ago that a new raise is a new message.
func (h *Holder) Tick() {
h.warmRootFree() // outside the work: what is offered inside it reads a recent word
h.warmRootFree(false) // outside the work: what is offered inside it reads a recent word
h.work.Lock()
defer h.work.Unlock()
h.mu.Lock()
@@ -610,7 +610,7 @@ func (h *Holder) Tick() {
// Flush sends what is due on each channel; the runtime calls it every few seconds, so a held
// message waits its window and not a minute more.
func (h *Holder) Flush() {
h.warmRootFree() // outside the work: what is offered inside it reads a recent word
h.warmRootFree(false) // outside the work: what is offered inside it reads a recent word
h.work.Lock()
defer h.work.Unlock()
h.mu.Lock()
+2 -2
View File
@@ -186,7 +186,7 @@ func (h *Holder) offerDeskUnlink(id Identity) bool {
// Link takes a link asked for on a kind: a code shown where only the operator sees it, or a refusal said on
// the kind.
func (h *Holder) Link(kind string, l asks.LinkAsked) string {
h.warmRootFree() // outside the work: a question it asks reads a recent word
h.warmRootFree(false) // outside the work: a question it asks reads a recent word
h.work.Lock()
defer h.work.Unlock()
reply := func(title, body string) string {
@@ -277,7 +277,7 @@ func (h *Holder) Link(kind string, l asks.LinkAsked) string {
// Prove answers a code sent on a kind: the link it confirms, or why not. Never said in an event.
func (h *Holder) Prove(kind string, c asks.Code) asks.ProofAnswer {
h.warmRootFree() // outside the work: a question it asks reads a recent word
h.warmRootFree(false) // outside the work: a question it asks reads a recent word
h.work.Lock()
defer h.work.Unlock()
no := func(words string) asks.ProofAnswer { return asks.ProofAnswer{Accepted: false, Words: words} }
+1 -1
View File
@@ -141,7 +141,7 @@ func (h *Holder) selfAct(rec AskRecord, w asks.Warrant) {
// Unlink asks the account linked on a kind whether it is to be unlinked: an approve ask put to it alone.
func (h *Holder) Unlink(kind string) (map[string]any, error) {
h.warmRootFree() // outside the work: a question it asks reads a recent word
h.warmRootFree(false) // outside the work: a question it asks reads a recent word
h.work.Lock()
defer h.work.Unlock()
ids := h.identitiesOf(kind)