mesh-conditions answers hand-back as gone and writes nothing: nothing reads the take-over key any more (ADR 0564 clause 9)

After the group the messenger and the watcher hear the condition keeper alone; a hand-back written to the take-over key
answered follow: controller and changed nothing. It is now answered as gone, naming its record, as placement's adopt and
hand-back are; the verb stays on the seat so a caller is told rather than not found.
Tests: none added; the hand-back lines of TestCompareCountsOnlyWhatBothSidesRaise become a row per lease state, each
answered as gone with the take-over key unwritten.

(issue 774, review g1 round 1)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 00:51:55 +01:00
parent 2877b87f29
commit e4382d016e
3 changed files with 19 additions and 23 deletions
@@ -4,8 +4,8 @@ package main
// lists the open keys of the keeper and of the controller, each difference with its reason; the kinds that end with the
// controller, the kinds only the keeper raises and the keys held while their module is delivered are listed apart and
// not counted. `take-over` raises an approve ask under the keeper's own name (ADR 0259), refused while compare shows a
// difference, and on the warrant writes the take-over key naming the keeper. `hand-back` writes it back to the
// controller until the lease write, and is refused after it.
// difference, and on the warrant writes the take-over key naming the keeper. `hand-back` is gone (issue 774): nothing
// reads the take-over key any more, so it is answered as gone and changes nothing.
import (
"encoding/json"
@@ -179,19 +179,12 @@ func (k *Keeper) Warranted(o asks.Option, w asks.Warrant) error {
return k.writeTakeover(health.Takeover{Follow: health.FollowKeeper, At: k.Now().UTC(), By: w.Says(), Ask: w.Ask})
}
// HandBack switches the readers back to the controller, until the lease write.
// HandBack is gone (issue 774): the readers of conditions hear the keeper alone and nothing reads the take-over key, so
// a hand-back written there would answer success and do nothing (ADR 0564 clause 9). It is answered as gone, naming the
// record, and changes nothing.
func (k *Keeper) HandBack(by, why string) (map[string]any, error) {
if strings.TrimSpace(why) == "" {
return nil, errors.New("a hand-back says why")
}
if k.LeaseAway() {
return nil, errors.New("refused: the controller's lease is handed away, so there is no controller to hand back to")
}
t := health.Takeover{Follow: health.FollowController, At: k.Now().UTC(), By: by, Why: why}
if err := k.writeTakeover(t); err != nil {
return nil, err
}
return map[string]any{"follow": t.Follow, "at": t.At}, nil
return nil, errors.New("hand-back is gone: the readers of conditions hear the condition keeper alone, and nothing " +
"reads the take-over key, so there is no controller to hand them back to (ADR 0564 clause 9)")
}
// writeTakeover writes the take-over key, and keeps it in history as the record of the switch.
@@ -265,7 +265,7 @@ func TestTheKeeperRaisesChangesAndClearsFromFindings(t *testing.T) {
// ADR 0564 clause 9 (issue 1952 C5, C8; the lead 2026-10-29): compare counts only what both sides can raise. A key held
// while its module is delivered, a kind that ends with the controller and a kind the controller never raises are listed
// apart; a kind with no home decided is a difference naming the gap; take-over is refused while compare differs, and
// hand-back after the lease write.
// hand-back is gone (issue 774, review g1): answered as gone, naming its record, and writing nothing.
func TestCompareCountsOnlyWhatBothSidesRaise(t *testing.T) {
r := newRig()
put := func(key, kind string) []byte {
@@ -335,11 +335,14 @@ func TestCompareCountsOnlyWhatBothSidesRaise(t *testing.T) {
if _, err := r.k.TakeOver(nil, "jochen", "now"); err == nil || !strings.Contains(err.Error(), "2 difference") {
t.Errorf("take-over while compare differs: %v", err)
}
if _, err := r.k.HandBack("jochen", "back"); err != nil {
t.Errorf("hand-back before the lease write: %v", err)
}
_ = r.k.Lease(true)
if _, err := r.k.HandBack("jochen", "back"); err == nil {
t.Error("hand-back after the lease write was taken")
for _, away := range []bool{false, true} {
_ = r.k.Lease(away)
if _, err := r.k.HandBack("jochen", "back"); err == nil || !strings.Contains(err.Error(), "hand-back is gone") ||
!strings.Contains(err.Error(), "ADR 0564 clause 9") {
t.Errorf("hand-back with the lease away %v: %v, want it answered as gone naming its record", away, err)
}
if _, _, found, _ := r.k.Store.Get(TakeoverState, health.TakeoverKey); found {
t.Errorf("hand-back with the lease away %v wrote the take-over key", away)
}
}
}
@@ -79,8 +79,8 @@ func Tools(k *Keeper, a Asks) []stdio.Tool {
Run: func(args map[string]any) (any, error) {
return k.TakeOver(a, stdio.CallerOf(args), strArg(args, "why"))
}},
{Name: Seat + ".hand-back", Description: "Switch the readers of conditions back to the controller, until its lease " +
"is handed away.",
{Name: Seat + ".hand-back", Description: "Gone: the readers of conditions hear the keeper alone, so there is nothing " +
"to hand back; answered as gone and changes nothing (ADR 0564 clause 9).",
Input: obj(map[string]any{"why": str("why")}, "why"),
Run: func(args map[string]any) (any, error) { return k.HandBack(stdio.CallerOf(args), strArg(args, "why")) }},
{Name: Seat + ".resync", Description: "Read every finding again and raise and clear what differs.",