carlos / platform Public

console: region API messages, as approved

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 89802b7646c647ec380c3444370016d7e596b030 parent 97a0a41
2 files changed, +223 −0
  • internal/console/regionmsgs.go +96 −0
  • internal/console/regionmsgs_test.go +127 −0
diff --git a/internal/console/regionmsgs.go b/internal/console/regionmsgs.go
new file mode 100644
index 000000000..9b3504436
--- /dev/null
+++ b/internal/console/regionmsgs.go
@@ -0,0 +1,96 @@
+package console
+
+import (
+ "fmt"
+ "strings"
+)
+
+// The nearest-region feature's API and operator messages (spec §19), as
+// approved in the nearest-region copy record. Change none of them without a
+// new copy review.
+//
+// Where a message takes a list ("Choose from: %s."), the caller passes the
+// region codes and the message joins them with ", ". An empty list never
+// renders as a list: the b variant drops the list sentence (codex plan-R4 2,
+// a list names only what the door would accept, so it can be empty).
+
+// reasonText turns a refusal reason (absent, not-hosting, no-secrets-key,
+// not-eligible) into the words C16 puts in its brackets (R-absent,
+// R-not-hosting, R-no-secrets-key, R-not-eligible). An unknown reason passes
+// through unchanged.
+func reasonText(reason string) string {
+ switch reason {
+ case "absent":
+ return "no box runs there now"
+ case "not-hosting":
+ return "boxes there can't start instances"
+ case "no-secrets-key":
+ return "boxes there can't open this deployment's secrets"
+ case "not-eligible":
+ return "boxes there don't take ordinary instances"
+ }
+ return reason
+}
+
+// msgAppRegionUnavailable is C16, or C16b when can is empty. The verbs are
+// the app's region code, the reason text and the joined list.
+func msgAppRegionUnavailable(code, reason string, can []string) string {
+ if len(can) == 0 {
+ return fmt.Sprintf("this app's region %s can't take new instances right now (%s), and no other region this app may use can either. Try again later.",
+ code, reasonText(reason))
+ }
+ return fmt.Sprintf("this app's region %s can't take new instances right now (%s). Pass a region to place this instance elsewhere. Regions that can: %s.",
+ code, reasonText(reason), strings.Join(can, ", "))
+}
+
+// msgAppRegionNotAllowed is C17, or C17b when allowed is empty.
+func msgAppRegionNotAllowed(code string, allowed []string) string {
+ if len(allowed) == 0 {
+ return fmt.Sprintf("this app's region %s is not one its instances may use, and no region they may use can take new instances right now. Try again later.", code)
+ }
+ return fmt.Sprintf("this app's region %s is not one its instances may use. Allowed: %s. Pass a region to choose one.",
+ code, strings.Join(allowed, ", "))
+}
+
+// msgRegionUnknown is C18, or C18b when choices is empty.
+func msgRegionUnknown(code string, choices []string) string {
+ if len(choices) == 0 {
+ return fmt.Sprintf("region %s is not one this platform runs in. Run the command without --region.", code)
+ }
+ return fmt.Sprintf("region %s is not one this platform runs in. Choose from: %s.", code, strings.Join(choices, ", "))
+}
+
+// msgRegionNotArmed is C19, or C19b when choices is empty.
+func msgRegionNotArmed(code string, choices []string) string {
+ if len(choices) == 0 {
+ return fmt.Sprintf("region %s can't take new apps right now. Run the command without --region.", code)
+ }
+ return fmt.Sprintf("region %s can't take new apps right now. Choose from: %s.", code, strings.Join(choices, ", "))
+}
+
+// msgAppRegionUnreadable is C20.
+const msgAppRegionUnreadable = "could not read this app's region. Try again."
+
+// msgReplicaOverlap is C26. The region code fills both verbs.
+func msgReplicaOverlap(code string) string {
+ return fmt.Sprintf("this app's region %s is also one of this instance's replica placements. Pass a region for the instance, or drop %s from replicas.", code, code)
+}
+
+// msgPolicyUnreadable is C27.
+const msgPolicyUnreadable = "could not read this app's instance policy, so there is no region suggestion. Try again."
+
+// msgAppRegionUnconfirmed is C28.
+func msgAppRegionUnconfirmed(code string) string {
+ return fmt.Sprintf("could not confirm that this app's region %s can take new instances. Try again, or pass a region.", code)
+}
+
+// msgPlacementDiscrepancy is C29, an operator log line. The app name fills
+// the second and third verbs.
+func msgPlacementDiscrepancy(host, app, code string) string {
+ return fmt.Sprintf("possible placement discrepancy: %s of %s has no region, but %s's region is %s. It may be a deliberate home override; review it.", host, app, app, code)
+}
+
+// msgNoSecretsKey is C31 (R9).
+func msgNoSecretsKey(code string) string {
+ return fmt.Sprintf("region %s can't run instances right now: its boxes don't hold this deployment's secrets key, so an instance there would start without its config or secrets. Choose another region.", code)
+}
diff --git a/internal/console/regionmsgs_test.go b/internal/console/regionmsgs_test.go
new file mode 100644
index 000000000..0d0709866
--- /dev/null
+++ b/internal/console/regionmsgs_test.go
@@ -0,0 +1,127 @@
+package console
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestRegionMessagesRender(t *testing.T) {
+ all := []string{
+ msgAppRegionUnavailable("uk", "absent", []string{"ie", "au"}),
+ msgAppRegionNotAllowed("uk", []string{"ie"}),
+ msgRegionUnknown("zz", []string{"ie", "uk"}),
+ msgRegionNotArmed("uk", []string{"ie"}),
+ msgAppRegionUnreadable,
+ msgReplicaOverlap("uk"),
+ msgPolicyUnreadable,
+ msgAppRegionUnconfirmed("uk"),
+ msgPlacementDiscrepancy("a.go.tito.io", "blog", "uk"),
+ msgNoSecretsKey("uk"),
+ // codex plan-R4 2: the empty-list variants, C16b to C19b.
+ msgAppRegionUnavailable("uk", "absent", nil),
+ msgAppRegionNotAllowed("uk", nil),
+ msgRegionUnknown("zz", nil),
+ msgRegionNotArmed("uk", nil),
+ }
+ for _, s := range all {
+ if s == "" || strings.Contains(s, "—") || strings.Contains(s, "%!") {
+ t.Errorf("bad message %q", s)
+ }
+ }
+ if !strings.Contains(all[0], reasonText("absent")) || !strings.Contains(all[0], "ie, au") {
+ t.Errorf("C16 = %q, want the readable reason and the list", all[0])
+ }
+ for _, s := range all[len(all)-4:] {
+ if strings.Contains(s, ": .") || strings.Contains(s, ": ,") {
+ t.Errorf("an empty list rendered as a list: %q", s)
+ }
+ }
+ seen := map[string]bool{}
+ for _, r := range []string{"absent", "not-hosting", "no-secrets-key", "not-eligible"} {
+ txt := reasonText(r)
+ if txt == r || seen[txt] {
+ t.Errorf("reasonText(%s) = %q: untranslated or duplicated", r, txt)
+ }
+ seen[txt] = true
+ }
+}
+
+// TestRegionMessagesMatchTheApprovedRecord holds every record entry mapped to
+// regionmsgs.go to the source as a quoted literal (so a short text cannot
+// pass by matching part of an identifier), and holds each rendered message to
+// the record's text filled with the same verbs, in the record's order.
+func TestRegionMessagesMatchTheApprovedRecord(t *testing.T) {
+ raw, err := os.ReadFile("../../docs/superpowers/plans/2026-10-02-nearest-region-copy.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ var rec struct {
+ Entries []struct {
+ ID string `json:"id"`
+ File string `json:"file"`
+ Text string `json:"text"`
+ } `json:"entries"`
+ }
+ if err := json.Unmarshal(raw, &rec); err != nil {
+ t.Fatal(err)
+ }
+ src, err := os.ReadFile("regionmsgs.go")
+ if err != nil {
+ t.Fatal(err)
+ }
+ byID := map[string]string{}
+ mapped := 0
+ for _, e := range rec.Entries {
+ byID[e.ID] = e.Text
+ if e.File != "internal/console/regionmsgs.go" {
+ continue
+ }
+ mapped++
+ if !strings.Contains(string(src), strconv.Quote(e.Text)) {
+ t.Errorf("%s: regionmsgs.go has no literal %s", e.ID, strconv.Quote(e.Text))
+ }
+ }
+ // The record maps 4 reasons, C16 to C20, C16b to C19b, C26 to C29 and C31.
+ if mapped != 18 {
+ t.Errorf("the record maps %d entries to regionmsgs.go, want 18", mapped)
+ }
+ list := []string{"ie", "au"}
+ cases := []struct {
+ id, got, want string
+ }{
+ {"R-absent", reasonText("absent"), byID["R-absent"]},
+ {"R-not-hosting", reasonText("not-hosting"), byID["R-not-hosting"]},
+ {"R-no-secrets-key", reasonText("no-secrets-key"), byID["R-no-secrets-key"]},
+ {"R-not-eligible", reasonText("not-eligible"), byID["R-not-eligible"]},
+ {"C16", msgAppRegionUnavailable("uk", "absent", list), fmt.Sprintf(byID["C16"], "uk", reasonText("absent"), "ie, au")},
+ {"C17", msgAppRegionNotAllowed("uk", list), fmt.Sprintf(byID["C17"], "uk", "ie, au")},
+ {"C18", msgRegionUnknown("zz", list), fmt.Sprintf(byID["C18"], "zz", "ie, au")},
+ {"C19", msgRegionNotArmed("uk", list), fmt.Sprintf(byID["C19"], "uk", "ie, au")},
+ {"C16b", msgAppRegionUnavailable("uk", "absent", nil), fmt.Sprintf(byID["C16b"], "uk", reasonText("absent"))},
+ {"C17b", msgAppRegionNotAllowed("uk", nil), fmt.Sprintf(byID["C17b"], "uk")},
+ {"C18b", msgRegionUnknown("zz", nil), fmt.Sprintf(byID["C18b"], "zz")},
+ {"C19b", msgRegionNotArmed("uk", nil), fmt.Sprintf(byID["C19b"], "uk")},
+ {"C20", msgAppRegionUnreadable, byID["C20"]},
+ {"C26", msgReplicaOverlap("uk"), fmt.Sprintf(byID["C26"], "uk", "uk")},
+ {"C27", msgPolicyUnreadable, byID["C27"]},
+ {"C28", msgAppRegionUnconfirmed("uk"), fmt.Sprintf(byID["C28"], "uk")},
+ {"C29", msgPlacementDiscrepancy("a.go.tito.io", "blog", "uk"), fmt.Sprintf(byID["C29"], "a.go.tito.io", "blog", "blog", "uk")},
+ {"C31", msgNoSecretsKey("uk"), fmt.Sprintf(byID["C31"], "uk")},
+ }
+ for _, c := range cases {
+ if byID[c.id] == "" {
+ t.Errorf("%s: not in the record", c.id)
+ continue
+ }
+ if c.got != c.want {
+ t.Errorf("%s = %q, record gives %q", c.id, c.got, c.want)
+ }
+ }
+ if reasonText("nonsense") != "nonsense" {
+ t.Errorf("an unknown reason should pass through unchanged")
+ }
+}