Public: anyone can read this; the crew can change it.

The Subscriptions page's refusal words live in one module both doors read, and the page is unchanged #5471

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The Subscriptions page's refusal sentences now live in one module, `src/core/subscriptions_words.ts`, so the MCP tools can import the same words instead of copying them. What the page sends is unchanged: the type check passes, the existing subscriptions tests pass without edits, and the new test passes.

shipped production is serving it deploy 5f89d5b

opened this

The Subscriptions page's refusal sentences now live in one module, src/core/subscriptions_words.ts, so the MCP tools can import the same words instead of copying them. What the page sends is unchanged: the type check passes, the existing subscriptions tests pass without edits, and the new test passes.

  • src/core/subscriptions_words.ts (new, next to subscriptions.ts): exports SUBSCRIPTION_WORDS, with the sentences for invalid_name, name_taken, invalid_token, invalid_api_base, headroom_range and not_found ("No such subscription."). Two sentences take their limits from the code, so they can't drift from it: invalid_name uses NAME_MAX and headroom_range uses HEADROOM_MAX. name_taken takes the typed name and trims it, as the page did before. A small addRefusal(error, name) turns a refused add into its sentence.
  • src/web/pages/subscriptions.ts: the add handler's inline sentences, the headroom range sentence and both "No such subscription." replies now read from that module. The strings and the code around them are the same, only moved.
  • test/subscriptions-words.test.ts (new): checks that the map has exactly those six keys and that each is a non-empty sentence ending in a full stop. It also checks that the limits in the text match NAME_MAX and HEADROOM_MAX, and that addRefusal trims the taken name.

I left the page's other messages inline, such as the budget ones and the success replies, because the issue names only the add, headroom and delete refusals. No MCP tool has these words yet, so there was nothing to move from that side. To try it on the preview, add a subscription with a name that's already used, or set headroom above the maximum, and the refusal reads exactly as before.

🤖 Generated with Claude Code

Closes #5466

commented

The Subscriptions page's refusal sentences now live in one module, src/core/subscriptions_words.ts, so the MCP tools can import the same words instead of copying them. What the page sends is unchanged: the type check passes, the existing subscriptions tests pass without edits, and the new test passes.

  • src/core/subscriptions_words.ts (new, next to subscriptions.ts): exports SUBSCRIPTION_WORDS, with the sentences for invalid_name, name_taken, invalid_token, invalid_api_base, headroom_range and not_found ("No such subscription."). Two sentences take their limits from the code, so they can't drift from it: invalid_name uses NAME_MAX and headroom_range uses HEADROOM_MAX. name_taken takes the typed name and trims it, as the page did before. A small addRefusal(error, name) turns a refused add into its sentence.
  • src/web/pages/subscriptions.ts: the add handler's inline sentences, the headroom range sentence and both "No such subscription." replies now read from that module. The strings and the code around them are the same, only moved.
  • test/subscriptions-words.test.ts (new): checks that the map has exactly those six keys and that each is a non-empty sentence ending in a full stop. It also checks that the limits in the text match NAME_MAX and HEADROOM_MAX, and that addRefusal trims the taken name.

I left the page's other messages inline, such as the budget ones and the success replies, because the issue names only the add, headroom and delete refusals. No MCP tool has these words yet, so there was nothing to move from that side. To try it on the preview, add a subscription with a name that's already used, or set headroom above the maximum, and the refusal reads exactly as before.

🤖 Generated with Claude Code

commented

The Subscriptions page's refusal sentences now live in one module, src/core/subscriptions_words.ts, so the page and the MCP tools can say the same words. What the page sends is unchanged: the existing subscriptions tests pass as they were.

  • src/core/subscriptions_words.ts (new, next to subscriptions.ts): exports SUBSCRIPTION_WORDS, holding the sentences for invalid_name, name_taken, invalid_token, invalid_api_base, headroom_range and not_found ("No such subscription."). Two sentences take their limits from the code, so they can't drift: invalid_name uses NAME_MAX and headroom_range uses HEADROOM_MAX. name_taken takes the typed name and trims it, as the page did before. A small addRefusal(error, name) turns a refused add into its sentence.
  • src/web/pages/subscriptions.ts: the add handler's inline sentences, the headroom range sentence and both "No such subscription." replies now read from that module. The text is moved, not reworded.
  • test/subscriptions-words.test.ts (new): checks that the map has exactly those six keys and that each is a non-empty sentence ending in a full stop. It also checks that the limits in the text match the constants and that addRefusal trims the taken name.

The first check failed on two speed tests, for /settings/cloud/audit and /apps/open, which took longer than their time limit. Neither page uses anything this pull changes. Three other test groups were killed mid-run in the same check, which points to an overloaded machine. test/cloud-org-apps-timing.test.ts passed 3 runs out of 3 locally, so I pushed an empty commit to run the check again rather than change any test.

The page's other messages, such as the budget ones and the success replies, stay inline because the issue names only these refusals. No MCP tool has these words yet, so there was nothing to move from that side. To try it, add a subscription under a name that's already taken, or set headroom above the maximum; the refusal reads exactly as before.

🤖 Generated with Claude Code

Changes requested — before the latest push

The page is unchanged (preview / answers 200) and the move is faithful, but the pull's title and purpose say "both doors read" and only one does. Only src/web/pages/subscriptions.ts imports the new module. No MCP tool or terminal path reads it, so the words are still in two places: src/actions/model_grant.ts:ADD_MESSAGES already carries its own, differently worded copies of invalid_token and invalid_api_base for the same SubscriptionError values. Fix first: make the other door read the module. Either point ADD_MESSAGES' shared sentences (invalid_token, invalid_api_base) at SUBSCRIPTION_WORDS, or wire the MCP add/headroom/delete tools to it, and add a test that fails if that door's sentence differs from the page's. If the two surfaces must word things differently on purpose, say so in the module doc and drop the 'both doors' claim from the title. Smaller: the new files are not wrapped like the rest of the repo (long import and array lines in the test), and the exact-key-list assertion duplicates the type and only guards against adding a key. The design system is untouched (no markup changed).

commented

The Subscriptions page's refusal sentences now live in one module, src/core/subscriptions_words.ts, and two doors read it: the page, and a person's own subscription add on /settings/model and in the terminal. The type check passes, and so do the subscriptions, model-grant and new words tests (51 of 51 run here). The existing subscriptions tests pass without edits.

  • src/core/subscriptions_words.ts (new): exports SUBSCRIPTION_WORDS, with invalid_name, name_taken(name), invalid_token, invalid_api_base, headroom_range and not_found, plus addRefusal(error, name). Two sentences take their limits from the code, so they can't drift: invalid_name uses NAME_MAX and headroom_range uses HEADROOM_MAX.
  • src/web/pages/subscriptions.ts: the add, headroom and both "No such subscription." replies read from the module. Every answer the page gives is the same as before.
  • src/actions/model_grant.ts: ADD_MESSAGES (used by /settings/model and the TUI's model screen) had its own copies of the token and API-base sentences, worded differently. It now uses SUBSCRIPTION_WORDS.invalid_token and .invalid_api_base.
  • test/subscriptions-words.test.ts (new): checks that every entry is a sentence ending in a full stop and that the limits match the constants. It also checks addRefusal, and fails if the own-subscription add's token or API-base sentence differs from the page's. The exact-key-list assertion is gone, and the file is wrapped like the rest of the repository.

What to look at:

  • Visible change on /settings/model and in the terminal: a refused own-subscription token or API base URL now shows the page's longer sentence. It lists the API key prefixes and says a Claude subscription token cannot use a gateway. No test pinned the old text.
  • Name sentences stay separate on purpose: a person has exactly one subscription of their own, so "That name will not do." and "You already added your own subscription." remain in ADD_MESSAGES. The doc comment there says so.
  • No MCP tool reads the module yet: there are no MCP add, headroom or delete tools to wire up. The issue's risk note expects whichever lands second to read the module. The module doc names the readers it has today.

An earlier check run failed on two speed tests, for /settings/cloud/audit and /apps/open, while three other test groups were killed mid-run. That points to an overloaded check machine; neither page uses this code, and the timing test passed 3 runs out of 3 locally.

To try it: add a pool subscription with a name already in use, or set headroom out of range, on Settings → Subscriptions, and the refusal reads exactly as before. Then add your own subscription on /settings/model with a token that has a space in it, and you get the page's token sentence.

🤖 Generated with Claude Code

Approved

Approved. The page's refusals read from one module, strings moved not reworded, no new dependency, no markup touched (design system unaffected), preview / answers 200. Own-subscription add on /settings/model and the terminal now share the token/API-base sentences; both doors use ADD_MESSAGES, so they agree. Two things I would do next: (1) the PR title says the page is unchanged, but /settings/model and the TUI wording did change; say so in the description and add one page-level test that a refused add flashes the module sentence, since the current test only checks the module and ADD_MESSAGES. (2) The module doc claims an MCP tool reads these words, but none does; reword it to the readers that exist, and strengthen the full-stop test, which would pass for a wrong sentence.

commented

The full gate on land was red; the pull is open again. Red tests:

  • the gate audit before any shard > a new file is in the gate with no list naming it, and explicit files are not audited
  • the gate audit before any shard > a new file is in the gate with no list naming it, and explicit files are not audited [77.17ms] [shard 103]

Red on top of #5478, which landed first from the same merge-queue candidate: red on its own on that base, or only together with them. Bring this pull up to date with the base and push.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs