Send the live cast to the resolver, not cosine's top-k

The gallery gemma chose from was the union of the tracklet members' cosine
shortlists. Cosine cannot separate people on this data, so a metric that
cannot tell two men apart decided who gemma was allowed to consider, and the
right character was often not on the list.

The gallery is now the live cast: gender-compatible rows from
get_known_characters, named first, capped, re-read per tracklet so a minted
character is visible to later ones. Every crop reaches the resolver now,
including one whose cosine top-k was empty; those used to be dropped.

worker_vision spreads reference images across a budget instead of sending 3
per candidate, so a 9-character cast costs 9 images and not 27.

Ran on a GPU, 19:44-19:52 UTC. The lead's assignments drop from 36 to 16 and
14 of the 16 are him; the other two are art inside a panel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 00:15:52 +04:00
parent de7e34bc0f
commit a386e9d910
5 changed files with 133 additions and 17 deletions
+31
View File
@@ -302,3 +302,34 @@ This makes the module docstring's claim true. The self-check links three compati
9 and 17 and asserts they do not land in one tracklet; it returns `[[0, 1, 2]]` with the cap disabled.
Cost: 3 resolve calls become 8 for this character, so identity should go from about 1m25s to 3 or 4 minutes.
## The gallery is the live cast, not cosine's top-k {#cast-is-the-gallery}
**Closed, 2026-08-12, not yet run on a GPU.**
`run_stage_identity` built each tracklet's gallery by unioning the members' cosine shortlists, so the
metric that `caveats/audit-open.md#cosine-not-identity` shows cannot separate people decided who gemma was
even allowed to consider. Two different men reach 0.93 on this chapter's crops where the same man reaches
0.96. When the right character fell outside every member's top-5, gemma could only pick a wrong one or
answer NONE, and NONE mints a duplicate.
The cast is small. `tracklets.cast_gallery` builds the gallery from `get_known_characters` instead:
gender-compatible rows only, named first, then registry order. Cosine still shortlists per crop, and those
shortlists are still the linker's candidate-overlap evidence, but they no longer bound the answer.
Three consequences, each deliberate:
- **Every crop now reaches the resolver.** A crop only entered `shortlists` when its cosine top-k was
non-empty, so an early crop seen while the roster was still empty, or one with no gender-compatible row,
was dropped from resolution and could never mint. The guard is gone.
- **The gallery is re-read per tracklet**, so a character an earlier tracklet minted is visible to the
later ones. That closes the `ponytail:` note beside the mint path.
- **Two caps, because the prompt carries images.** `GALLERY_CAP = 10` in `service.py` bounds the
candidates and logs the ids it drops. `worker_vision.REF_BUDGET = 12` spreads reference images across
them, `max(1, min(3, 12 // n))` apiece, so a 9-character cast sends 9 images plus the query instead of
the 27 plus query that 3-apiece would have sent.
Cost: more gemma calls, because the crops that used to be dropped now each get one.
**Not fixed by this.** The gallery being right does not make the crop legible. A back-turned or tiny crop
still has no face for gemma to judge, which is `NEXT.md` item 4.