carlos / platform Public

console: Instances tab names where new instances start, only when confirmed (nearest-region §10)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 594cf6fb86b5a416c20886777526460495267712 parent 1f8ca0a
3 files changed, +117 −1
  • internal/console/regiondefault_test.go +92 −0
  • internal/console/server.go +23 −0
  • internal/console/templates/app_instances.html +2 −1
diff --git a/internal/console/regiondefault_test.go b/internal/console/regiondefault_test.go
index ef45cb603..a4c35aa67 100644
--- a/internal/console/regiondefault_test.go
+++ b/internal/console/regiondefault_test.go
@@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"errors"
+ "fmt"
+ "html"
"io"
"net/http"
"strings"
@@ -346,3 +348,93 @@ func TestRegionsLegacyFieldsGolden(t *testing.T) {
}
}
}
+
+// C7 only when the default is CONFIRMED available; C8 whenever it is
+// confirmed unavailable; nothing for fleet, pooled, or a default that
+// could not be read (codex plan-R1 14). Assertions compare the HTML-escaped
+// copy, both ways (codex plan-R1 8).
+func TestInstancesTabRegionLines(t *testing.T) {
+ starts := html.EscapeString(fmt.Sprintf(regionCopy.StartsIn, ukPlace))
+ cantUK := html.EscapeString(fmt.Sprintf(regionCopy.CantStart, "uk"))
+ page := func(fx *consoleFixture) string {
+ _, _, body := get(t, signInPaul(t, fx), fx.u("/apps/titogo/instances"))
+ return body
+ }
+ anyLine := func(body string) bool { return strings.Contains(body, `id="app-region-line"`) }
+
+ fx, _, _ := fillFixture(t, "uk")
+ fx.srv.cfg.AppRegions = true
+ body := page(fx)
+ if !strings.Contains(body, starts) {
+ t.Fatal("no C7 line for an available region")
+ }
+ if strings.Contains(body, `id="app-region-line" class="banner error"`) {
+ t.Error("C7 carries the warning class")
+ }
+ fx.rel.Delete(t.Context(), "control/edges/uk-box.json")
+ // No box publishes uk, so its place falls back to the bare code (§14).
+ body = page(fx)
+ if !strings.Contains(body, cantUK) || strings.Contains(body, starts) {
+ t.Fatal("want C8, not C7, when the region is absent (codex 7)")
+ }
+ if !strings.Contains(body, `id="app-region-line" class="banner error"`) {
+ t.Error("C8 lacks the warning class")
+ }
+
+ for _, setup := range []func(*consoleFixture){
+ func(fx *consoleFixture) { fx.st.SetAppPlacement(OpsAccountID, "titogo", "fleet/acme") },
+ func(fx *consoleFixture) {
+ m := titoManifest()
+ m.Pool = "tito"
+ enableInstances(t, fx, "titogo", m)
+ },
+ func(fx *consoleFixture) { corruptManifest(t, fx) },
+ func(fx *consoleFixture) { fx.srv.cfg.Releases = failingList{fx.rel} },
+ // the stored region could not be read: never claimed available.
+ func(fx *consoleFixture) {
+ fx.srv.appRegionOf = func(int64, string) (string, string, error) { return "", "", errors.New("db gone") }
+ },
+ } {
+ fx, _, _ := fillFixture(t, "uk")
+ setup(fx)
+ if anyLine(page(fx)) {
+ t.Error("a fleet, pooled or unreadable default showed a region line")
+ }
+ }
+}
+
+// PF9, PF10: an app whose region is the home (stored "", the record
+// vocabulary's home) gets C7 only when the home has a sibling to be told
+// apart from (two eligible regions); alone it says nothing. A home the
+// allow-list leaves out is still C8, whatever the sibling count.
+func TestInstancesTabRegionLineForAHomeApp(t *testing.T) {
+ homePlace := geo.PlaceName("eu-west-1", "ie")
+ starts := html.EscapeString(fmt.Sprintf(regionCopy.StartsIn, homePlace))
+ cantHome := html.EscapeString(fmt.Sprintf(regionCopy.CantStart, homePlace))
+ page := func(fx *consoleFixture) string {
+ _, _, body := get(t, signInPaul(t, fx), fx.u("/apps/titogo/instances"))
+ return body
+ }
+
+ fx, _, _ := fillFixture(t, "")
+ if body := page(fx); !strings.Contains(body, starts) {
+ t.Errorf("two eligible regions: want C7 naming the home %q", homePlace)
+ }
+
+ // Only the home is left: nothing to distinguish it from.
+ fx, _, _ = fillFixture(t, "")
+ fx.rel.Delete(t.Context(), "control/edges/uk-box.json")
+ if body := page(fx); strings.Contains(body, `id="app-region-line"`) {
+ t.Error("one eligible region: a region line showed")
+ }
+
+ fx, _, _ = fillFixture(t, "")
+ m := titoManifest()
+ m.Pool, m.Regions = "", []string{"uk"}
+ enableInstances(t, fx, "titogo", m)
+ body := page(fx)
+ if !strings.Contains(body, cantHome) || strings.Contains(body, starts) ||
+ !strings.Contains(body, `id="app-region-line" class="banner error"`) {
+ t.Error("home left out of the allow-list: want C8 only")
+ }
+}
diff --git a/internal/console/server.go b/internal/console/server.go
index ec11a244a..737d1da57 100644
--- a/internal/console/server.go
+++ b/internal/console/server.go
@@ -1412,6 +1412,11 @@ type page struct {
StorePending *int
StoreQueueUnknown bool
+ // RegionLine is the Instances tab's app-region note (C7 or C8, spec
+ // §10); "" shows nothing. RegionLineWarn marks C8.
+ RegionLine string
+ RegionLineWarn bool
+
InstanceDetail *instanceDetail
InstanceGroups []instanceGroup
InstanceChannels []string
@@ -3027,6 +3032,24 @@ func (s *Server) handleAppInstances(w http.ResponseWriter, r *http.Request) {
if warn != "" && p.FlashErr == "" {
p.FlashErr = warn
}
+ // The app-region note (spec §10). Available is nil for a fleet app and
+ // for a policy, edges or stored region that could not be read, so those
+ // show nothing: unreadable never reads as available. Pooled is excluded
+ // by name.
+ if d := s.appDefault(r.Context(), acct, p.AppName); d.Available != nil && d.Reason != "pooled" {
+ switch {
+ case !*d.Available:
+ p.RegionLine, p.RegionLineWarn = fmt.Sprintf(regionCopy.CantStart, d.Name), true
+ case d.Region != s.cfg.HomeRegion:
+ p.RegionLine = fmt.Sprintf(regionCopy.StartsIn, d.Name)
+ default:
+ // A home app is told where it starts only when the home has a
+ // sibling to be told apart from.
+ if files, err := readEdgesSnapshot(r.Context(), s.cfg.Releases); err == nil && eligibleCount(s.regionCands(files)) >= 2 {
+ p.RegionLine = fmt.Sprintf(regionCopy.StartsIn, d.Name)
+ }
+ }
+ }
// The empty state keys off the app's UNFILTERED rows, not the filtered
// page: a filter that matches nothing shows "no matches" (below), never
// the attach-onboarding copy — that copy is only true when the app has
diff --git a/internal/console/templates/app_instances.html b/internal/console/templates/app_instances.html
index 184e7e952..b3904fcbb 100644
--- a/internal/console/templates/app_instances.html
+++ b/internal/console/templates/app_instances.html
@@ -57,7 +57,8 @@ against that copy's own peak.</p>
<p class="small">The address comes live within a couple of minutes.</p>
{{end}}
-<h2>Placement</h2>
+<h2>Placement</h2>{{if .RegionLine}}
+<p id="app-region-line"{{if .RegionLineWarn}} class="banner error"{{end}}>{{.RegionLine}}</p>{{end}}
{{if .AppPlacementKnown}}
<p>{{if .AppPlacement}}This app runs on your fleet <a href="{{.Nav}}/fleets/{{fleetName .AppPlacement}}"><code>{{fleetName .AppPlacement}}</code></a>.{{else}}This app runs on the shared fleet.{{end}}</p>
{{if and .IsOwner .OwnedFleetNames}}