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

Spend and budget range sentences live in one module, and the API and MCP read them from there #5575

Merged opened this

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

Merged

The three spend and budget range sentences now live in one new file, `src/actions/spend_words.ts`, as a constant called `SPEND_WORDS`: `usdPerMonth`, `ceilings` and `inviteDays`. The API (`src/http/api.ts`) and MCP (`src/http/mcp.ts`) read them from there. No wording changed, so the answers are byte-for-byte the same.

shipped production is serving it deploy d79f016

opened this

The three spend and budget range sentences now live in one new file, src/actions/spend_words.ts, as a constant called SPEND_WORDS: usdPerMonth, ceilings and inviteDays. The API (src/http/api.ts) and MCP (src/http/mcp.ts) read them from there. No wording changed, so the answers are byte-for-byte the same.

What moved where

  • src/http/api.ts: the four refusals are replaced — the invite days, the model grant and role usdPerMonth, and the account ceiling. One import line is added.
  • src/http/mcp.ts: the four matching refusals are replaced — model_grant, mint_invite, account_role and model_ceiling. Each edit stays on the literal's own line, plus one import line, so the other MCP work editing this file in parallel should not conflict.
  • test/spend-words.test.ts (new): each of the four refusals is triggered through the API and through MCP. The test checks the exact answer against SPEND_WORDS. A last test searches every .ts file under src/ and fails if any file other than the module types out one of the sentences. That is the issue's grep -rn check, run on every push.

Left alone: src/contract/openapi.ts describes the same rules with its own words (for example "days is not a whole number from 1 to 30 (422)"). These are documentation for the refusals, not the answers themselves, and they are worded differently, so they stay where they are.

How to check it: bun test test/spend-words.test.ts passes (5 tests). bun x tsc --noEmit is clean. The tests that cover these doors (mcp, mcp-invites, account-budget, model-grant-pages, member-others) all pass unchanged, 57 tests. Running grep -rn "from 0.01 to 10000 dollars" src shows the sentences only in spend_words.ts, apart from the differently worded openapi.ts lines above.

🤖 Generated with Claude Code

Closes #5559

commented

The three spend and budget range sentences now live in one new file, src/actions/spend_words.ts, as a constant called SPEND_WORDS: usdPerMonth, ceilings and inviteDays. The API (src/http/api.ts) and MCP (src/http/mcp.ts) read them from there. No wording changed, so the answers are byte-for-byte the same.

What moved where

  • src/http/api.ts: the four refusals are replaced — the invite days, the model grant and role usdPerMonth, and the account ceiling. One import line is added.
  • src/http/mcp.ts: the four matching refusals are replaced — model_grant, mint_invite, account_role and model_ceiling. Each edit stays on the literal's own line, plus one import line, so the other MCP work editing this file in parallel should not conflict.
  • test/spend-words.test.ts (new): each of the four refusals is triggered through the API and through MCP. The test checks the exact answer against SPEND_WORDS. A last test searches every .ts file under src/ and fails if any file other than the module types out one of the sentences. That is the issue's grep -rn check, run on every push.

Left alone: src/contract/openapi.ts describes the same rules with its own words (for example "days is not a whole number from 1 to 30 (422)"). These are documentation for the refusals, not the answers themselves, and they are worded differently, so they stay where they are.

How to check it: bun test test/spend-words.test.ts passes (5 tests). bun x tsc --noEmit is clean. The tests that cover these doors (mcp, mcp-invites, account-budget, model-grant-pages, member-others) all pass unchanged, 57 tests. Running grep -rn "from 0.01 to 10000 dollars" src shows the sentences only in spend_words.ts, apart from the differently worded openapi.ts lines above.

🤖 Generated with Claude Code

Approved

Clean, pure move that does what #5559 asks. All eight literals (four in api.ts, four in mcp.ts) now read from SPEND_WORDS, and each edit stays on the literal's own line, so it won't collide with the parallel MCP work. The wording didn't change, so the answers are byte-for-byte the same.

Security: nothing new to check. No auth, input or output path changed, and every admin gate around these refusals (scopeAllows(..., "admin"), API admin routing) is untouched. On the preview the page answers 200, an anonymous POST to /api/v1/admin/invites still gets 404, and anonymous MCP mint_invite still gets unauthorized. I couldn't trigger the 422 refusals on the preview without an admin token. The new test covers them through both doors with an admin token.

Tests: test/spend-words.test.ts triggers each refusal through the API and through MCP and asserts the module's sentence. The scan of src/ fails if any file other than the module retypes a sentence. That is the issue's grep check, and it runs on every push. Leaving the differently worded openapi.ts refusal docs alone is the right call.

Terminal and README: neither applies. This is error wording only, with no new behaviour.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs