On 2026-10-31 two merge asks of pull requests merged by another path held two of the router's three places for the teller and a placement ask was refused for want of room. The gate's fix ends such asks by itself; this tool is the recovery for any ask that still outlives its question: it cancels the open ask at the router, or drops one waiting for room, by its router id or its key, and logs who and why. The tools bundle asks through the teller's new state ends, as indexer_take_in does.
80 lines
3.2 KiB
Go
80 lines
3.2 KiB
Go
// Package ends is the teller's recovery tool teller_end_ask and the value it writes (issue 774; the instruction "tools are
|
|
// how a module is spoken to"). An ask the teller holds open at the router keeps one of the three places the router gives
|
|
// its asker (asks.MostOpen) until it is answered, it lapses, or the gate or the assigner takes its key away. When that last
|
|
// never comes (on 2026-10-31 two merge asks of pull requests merged by another path held two places for a day, and a
|
|
// placement ask was refused for want of room), a person ends the ask by hand. The teller's process serves no request
|
|
// subject (ADR 0437 §4), so the tool writes an end into the teller's state ends, which the teller watches, carries out
|
|
// and answers there, as the indexer answers indexer_take_in.
|
|
package ends
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
const (
|
|
Tool = "teller_end_ask"
|
|
// State is the teller's state of ends asked by hand: the bucket mesh-delivery-teller_ends.
|
|
State = "ends"
|
|
Bucket = delivery.TellerModule + "_" + State
|
|
)
|
|
|
|
// Result is the teller's answer to an end: which ask it found (its router id and the gate's or the assigner's key it was
|
|
// asked for), whether this end ended it, and what it did in words.
|
|
type Result struct {
|
|
ID string `json:"id,omitempty"`
|
|
Key string `json:"key,omitempty"`
|
|
Ended bool `json:"ended"`
|
|
Says string `json:"says"`
|
|
}
|
|
|
|
// End is a value of the state ends: the ask named (a router id, as operator-channel's asks show it, or the key of the
|
|
// gate's or the assigner's asks it was asked for), why, who and when, and, once the teller carried it out, its answer.
|
|
type End struct {
|
|
V int `json:"v"`
|
|
Ask string `json:"ask"`
|
|
Why string `json:"why"`
|
|
By string `json:"by,omitempty"`
|
|
At time.Time `json:"at"`
|
|
Result *Result `json:"result,omitempty"`
|
|
Answered time.Time `json:"answered,omitzero"`
|
|
}
|
|
|
|
// Done says the end is answered.
|
|
func (e End) Done() bool { return !e.Answered.IsZero() && e.Result != nil }
|
|
|
|
// Decode reads a value of the state ends.
|
|
func Decode(data []byte) (End, error) {
|
|
var e End
|
|
if err := json.Unmarshal(data, &e); err != nil {
|
|
return e, err
|
|
}
|
|
if e.V != 1 {
|
|
return e, fmt.Errorf("an ends value of version %d, not 1", e.V)
|
|
}
|
|
if e.Ask == "" || e.Why == "" {
|
|
return e, errors.New("an ends value naming no ask or no why")
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
// New is the value teller_end_ask writes: its key and the value. The key is the router's spelling of the name given
|
|
// (delivery.AskID), which a bucket key takes whatever the name holds, and is the same for the same name, so a second end
|
|
// of one ask overwrites the first.
|
|
func New(ask, why, by string, at time.Time) (string, []byte, error) {
|
|
ask, why = strings.TrimSpace(ask), strings.TrimSpace(why)
|
|
if ask == "" {
|
|
return "", nil, errors.New("name the ask to end (ask): its router id, as operator-channel's asks show it, or the key of the gate's or the assigner's asks")
|
|
}
|
|
if why == "" {
|
|
return "", nil, errors.New("say why the ask is ended (why), in plain words: it is logged with who ended it")
|
|
}
|
|
data, err := json.Marshal(End{V: 1, Ask: ask, Why: why, By: by, At: at.UTC()})
|
|
return delivery.AskID(ask), data, err
|
|
}
|