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

What you may do, and what it would pay you: `may`, `canStart` and the standing offer's worth, in the state itself #74

shipped production is serving it opened this
opened this

Intent. Put §5 and §6's answers into tableState once, so the page, the terminal and the MCP tools can show a playable turn without any of them re-deriving a rule.

Measured on main today (948abc0): a full turn cycle works over HTTP — open, join ×3, POST /api/tables/1/start deals the charter and my charge, /offer writes the accord, /accept refuses with "This accord satisfies none of your aims" when §6 has nothing for me to swear. taking() computes mayAct(round, seat.ordinal, charge) on every act and throws it away. What comes back in the view is turn, your_turn and standing and nothing else: no list of what this seat may do, no citations it could swear, no worth. So a door that wants to draw three buttons and a choice of truths has to reimplement §6 — which is the drift AGENTS.md forbids — and canStart does not exist at all, so a host who opened a table on the page cannot start it without curl (#33 expects to draw that button from a canStart that #8 did not export).

Done looks like

  • [ ] TableView carries may for the viewer's own seat — the MayAct src/round.ts already returns: may I offer, may I accept, may I pass, the refusal sentence for each, and cite, the aims §6 would let me swear on the standing offer, in charter order. Null for a viewer without a seat.
  • [ ] It is redacted like everything else: may is the viewer's and nobody else's, cite names only aims the viewer holds, and a test asserts a guest and another player see may: null and no aim of anyone's — the property test in #63 covers the same ground and should pass unchanged.
  • [ ] your_payoff: what the standing accord would pay this seat if it bound, from score in src/board.ts, for the viewer's seat only. It is the number a player is actually deciding on, and it is arithmetic they are entitled to.
  • [ ] canStart(who, table) exported beside canJoin and canLeave, agreeing exactly with startTable's refusals (host or admin, open, between SEATS.min and SEATS.max seated).
  • [ ] Nothing new is stored and no rule is written twice: every field here is src/round.ts or src/board.ts answering, carried through.
  • [ ] Tests that fail without it: the seat to act sees may.offer.may === true; the seat that already accepted sees the refusal it would get; a seat whose charge the accord pays nothing sees may.accept.may === false with §6's words and an empty cite; your_payoff matches score by hand on the §10 fixture in src/example.ts.

Files I expect to change. src/actions.ts, src/actions.test.ts, src/server.test.ts, README.md where it describes the state.

Risks. mayAct throws when handed another seat's charge — call it only for the viewer's own. Keep the cost down: it is pure arithmetic over acts already read, so no extra query. And keep may out of the live payload: it is per-viewer, and the bus carries addresses only.

Who. @mason, reviewed by @sentinel (nothing per-seat leaks). Ready now.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs