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

One list of what this app can do, and a test that every door is on it or says why not #52

cancelled cancelled by trevato opened this
opened this

"The page, the terminal and MCP are one implementation" is the direction's promise, and it is drifting quietly. addMember, removeMember and listMembers have been in src/actions.ts since #27 with no route, no MCP tool and no terminal key — a whole capability with no door, which nobody noticed until a planning pass read the file. exportSnapshot has two API routes and no tool, which may be right, but nothing says so. Make the doors a thing that can be checked instead of a thing that is remembered.

What done looks like

  • [ ] src/doors.ts: one exported table, one row per capability the app has — the action's name, and for each of the four doors (page, API, MCP, terminal) either the address that reaches it (POST /api/proposals, tool propose, key n) or the sentence saying why it deliberately has none ("a backup is a file download; an agent reads the record through list_proposals"). Plain data, no imports from the doors themselves.
  • [ ] A test asserts the table against reality, in both directions: every entry claiming an API route matches a real route in routes, every entry claiming a tool matches a real tool in src/mcp.ts's list, and — the half that catches drift — every route and every tool that exists appears somewhere in the table. A door added without a row fails the test; a row naming a door that was removed fails it too.
  • [ ] Every exported action in src/actions.ts has a row. The test reads the module's exports so a new action with no row fails, rather than trusting a hand-written list. Helpers that are not capabilities (canWithdraw, the *_MAX constants) are excluded by an explicit, short deny-list in the table's own file, not by a clever heuristic.
  • [ ] The README.md "Any interface" section is generated from — or checked against — the same table, so the documentation cannot claim a door that does not exist. One or the other, whichever is simpler; say which in the pull.
  • [ ] The rows for the roster name #28 as the issue that will fill them, and the rows for voting name #45. This issue adds no doors: it writes down what is there and what is missing, and makes the gap fail a test.
  • [ ] bun test is green, and the pull lists every gap the table exposed on the day it landed.

Files I expect to change: src/doors.ts (new), src/doors.test.ts (new), README.md.

Risks: a table that must be hand-updated is only worth having if forgetting it fails the build — the bidirectional check is the whole point, so write that test first and watch it fail before filling the table in. Keep it to names and addresses: the moment it starts describing arguments or shapes it becomes a schema nobody maintains. It must import nothing from src/mcp.ts or src/routes.ts at module scope beyond the lists it compares, or a circular import will make the test the reason the app will not boot.

Fit: Ledger — the map of the app's own surface, kept honest by a test rather than by memory. A person assigns; I do not.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs