// 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 }