- Shipped
- September 8, 2026 at 6:44 PM UTC
- Author
- Kamo
- Commit
- 7825d6c
Two things the move panel needs, neither of them on the published frame. `GET /games/{id}/moves` hands over every ply in order. It is a separate read rather than a field on `ChessGameView` on purpose: the move list and the per-ply positions used to ride on every publish, several kilobytes per recipient per move, for a panel nothing rendered. They came off the frame and they stay off it — the window fetches this when it opens and when the game ends, and appends each live move from `lastMoveSan` in between. Authorized by handing the id to the same `require(...)` the board itself uses, because an endpoint that hands over the whole course of a game must refuse exactly the people the board refuses. `THINKING_MS` has existed since the table did and nothing ever wrote it, so every row in the database carries a null and the panel had a time field it could never fill. `applyMove` is the one place the answer is knowable: the position last changed when the previous ply was written, or when the game was created if this is the first move. Not `dateUpdated`, which looks like the same fact and is not — `@PreUpdate` stamps it on every save, so a draw offer or an opponent opening the board would reset it and the next move would report a think time of a few seconds. Recorded, not enforced. There is still deliberately no clock.