settings-1 final fixes: one definition of the rate-limit refusal
textTooManyRequests holds the shipped wording once; the rollback door and both pointer digest doors use it instead of repeating the literal (Astra r1 #8). No wording change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 files changed,
+23
−4
internal/console/apicli.go+6 −1internal/console/apidigests.go+2 −2internal/console/apidigests_test.go+15 −1
diff --git a/internal/console/apicli.go b/internal/console/apicli.go| index cbedff9a7..ae5ef819b 100644 |
| --- a/internal/console/apicli.go |
| +++ b/internal/console/apicli.go |
| @@ -1093,7 +1093,7 @@ func (s *Server) handleAPIRollback(w http.ResponseWriter, r *http.Request) { |
| // than it should, not more. A service is keyed on its NAME, its own |
| // budget separate from the operator who minted it (issue #220). |
| if !s.allowWrite(p, time.Now()) { |
| - jsonError(w, http.StatusTooManyRequests, "too many requests — wait a minute and try again") |
| + jsonError(w, http.StatusTooManyRequests, textTooManyRequests) |
| return |
| } |
| if s.cfg.Key == nil { |
| @@ -1216,6 +1216,11 @@ func (s *Server) handleAPIRollback(w http.ResponseWriter, r *http.Request) { |
| writeJSON(w, http.StatusOK, map[string]string{"result": "rolled back"}) |
| } |
| +// textTooManyRequests is the write budget's refusal over the API, shared by |
| +// the rollback door and the pointer digest doors so the text is defined once |
| +// (Astra r1 #8). The wording is the shipped one, unchanged. |
| +const textTooManyRequests = "too many requests — wait a minute and try again" |
| + |
| // refuseStableOverAPI is the API's refusal of a pointer write onto stable |
| // that is not a promotion (rollback, resign; Paul's ruling 2026-08-03): a |
| // deploy credential gets its scope refusal, as it never holds the stable rung, |
diff --git a/internal/console/apidigests.go b/internal/console/apidigests.go| index 7c68ff5a3..9ee3a1b26 100644 |
| --- a/internal/console/apidigests.go |
| +++ b/internal/console/apidigests.go |
| @@ -36,7 +36,7 @@ func (s *Server) handleAPIChannelResign(w http.ResponseWriter, r *http.Request) |
| } |
| p, _ := principalFrom(r.Context()) |
| if !s.allowWrite(p, time.Now()) { |
| - jsonError(w, http.StatusTooManyRequests, "too many requests — wait a minute and try again") |
| + jsonError(w, http.StatusTooManyRequests, textTooManyRequests) |
| return |
| } |
| var req struct { |
| @@ -156,7 +156,7 @@ func (s *Server) handleAPISystemPointerDigests(w http.ResponseWriter, r *http.Re |
| if r.Method == http.MethodPost { |
| p, _ := principalFrom(r.Context()) |
| if !s.allowWrite(p, time.Now()) { |
| - jsonError(w, http.StatusTooManyRequests, "too many requests — wait a minute and try again") |
| + jsonError(w, http.StatusTooManyRequests, textTooManyRequests) |
| return |
| } |
| var req struct { |
diff --git a/internal/console/apidigests_test.go b/internal/console/apidigests_test.go| index 6e226932c..e10872935 100644 |
| --- a/internal/console/apidigests_test.go |
| +++ b/internal/console/apidigests_test.go |
| @@ -5,6 +5,7 @@ import ( |
| "encoding/json" |
| "fmt" |
| "net/http" |
| + "os" |
| "strings" |
| "testing" |
| "time" |
| @@ -93,11 +94,24 @@ func TestResignDoorSpendsTheWriteBudget(t *testing.T) { |
| if st, out := postRaw(t, fx.ts.URL+"/api/cli/apps/bab/km/channels/resign", body, fx.member); st != http.StatusOK { |
| t.Fatalf("first request: %d %s", st, out) |
| } |
| - if st, out := postRaw(t, fx.ts.URL+"/api/cli/apps/bab/km/channels/resign", body, fx.member); st != http.StatusTooManyRequests { |
| + if st, out := postRaw(t, fx.ts.URL+"/api/cli/apps/bab/km/channels/resign", body, fx.member); st != http.StatusTooManyRequests || jsonErrorText(t, out) != textTooManyRequests { |
| t.Fatalf("second request over the budget: %d %s", st, out) |
| } |
| } |
| +// Astra r1 #8: the digest doors reuse the rollback door's one definition of |
| +// the rate-limit refusal instead of repeating its literal, so their source |
| +// adds no em dash. |
| +func TestDigestDoorsDefineNoEmDashLiteral(t *testing.T) { |
| + src, err := os.ReadFile("apidigests.go") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if n := strings.Count(string(src), "\u2014"); n != 0 { |
| + t.Fatalf("apidigests.go carries %d em dash(es); reuse textTooManyRequests", n) |
| + } |
| +} |
| + |
| func TestSystemDigestsDoorTurnsOnAndListsStragglers(t *testing.T) { |
| fx := newDigestsFixture(t) |
| ctx := context.Background() |