CT10: meshdeliveryclient reads the source per watched branch; SourceMainKey and the removed-verb rows retire

Builds: design 47 G §15, §305 (a reader names a branch's source revision: SourceKey, the
value SourceByBranchValue), §431 (the five verbs of release 6 leave the definition, so the
list RemovedVerbs and its test go; the settle ask goes with the verb settle).

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 11:05:56 +01:00
parent e1df6af536
commit 349a3519ef
5 changed files with 100 additions and 77 deletions
@@ -90,28 +90,9 @@ func ParseAskKey(key string) (kind, group, hash string, err error) {
return parts[0], group, parts[2], nil
}
// SettleGroupPrefix begins the group of a settle ask (novox/hq ADR 0556 clause 5): a person's ask to settle deliveries held
// because their merge reached the trunk without a passing check. It is kept in the asks under MergeAskKey like a merge ask,
// so the teller carries it unchanged. Git forbids a colon in a branch name, so no pull request's group ever begins with it.
const SettleGroupPrefix = "settle:"
// SettleGroup is the group of the settle ask of these delivery ids: the prefix and twelve hex digits of the sha256 over the
// ids, sorted and joined by a newline, so the same deliveries asked again keep one key.
func SettleGroup(ids []string) string {
sorted := append([]string(nil), ids...)
sort.Strings(sorted)
sum := sha256.Sum256([]byte(strings.Join(sorted, "\n")))
return SettleGroupPrefix + hex.EncodeToString(sum[:])[:12]
}
// AskVerb is the act the options of an ask bind, by its group: a settle ask's `mesh-delivery.settle`, any other
// `mesh-delivery.merge`. The gate composes the act with it and the teller checks a warrant against it.
func AskVerb(group string) string {
if strings.HasPrefix(group, SettleGroupPrefix) {
return seat.Seat + "." + seat.VerbSettle
}
return seat.Seat + "." + VerbMerge
}
// AskVerb is the act the options of an ask bind, `mesh-delivery.merge`; the settle ask went with the verb settle (design 47
// G §431). The gate composes the act with it and the teller checks a warrant against it.
func AskVerb(group string) string { return seat.Seat + "." + VerbMerge }
// AskID is the id the teller gives the router for the ask at askKey. The router's ids refuse dots, so in the readable part a
// dot becomes `-` and any other byte outside letters, digits, `_` and `-` becomes `_`; that part is cut to fit, and every id
@@ -1,8 +1,6 @@
package meshdeliveryclient
import (
"encoding/json"
"os"
"strings"
"testing"
@@ -40,10 +38,7 @@ func TestTheAsksAndOwedActsKeepTheirFormsAndRules(t *testing.T) {
if id := AskingID(key, 3); id != "merge-fix_1_2e2-d106c7950d28-3-ea06879e48d8" || len(id) > 64 {
t.Errorf("AskingID(3) = %q", id)
}
if g := SettleGroup([]string{"b", "a"}); g != "settle:7e18f737311b" || g != SettleGroup([]string{"a", "b"}) {
t.Errorf("SettleGroup = %q", g)
}
if AskVerb(SettleGroup([]string{"a"})) != "mesh-delivery.settle" || AskVerb("fix/1") != "mesh-delivery.merge" {
if AskVerb("fix/1") != "mesh-delivery.merge" {
t.Error("AskVerb binds the wrong act")
}
@@ -91,39 +86,3 @@ func TestTheAsksAndOwedActsKeepTheirFormsAndRules(t *testing.T) {
}
}
}
// The removed verbs are the optional rows of the seat's definition that say they were removed, each with the replacement
// the definition gives: the list and the definition cannot drift apart.
func TestTheRemovedVerbsAreTheDefinitionsRemovedRows(t *testing.T) {
raw, err := os.ReadFile("../../../seats/mesh-delivery/seat.json")
if err != nil {
t.Fatal(err)
}
var def struct {
Verbs []struct {
Name string `json:"name"`
Description string `json:"description"`
Optional bool `json:"optional"`
} `json:"verbs"`
}
if err := json.Unmarshal(raw, &def); err != nil {
t.Fatal(err)
}
removed := RemovedVerbs()
rows := 0
for _, v := range def.Verbs {
if !strings.HasPrefix(v.Description, RemovedPrefix) {
if _, in := removed[v.Name]; in {
t.Errorf("%s is in the removed list, the definition keeps it", v.Name)
}
continue
}
rows++
if want, in := removed[v.Name]; !in || !v.Optional || v.Description != RemovedPrefix+want {
t.Errorf("%s: in the list %v, optional %v, described %q, want %q", v.Name, in, v.Optional, v.Description, RemovedPrefix+want)
}
}
if rows != len(removed) || rows == 0 {
t.Errorf("the definition removes %d verbs, the list names %d", rows, len(removed))
}
}
@@ -31,8 +31,9 @@ const (
MergeRefused MergeState = "refused"
)
// SourceMainKey is the one key of the state source.
const SourceMainKey = "main"
// SourceKey is the key of the state source for a watched branch (design 47 *Watched branches and the catalog graph per
// branch*: source.<branch> in place of source.main).
func SourceKey(branch string) string { return seat.SourceByBranchKey(branch) }
func encodeValue(v any, check error, stamp func()) ([]byte, error) {
if check != nil {
@@ -76,7 +77,7 @@ func ParseTurnsKey(key string) (string, error) {
// ----- source
// CheckSource refuses a source naming no repository, a repository with no name or an empty head.
func CheckSource(s seat.SourceByMainValue) error {
func CheckSource(s seat.SourceByBranchValue) error {
if len(s.Heads) == 0 {
return fmt.Errorf("meshdeliveryclient: a source names no repository")
}
@@ -92,16 +93,16 @@ func CheckSource(s seat.SourceByMainValue) error {
}
// EncodeSource is s as the value of the key main.
func EncodeSource(s seat.SourceByMainValue) ([]byte, error) {
func EncodeSource(s seat.SourceByBranchValue) ([]byte, error) {
return encodeValue(&s, CheckSource(s), func() { s.V = Version })
}
// DecodeSource reads the value of the key main.
func DecodeSource(data []byte) (seat.SourceByMainValue, error) {
func DecodeSource(data []byte) (seat.SourceByBranchValue, error) {
if err := keys.CheckVersion(data); err != nil {
return seat.SourceByMainValue{}, err
return seat.SourceByBranchValue{}, err
}
s, err := seat.DecodeSourceByMainValue(data)
s, err := seat.DecodeSourceByBranchValue(data)
if err != nil {
return s, fmt.Errorf("meshdeliveryclient: a source does not fit its shape: %w", err)
}
@@ -109,7 +110,7 @@ func DecodeSource(data []byte) (seat.SourceByMainValue, error) {
}
// SameSource says whether two sources are the same.
func SameSource(a, b seat.SourceByMainValue) bool { return reflect.DeepEqual(a, b) }
func SameSource(a, b seat.SourceByBranchValue) bool { return reflect.DeepEqual(a, b) }
// ----- merge
@@ -41,9 +41,9 @@ func TestGateValuesEncodeAsGoDeliveryDid(t *testing.T) {
golden string
}{
{"source", func() ([]byte, error) {
return EncodeSource(seat.SourceByMainValue{Group: "fix/1.2", Heads: map[string]string{"novox/a": "aaaa", "novox/b": "bbbb"}})
return EncodeSource(seat.SourceByBranchValue{Group: "fix/1.2", Heads: map[string]string{"novox/a": "aaaa", "novox/b": "bbbb"}})
}, func(b []byte) (any, error) { return DecodeSource(b) },
seat.SourceByMainValue{V: 1, Group: "fix/1.2", Heads: map[string]string{"novox/a": "aaaa", "novox/b": "bbbb"}},
seat.SourceByBranchValue{V: 1, Group: "fix/1.2", Heads: map[string]string{"novox/a": "aaaa", "novox/b": "bbbb"}},
`{"v":1,"group":"fix/1.2","heads":{"novox/a":"aaaa","novox/b":"bbbb"}}`},
{"merge", func() ([]byte, error) {
return EncodeMerge(seat.MergesByGroupValue{Group: "g", Ask: "merge.g.0123456789ab", Approved: map[string]string{"novox/a@7": "h1"},
@@ -108,8 +108,39 @@ func TestGateKeysAsGoDeliveryHadThem(t *testing.T) {
if k := ApprovedKey("novox/a", 7); k != "novox/a@7" {
t.Error(k)
}
if SourceMainKey != seat.SourceByMainKey() {
t.Error("the source's key is the seat's")
if SourceKey("beta") != seat.SourceByBranchKey("beta") || SourceKey("beta") != "beta" {
t.Error("the source's key is the seat's, one per watched branch")
}
// A switched act's id carries its state, key and revision, and reads back; any other id is not one.
for _, c := range []struct {
state, key string
rev uint64
id string
}{
{SwitchedOwed, "novox.app.7.0123abcd.status=mesh/delivery", 4, "switched.owed.4.novox.app.7.0123abcd.status=mesh/delivery"},
{SwitchedMerges, "release-g", 12, "switched.merges.12.release-g"},
} {
id, err := SwitchedActID(c.state, c.key, c.rev)
if err != nil || id != c.id {
t.Errorf("SwitchedActID(%q, %q, %d) = %q, %v; want %q", c.state, c.key, c.rev, id, err, c.id)
}
if s, k, r, ok := ParseSwitchedActID(id); !ok || s != c.state || k != c.key || r != c.rev {
t.Errorf("ParseSwitchedActID(%q) = %q %q %d %v", id, s, k, r, ok)
}
}
for _, bad := range []struct {
state, key string
rev uint64
}{{"turns", "k", 1}, {SwitchedOwed, "", 1}, {SwitchedOwed, "a@b", 1}, {SwitchedMerges, "k", 0}} {
if id, err := SwitchedActID(bad.state, bad.key, bad.rev); err == nil {
t.Errorf("SwitchedActID(%+v) = %q, no error", bad, id)
}
}
for _, other := range []string{"novox.app.7.0123abcd.view@1700000000", "switched.turns.1.k", "switched.owed.x.k",
"switched.owed.0.k", "switched.owed.3", "switched.owed.3.k@1"} {
if _, _, _, ok := ParseSwitchedActID(other); ok {
t.Errorf("%q reads as a switched act's id", other)
}
}
}
@@ -150,7 +181,7 @@ func TestGateValuesRefuseWhatTheirRulesRefuse(t *testing.T) {
t.Errorf("%s: %v, want %q", name, c.err, c.want)
}
}
if _, err := EncodeSource(seat.SourceByMainValue{Group: "g"}); err == nil {
if _, err := EncodeSource(seat.SourceByBranchValue{Group: "g"}); err == nil {
t.Error("an empty source encodes")
}
if _, err := EncodeAsk(seat.AsksByMergeGroupHeadsValue{Group: "g", Ask: "text", Heads: []seat.AsksByMergeGroupHeadsValueHeadsItem{{Repository: "novox/a", Number: 1, Head: "h"}}}); err == nil {
@@ -0,0 +1,51 @@
package meshdeliveryclient
import (
"fmt"
"strconv"
"strings"
)
// A switched act is an act the gate owed before its switch to the delivery record (design 47 *The switch carries what is
// owed*): a key of the gate's state owed or merges that the old forge bridge had not performed. The gate says it as the
// event owed under an id made of the state, the key and its revision, so the new forge bridge counts it as performed when
// the old bridge's own bookkeeping names that key at that revision, and nothing is done twice. The form is
// `switched.<state>.<revision>.<key>`, the key last because it holds dots; it holds no `@`, which the gate's own ids hold,
// so the two never meet.
// The states a switched act comes from.
const (
SwitchedOwed = "owed"
SwitchedMerges = "merges"
)
const switchedPrefix = "switched."
// SwitchedActID is the id of the act the key of the state held at the revision.
func SwitchedActID(state, key string, revision uint64) (string, error) {
if state != SwitchedOwed && state != SwitchedMerges {
return "", fmt.Errorf("meshdeliveryclient: a switched act comes from the state owed or merges, not %q", state)
}
if key == "" || strings.Contains(key, "@") {
return "", fmt.Errorf("meshdeliveryclient: a switched act's key %q is empty or holds an @", key)
}
if revision == 0 {
return "", fmt.Errorf("meshdeliveryclient: a switched act's revision is 1 or more")
}
return switchedPrefix + state + "." + strconv.FormatUint(revision, 10) + "." + key, nil
}
// ParseSwitchedActID reads the state, the key and the revision out of a switched act's id; ok is false for any other id.
func ParseSwitchedActID(id string) (state, key string, revision uint64, ok bool) {
rest, found := strings.CutPrefix(id, switchedPrefix)
if !found || strings.Contains(id, "@") {
return "", "", 0, false
}
state, rest, _ = strings.Cut(rest, ".")
rev, key, _ := strings.Cut(rest, ".")
revision, err := strconv.ParseUint(rev, 10, 64)
if err != nil || revision == 0 || key == "" || (state != SwitchedOwed && state != SwitchedMerges) {
return "", "", 0, false
}
return state, key, revision, true
}