From e5b3a894303a259be8ba9f58a40325aba1827c2c Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 11 Nov 2026 23:27:19 +0100 Subject: [PATCH] A-91: the operator-channel contract's asks value carries the ask's warrant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §700, A-91; the router's record carries its warrant Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h --- .../clients/operatorchannelclient/client.go | 5 +---- .../operatorchannelclient/client_test.go | 13 ++++++++++++ .../go/seats/operatorchannel/contract.go | 3 ++- .../go/seats/operatorchannel/contract_test.go | 2 ++ .../operator-channel/fixtures/states.json | 21 +++++++++++++++++++ .../seats/operator-channel/seat.json | 3 +++ .../ts/seats/operatorchannel/index.d.ts | 1 + 7 files changed, 43 insertions(+), 5 deletions(-) diff --git a/operator-and-conversation/go/clients/operatorchannelclient/client.go b/operator-and-conversation/go/clients/operatorchannelclient/client.go index 4acd445..01f974e 100644 --- a/operator-and-conversation/go/clients/operatorchannelclient/client.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/client.go @@ -106,10 +106,7 @@ func (c *Client) Where(id string) (state string, w *seat.Warrant, err error) { if err != nil || e == nil { return "", nil, err } - var rec struct { - State string `json:"state"` - Warrant *seat.Warrant `json:"warrant"` - } + var rec seat.AsksByAskerIDValue if err := json.Unmarshal(e.Value, &rec); err != nil { return "", nil, fmt.Errorf("the router's record of %s cannot be read: %w", id, err) } diff --git a/operator-and-conversation/go/clients/operatorchannelclient/client_test.go b/operator-and-conversation/go/clients/operatorchannelclient/client_test.go index 2007142..02d6bb0 100644 --- a/operator-and-conversation/go/clients/operatorchannelclient/client_test.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/client_test.go @@ -175,3 +175,16 @@ func TestAKeyPrefixIsPutBeforeEveryKeyOfTheClientsOwnStateAndNotBeforeWhere(t *t } } } + +// Design 47 G §700, A-91: Where reads the router's record as the contract's typed value, its warrant included. +func TestWhereReadsTheRecordsWarrantTyped(t *testing.T) { + c, _ := client() + rec := seat.AsksByAskerIDValue{Key: "an-asker.d-1", Asker: "an-asker", State: "chosen", + Warrant: &seat.Warrant{Ask: "d-1", Asker: "an-asker", Outcome: seat.WarrantOutcomeChosen, Option: "ok"}} + raw, _ := json.Marshal(rec) + c.Record = func(string) (*stdio.StateEntry, error) { return &stdio.StateEntry{Value: raw}, nil } + state, w, err := c.Where("d-1") + if err != nil || state != "chosen" || w == nil || w.Option != "ok" { + t.Fatalf("Where = %q, %+v, %v; want chosen and the record's warrant", state, w, err) + } +} diff --git a/operator-and-conversation/go/seats/operatorchannel/contract.go b/operator-and-conversation/go/seats/operatorchannel/contract.go index c27e0c9..f936017 100644 --- a/operator-and-conversation/go/seats/operatorchannel/contract.go +++ b/operator-and-conversation/go/seats/operatorchannel/contract.go @@ -541,5 +541,6 @@ type AsksByAskerIDValue struct { // when it was opened, RFC 3339 Opened string `json:"opened"` // how it stands: open, held-back (kept open behind the asker's newer asks, ADR 0666 §12), chosen, expired, cancelled, replaced or refused - State string `json:"state"` + State string `json:"state"` + Warrant *Warrant `json:"warrant,omitempty"` } diff --git a/operator-and-conversation/go/seats/operatorchannel/contract_test.go b/operator-and-conversation/go/seats/operatorchannel/contract_test.go index c389b3e..7a2e7bd 100644 --- a/operator-and-conversation/go/seats/operatorchannel/contract_test.go +++ b/operator-and-conversation/go/seats/operatorchannel/contract_test.go @@ -39,6 +39,8 @@ func TestFixtures(t *testing.T) { decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, {name: "an ask the router refused at take-in, closed (design 47 G 570)", subject: "", given: "{\"answer\":\"refused: its expiry passes the bound of its highest option level\",\"asker\":\"nats\",\"asker_told\":true,\"at\":\"2026-11-11T10:00:01Z\",\"expires\":\"2026-11-12T10:00:00Z\",\"headline\":\"Recreate a bus entry\",\"key\":\"nats.r-1\",\"opened\":\"2026-11-11T10:00:00Z\",\"state\":\"refused\"}", wire: "{\"answer\":\"refused: its expiry passes the bound of its highest option level\",\"asker\":\"nats\",\"asker_told\":true,\"at\":\"2026-11-11T10:00:01Z\",\"expires\":\"2026-11-12T10:00:00Z\",\"headline\":\"Recreate a bus entry\",\"key\":\"nats.r-1\",\"opened\":\"2026-11-11T10:00:00Z\",\"state\":\"refused\"}", decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, + {name: "an ask chosen, with its warrant in the router's record (design 47 G 700, A-91)", subject: "", given: "{\"asker\":\"mesh-delivery-pipeline\",\"expires\":\"2026-11-12T10:00:00Z\",\"headline\":\"Deliver a module?\",\"key\":\"mesh-delivery-pipeline.approval-1\",\"opened\":\"2026-11-11T10:00:00Z\",\"state\":\"chosen\",\"warrant\":{\"about\":\"mod.main\",\"ask\":\"approval-1\",\"ask-digest\":\"sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\",\"asker\":\"mesh-delivery-pipeline\",\"at\":\"2026-11-11T10:05:00Z\",\"option\":\"approve\",\"outcome\":\"chosen\"}}", wire: "{\"asker\":\"mesh-delivery-pipeline\",\"expires\":\"2026-11-12T10:00:00Z\",\"headline\":\"Deliver a module?\",\"key\":\"mesh-delivery-pipeline.approval-1\",\"opened\":\"2026-11-11T10:00:00Z\",\"state\":\"chosen\",\"warrant\":{\"about\":\"mod.main\",\"ask\":\"approval-1\",\"ask-digest\":\"sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\",\"asker\":\"mesh-delivery-pipeline\",\"at\":\"2026-11-11T10:05:00Z\",\"option\":\"approve\",\"outcome\":\"chosen\"}}", + decode: func(b []byte) (any, error) { var v AsksByAskerIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, {name: "notify asks the message sent on every channel", subject: "", given: "{\"every-channel\":true,\"key\":\"widens.mesh-delivery\",\"severity\":\"warning\",\"subject\":\"mesh-delivery\",\"summary\":\"a change widens what a module may do\"}", wire: "{\"every-channel\":true,\"key\":\"widens.mesh-delivery\",\"severity\":\"warning\",\"subject\":\"mesh-delivery\",\"summary\":\"a change widens what a module may do\"}", decode: func(b []byte) (any, error) { var v NotifyInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil}, } diff --git a/operator-and-conversation/seats/operator-channel/fixtures/states.json b/operator-and-conversation/seats/operator-channel/fixtures/states.json index 36e8ba0..f9c9f15 100644 --- a/operator-and-conversation/seats/operator-channel/fixtures/states.json +++ b/operator-and-conversation/seats/operator-channel/fixtures/states.json @@ -60,5 +60,26 @@ "opened": "2026-11-11T10:00:00Z", "state": "refused" } + }, + { + "name": "an ask chosen, with its warrant in the router's record (design 47 G 700, A-91)", + "of": "state:asks/.", + "given": { + "asker": "mesh-delivery-pipeline", + "expires": "2026-11-12T10:00:00Z", + "headline": "Deliver a module?", + "key": "mesh-delivery-pipeline.approval-1", + "opened": "2026-11-11T10:00:00Z", + "state": "chosen", + "warrant": { + "about": "mod.main", + "ask": "approval-1", + "ask-digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "asker": "mesh-delivery-pipeline", + "at": "2026-11-11T10:05:00Z", + "option": "approve", + "outcome": "chosen" + } + } } ] diff --git a/operator-and-conversation/seats/operator-channel/seat.json b/operator-and-conversation/seats/operator-channel/seat.json index 31f7db8..3c44a1b 100644 --- a/operator-and-conversation/seats/operator-channel/seat.json +++ b/operator-and-conversation/seats/operator-channel/seat.json @@ -312,6 +312,9 @@ "type": "string", "description": "when the ask was held back, RFC 3339; present while its state is held-back" }, + "warrant": { + "$ref": "#/defs/Warrant" + }, "answer": { "type": "string", "description": "how it ended, in words" diff --git a/operator-and-conversation/ts/seats/operatorchannel/index.d.ts b/operator-and-conversation/ts/seats/operatorchannel/index.d.ts index 3f91344..735429f 100644 --- a/operator-and-conversation/ts/seats/operatorchannel/index.d.ts +++ b/operator-and-conversation/ts/seats/operatorchannel/index.d.ts @@ -442,5 +442,6 @@ export interface AsksByAskerIDValue { "opened": string; /** how it stands: open, held-back (kept open behind the asker's newer asks, ADR 0666 §12), chosen, expired, cancelled, replaced or refused */ "state": string; + "warrant"?: Warrant; }