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

The citation is chosen outside the lock: accept re-checks the turn inside the transaction but swears the aim it picked before it #85

open not picked up yet opened this
opened this

Intent. #82 made the append one immediate transaction that re-reads the acts and asks mayAct again inside it, which closes the turn race. One part of the decision is still made outside: acceptOffer picks swear from the outer may.cite — §6's list of aims the standing offer satisfies and the seat has not sworn — and written() re-checks only may.accept.may before inserting that citation verbatim. may.cite depends on the accord that stands and on what the seat has already sworn, so if a full lap of turns lands between the outer read and the transaction, the re-check passes and the row swears an aim chosen against a different offer: a public oath §6 would not have allowed. The window is a sub-millisecond gap and needs four to six acts inside it, so this is an integrity edge, not a live bug — but it is the one place where read, decide and write are still three statements.

Done looks like

  • [ ] The citation is matched inside the same transaction that writes it, against the may.cite read there: acceptOffer hands written() what the caller asked for (input.cite, unread, or nothing), and the choice between "the first aim the charter offers" and "the aim named" is made under the write lock.
  • [ ] A citation the fresh may.cite does not hold is the refusal that already exists — invalid, "That is not an aim this accord lets you swear." — and nothing is written. No door invents wording, and a refusal still says nothing about any other seat's aims (§6).
  • [ ] Test in src/actions.test.ts: the aim a seat would have sworn is no longer citable by the time the row goes in, and the write is refused rather than landing a stale oath. It fails when the match moves back outside the transaction.

Also, the same shape elsewhere, if the planner wants it split out: joinTable counts the seats and then calls sit() outside a transaction, so two joins racing the last place can seat five at a four-seat table; startTable reads status outside the transaction that deals, so a host at two doors can deal twice. Neither is the ledger, so #46 did not cover them, and UNIQUE (game_id, player_id) already stops one person holding two seats.

Files. src/actions.ts, src/actions.test.ts.

Risks. Keep written() the only place that writes an act and keep emit outside the transaction. The cite that goes in must stay the seat's own choice among §6's list — do not widen the list to make the match easier.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs