console: a switch-off console reports one region available, so the CLI never prints C11 for a flag it ignores (final review, C11)
regions_available is what THIS create could use, not what the fleet has. With CONSOLE_APP_REGIONS off, or no named home, every create lands on the home and --region is ignored (C13b); the create response counted the fleet's eligible regions anyway, so a no-flag create printed C11 and invited a flag the console would ignore. The console now answers 1 when appRegionsOn is false. Fixed console-side so older CLIs benefit; no CLI string changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 files changed,
+32
−1
internal/console/apicli.go+11 −1internal/console/regionclaim_test.go+21 −0
diff --git a/internal/console/apicli.go b/internal/console/apicli.go| index 7fa6c931a..0787152fb 100644 |
| --- a/internal/console/apicli.go |
| +++ b/internal/console/apicli.go |
| @@ -483,6 +483,16 @@ func (s *Server) handleAPICreateApp(w http.ResponseWriter, r *http.Request) { |
| if ferr == nil { |
| cands = s.regionCands(files) |
| } |
| + // regions_available is what THIS create could use, not what the |
| + // fleet has. With the switch off (or no named home) every create |
| + // lands on the home and --region is ignored (C13b), so there is |
| + // one; a CLI that prints C11 at two or more then never invites a |
| + // flag this console ignores. Fixed here rather than in the CLI so |
| + // older CLIs benefit (final review, C11 on a switch-off console). |
| + available := eligibleCount(cands) |
| + if !s.appRegionsOn() { |
| + available = 1 |
| + } |
| writeJSON(w, http.StatusOK, map[string]any{ |
| "account": acct.Sqid, "app": name, |
| "url": s.cfg.Origin + "/a/" + url.PathEscape(acct.Sqid) + "/apps/" + url.PathEscape(name), |
| @@ -492,7 +502,7 @@ func (s *Server) handleAPICreateApp(w http.ResponseWriter, r *http.Request) { |
| "region": s.homeRegionOut(gotRegion), |
| "region_name": s.placeOf(gotRegion, cands), |
| "region_source": gotSource, |
| - "regions_available": eligibleCount(cands), |
| + "regions_available": available, |
| }) |
| default: |
| // Any future claimOutcome lands here rather than silently 200ing |
diff --git a/internal/console/regionclaim_test.go b/internal/console/regionclaim_test.go| index 1da50338c..d8262ecee 100644 |
| --- a/internal/console/regionclaim_test.go |
| +++ b/internal/console/regionclaim_test.go |
| @@ -248,6 +248,27 @@ func TestAPICreateAppSwitchOffIgnoresAnExplicitRegion(t *testing.T) { |
| } |
| } |
| +// Final review, C11 on a switch-off console: regions_available is what the |
| +// create could actually use. With the switch off every create lands on the |
| +// home and --region is ignored (C13b), so the answer is 1, and a CLI, old |
| +// or new, that prints C11 at two or more never invites a flag this console |
| +// ignores. The same fleet with the switch on still answers 2. |
| +func TestAPICreateAppSwitchOffReportsOneRegionAvailable(t *testing.T) { |
| + fx, token := regionFixture(t) |
| + if status, res := postJSON(t, newAppURL(fx), map[string]string{"name": "on"}, token); status != http.StatusOK || res["regions_available"] != 2.0 { |
| + t.Fatalf("switch on = %d %v, want regions_available 2", status, res) |
| + } |
| + fx.srv.cfg.AppRegions = false |
| + if status, res := postJSON(t, newAppURL(fx), map[string]string{"name": "off"}, token); status != http.StatusOK || res["regions_available"] != 1.0 { |
| + t.Fatalf("switch off = %d %v, want regions_available 1", status, res) |
| + } |
| + // An unnamed home is the switch off too (appRegionsOn). |
| + fx.srv.cfg.AppRegions, fx.srv.cfg.HomeRegion = true, "" |
| + if status, res := postJSON(t, newAppURL(fx), map[string]string{"name": "unnamed"}, token); status != http.StatusOK || res["regions_available"] != 1.0 { |
| + t.Fatalf("unnamed home = %d %v, want regions_available 1", status, res) |
| + } |
| +} |
| + |
| // Defence in depth for future doors: an explicit code that is not a valid |
| // region never reaches the row. |
| func TestResolveNewAppRegionRejectsAGarbageExplicitCode(t *testing.T) { |