Put the pager's numbers on the page

`2/4` needs the shape of a turn's take group for every message on screen.
Asking `attempts.group` per row would put a query behind each one -- the exact
cost `variant_count` was cached to avoid, and the reason SP8 could not just
drop that column and be done. So one query per page, keyed on the parents the
page mentions, and the count and ordinal land on the rows before they are
serialised.

`take_count` / `take_index` rather than reusing the old pair, because they do
not mean the same thing: the old ones cache a coordinate's siblings and say 0
for a turn nobody retook, these count the *turn's* takes across whatever
branches they ended up on and say 1/1. SP8 still drops `variant_count` and
`variant_index`.

Two traps, one of them mine. `parent_id` was not in ACTION_LIST_COLUMNS, so
reading it off a windowed row would have been a lazy load per action -- an
N+1 hidden behind the thing `load_only` exists to prevent. And the adventure
GET does not build `ActionOut` at all: it hands the window to the relationship
with `set_committed_value` and lets Pydantic walk it, so patching the three
places that do build ActionOut missed the one path every page load takes.
The test caught it by reading the wire instead of the helper.

424 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Dvvqn9ZDR4ixeFPHNbww7
This commit is contained in:
parththakkar106
2026-08-18 21:34:12 +05:30
committed by Parth
co-authored by Claude Opus 5
parent 2726840c60
commit c3c9b310eb
3 changed files with 117 additions and 3 deletions
+11
View File
@@ -189,6 +189,17 @@ class ActionOut(ORMModel):
# GET /actions/{id}/variants so this payload stays small.
variant_count: int = 0
variant_index: int = 0
# SP9: the pager. How many takes this turn has, and which one is on screen —
# `2/4`. Keyed on the parent, so it counts the takes of *this* turn and not
# every node that happens to share a depth, and so it keeps counting them
# after one has been forked onto a branch of its own.
#
# 1/1 for a turn nobody has retaken, which is most of them; the client draws
# no pager for a count of one. That is a different convention from
# `variant_count`, which says 0 for the same case — those two are the
# pre-SP9 pair and SP8 drops them.
take_count: int = 1
take_index: int = 0
created_at: datetime