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

The turn and the binding as pure arithmetic: src/round.ts, before the ledger writes a row #36

shipped production is serving it opened this has it
opened this

Intent. Twice now the pure half has gone first and the wiring has been easy — deal/score in #6, validAccord/satisfied/citable in #26 — so do the same for the rest of the game: one module that reads a table's public facts as plain data and says whose turn it is, what stands, who has signed, whether it binds, and what the settlement is. Then #8, #9 and #10 are a schema, a transaction and a boundary rather than a rules engine invented inside an action.

Done looks like

  • [ ] src/round.ts (new), pure, no bun:sqlite, no Date.now(), no Math.random(). Its input is plain data: the charter (Clause[] from src/board.ts), the seats in order, the acts newest last ({ seat, kind: "offer" | "accept" | "pass", accord?, cite?, at }), and now. Declare that act shape here — #8 stores rows that match it rather than the other way round.
  • [ ] whoseTurn(round): the seat to act, from seat order and the act count, and the rule for a seat that has left or a settled table. Say in the pull why the turn is derived rather than stored.
  • [ ] standing(round): the latest offer and the acceptances that came after it, in order — a new offer clears them, and an acceptance before any offer counts for nothing.
  • [ ] quorum(seats) = ⌈n/2⌉+1 (§2), and binds(round): whether the standing offer has reached it, and on which act.
  • [ ] settlement(round, charges): the binding accord, every seat's score from score() whether they signed or not, the ranking with ties sharing a rank and no tie-break, and each said claim's verdict with §7's none | small | large | dread mapping. The impound case is the same function with no binding accord: every score zero.
  • [ ] mayAct(round, seat) answers the one question every door asks before it draws a button: may this seat offer, accept (using citable from board.ts), or pass — and when not, the sentence to show.
  • [ ] src/round.test.ts replays §10's nine acts end to end: the standing offer after each act, the turn after each act, the third acceptance being the one that binds, the scores 8/6/4/8, the ranking, and the said claim that is false at the reveal. Plus an impound, an acceptance before any offer, a second offer clearing acceptances, and a seat accepting its own offer.
  • [ ] Nothing here writes, reads a database or touches a door. bun test stays green and the preview is unchanged.

Files src/round.ts (new), src/round.test.ts (new), README.md and AGENTS.md (one line each, as #26 did for the sworn arithmetic).

Risks The act shape declared here is the one #8 stores; get it wrong and the ledger and the rules disagree on the first migration. Keep it the smallest shape the rules need — a seat, a kind, an accord, a citation, a timestamp — and leave ids, authorship and storage to #8. src/board.ts is merged and free; this is a new file, so it runs beside every open pull.

Brief that fits @vesper — the rules engine of a game, as small typed modules with tests. (A person assigns.)

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs