From ebbe886d81c0612181f01bc7872fa2883c957fd7 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 22 Dec 2025 00:40:02 -0800 Subject: [PATCH] Fix crew worker mail identity mismatch (gt-2xiv) Normalize crew/ and polecats/ to canonical form in addressToIdentity(): - gastown/crew/dave -> gastown/dave - gastown/polecats/Toast -> gastown/Toast Follows Postels Law: be liberal in what you accept. --- internal/mail/types.go | 33 +++++++++++++++++++++++++-------- internal/mail/types_test.go | 15 +++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/internal/mail/types.go b/internal/mail/types.go index a6d51004..ffe2ec25 100644 --- a/internal/mail/types.go +++ b/internal/mail/types.go @@ -276,13 +276,17 @@ func ParseMessageType(s string) MessageType { // addressToIdentity converts a GGT address to a beads identity. // -// Addresses use slash format matching directory structure: +// Liberal normalization: accepts multiple address formats and normalizes +// to canonical form (Postel's Law - be liberal in what you accept). +// +// Addresses use slash format: // - "mayor/" → "mayor/" // - "mayor" → "mayor/" // - "deacon/" → "deacon/" // - "deacon" → "deacon/" -// - "gastown/polecats/Toast" → "gastown/polecats/Toast" -// - "gastown/crew/max" → "gastown/crew/max" +// - "gastown/polecats/Toast" → "gastown/Toast" (normalized) +// - "gastown/crew/max" → "gastown/max" (normalized) +// - "gastown/Toast" → "gastown/Toast" (already canonical) // - "gastown/refinery" → "gastown/refinery" // - "gastown/" → "gastown" (rig broadcast) func addressToIdentity(address string) string { @@ -299,17 +303,25 @@ func addressToIdentity(address string) string { address = address[:len(address)-1] } - // Keep slashes - addresses map to directory structure + // Normalize crew/ and polecats/ to canonical form: + // "rig/crew/name" → "rig/name" + // "rig/polecats/name" → "rig/name" + parts := strings.Split(address, "/") + if len(parts) == 3 && (parts[1] == "crew" || parts[1] == "polecats") { + return parts[0] + "/" + parts[2] + } + return address } // identityToAddress converts a beads identity back to a GGT address. // -// Identity format matches address format (slash-based): +// Liberal normalization (Postel's Law): // - "mayor/" → "mayor/" // - "deacon/" → "deacon/" -// - "gastown/polecats/Toast" → "gastown/polecats/Toast" -// - "gastown/crew/max" → "gastown/crew/max" +// - "gastown/polecats/Toast" → "gastown/Toast" (normalized) +// - "gastown/crew/max" → "gastown/max" (normalized) +// - "gastown/Toast" → "gastown/Toast" (already canonical) // - "gastown/refinery" → "gastown/refinery" func identityToAddress(identity string) string { // Town-level agents ensure trailing slash @@ -320,6 +332,11 @@ func identityToAddress(identity string) string { return "deacon/" } - // Identity already in slash format, return as-is + // Normalize crew/ and polecats/ to canonical form + parts := strings.Split(identity, "/") + if len(parts) == 3 && (parts[1] == "crew" || parts[1] == "polecats") { + return parts[0] + "/" + parts[2] + } + return identity } diff --git a/internal/mail/types_test.go b/internal/mail/types_test.go index ff61e8ac..d253d948 100644 --- a/internal/mail/types_test.go +++ b/internal/mail/types_test.go @@ -13,9 +13,11 @@ func TestAddressToIdentity(t *testing.T) { {"deacon", "deacon/"}, {"deacon/", "deacon/"}, - // Rig-level agents: slash format matches directory structure - {"gastown/polecats/Toast", "gastown/polecats/Toast"}, - {"gastown/crew/max", "gastown/crew/max"}, + // Rig-level agents: crew/ and polecats/ normalized to canonical form + {"gastown/polecats/Toast", "gastown/Toast"}, + {"gastown/crew/max", "gastown/max"}, + {"gastown/Toast", "gastown/Toast"}, // Already canonical + {"gastown/max", "gastown/max"}, // Already canonical {"gastown/refinery", "gastown/refinery"}, {"gastown/witness", "gastown/witness"}, @@ -44,9 +46,10 @@ func TestIdentityToAddress(t *testing.T) { {"deacon", "deacon/"}, {"deacon/", "deacon/"}, - // Rig-level agents: identity == address (slash format) - {"gastown/polecats/Toast", "gastown/polecats/Toast"}, - {"gastown/crew/max", "gastown/crew/max"}, + // Rig-level agents: crew/ and polecats/ normalized + {"gastown/polecats/Toast", "gastown/Toast"}, + {"gastown/crew/max", "gastown/max"}, + {"gastown/Toast", "gastown/Toast"}, // Already canonical {"gastown/refinery", "gastown/refinery"}, {"gastown/witness", "gastown/witness"},