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

Board, vote and milestone-watch refusals live in one module, read by the API and MCP #5688

Merged opened this

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

Merged

The refusals for a board save, a vote and a milestone watch now come from one module. The API and MCP both import them, so each sentence is typed in one place.

shipped production is serving it deploy 0583edb

opened this

The refusals for a board save, a vote and a milestone watch now come from one module. The API and MCP both import them, so each sentence is typed in one place.

  • New module, src/actions/board_words.ts. No existing module held these sentences, so this one is new. It exports BOARD_NOT_SAVED ("that board could not be saved"), VOTE_NOT_RECORDED ("that vote could not be recorded") and MILESTONE_WATCH_PERSON_ONLY ("only a person watches a milestone"). The wording is unchanged.
  • src/http/api.ts and src/http/mcp.ts. In each file, the three lines that typed a sentence now use the constant. One import line sits next to the existing ../actions/boards.ts import. Nothing else in either file changed.
  • test/board-words.test.ts. It checks that the module's three strings match the sentences word for word. It also reads api.ts and mcp.ts as text and fails if either drops the import or contains any of the three sentences typed out.

grep -rn for each sentence under src/ now finds only board_words.ts. bun x tsc --noEmit is clean. The new test, test/mcp.test.ts and the 13 suites that touch boards, votes and watches pass with no changes to them (154 tests). src/web is untouched, so the design lint was not needed.

🤖 Generated with Claude Code

Closes #5645

commented

The refusals for a board save, a vote and a milestone watch now come from one module. The API and MCP both import them, so each sentence is typed in one place.

  • New module, src/actions/board_words.ts. No existing module held these sentences, so this one is new. It exports BOARD_NOT_SAVED ("that board could not be saved"), VOTE_NOT_RECORDED ("that vote could not be recorded") and MILESTONE_WATCH_PERSON_ONLY ("only a person watches a milestone"). The wording is unchanged.
  • src/http/api.ts and src/http/mcp.ts. In each file, the three lines that typed a sentence now use the constant. One import line sits next to the existing ../actions/boards.ts import. Nothing else in either file changed.
  • test/board-words.test.ts. It checks that the module's three strings match the sentences word for word. It also reads api.ts and mcp.ts as text and fails if either drops the import or contains any of the three sentences typed out.

grep -rn for each sentence under src/ now finds only board_words.ts. bun x tsc --noEmit is clean. The new test, test/mcp.test.ts and the 13 suites that touch boards, votes and watches pass with no changes to them (154 tests). src/web is untouched, so the design lint was not needed.

🤖 Generated with Claude Code

Approved

Clean, and nothing in it is a security risk. It moves three refusal sentences into src/actions/board_words.ts and changes their wording by not one byte. The only lines touched in api.ts and mcp.ts are the ones carrying the sentences, plus one import each. The status codes, error codes and auth checks (respondAccess, unauthorized, the 403 for an agent watching a milestone) are untouched. I checked at head 9d1b0bd: grep -rn for each sentence under src/ finds only the module, bun x tsc --noEmit is clean, and test/board-words.test.ts passes (3/3). That test would fail if any copy were typed back into either file. The preview boots, / answers 200 and /api/v1/boards still refuses a guest with 401. Nothing here belongs in the terminal or the README: no new behaviour, only one source for words that are already said. One small note, not a blocker: the module is board_words.ts (underscore) while its neighbours are board-pins.ts and boards.ts. The issue named the file, so leave it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs