Owner ruling, 2026-07-30: "we'll leave as is for our first test flight. we need parity across all classes. the most recent run must be saved for every vote type and it should happen the same way, not 12 diff ways."
Deliberately NOT a blocker for the first monolith run. Recorded so the first run's limits are known.
The gap
models.vote_archive_model() registers exactly one model. On supersession:
| vote type |
on supersession |
CardPrintingTag |
copied to ArchivedCardPrintingTag first — survives, diffable |
CardTagVote (all three chip families) |
deleted outright |
CardIllustrationVote |
deleted outright |
CardArtistVote |
deleted outright |
So if a later run supersedes the shakedown, its printing verdicts stay comparable via --generation-diff and its chip, illustration and artist verdicts are gone. Only one grain of the first run is recoverable.
CardIllustrationVote is additionally the only production caller of purge_stale_machine_votes that bypasses vote_write.purge_and_write_votes, so it could not record superseded_by_run_id even if the model were registered tomorrow. That is the "12 different ways" problem concretely: the archiving mechanism is not the thing that varies — the write path is.
What would close this
- Every vote model archived on supersession, through one mechanism.
- Every write path routed through
purge_and_write_votes (or whatever the single choke point becomes), so run_id / superseded_by_run_id / original created_at are recorded identically for all of them. Registering models is not sufficient while one bypasses the choke point.
- A test that fails if a new vote model is added without archiving — otherwise this recurs, which is the same shape as the roster tethers.
Interaction with retention (#575): no janitor exists yet. ArchivedCardPrintingTag currently has no deletion path at all except FK cascade. Whatever sweeps archived generations must handle all of them, not just the printing grain.
Related: #604 (the archive), #575 (retention), #660 (the monolith).
Owner ruling, 2026-07-30: "we'll leave as is for our first test flight. we need parity across all classes. the most recent run must be saved for every vote type and it should happen the same way, not 12 diff ways."
Deliberately NOT a blocker for the first monolith run. Recorded so the first run's limits are known.
The gap
models.vote_archive_model()registers exactly one model. On supersession:CardPrintingTagArchivedCardPrintingTagfirst — survives, diffableCardTagVote(all three chip families)CardIllustrationVoteCardArtistVoteSo if a later run supersedes the shakedown, its printing verdicts stay comparable via
--generation-diffand its chip, illustration and artist verdicts are gone. Only one grain of the first run is recoverable.CardIllustrationVoteis additionally the only production caller ofpurge_stale_machine_votesthat bypassesvote_write.purge_and_write_votes, so it could not recordsuperseded_by_run_ideven if the model were registered tomorrow. That is the "12 different ways" problem concretely: the archiving mechanism is not the thing that varies — the write path is.What would close this
purge_and_write_votes(or whatever the single choke point becomes), sorun_id/superseded_by_run_id/ originalcreated_atare recorded identically for all of them. Registering models is not sufficient while one bypasses the choke point.Interaction with retention (#575): no janitor exists yet.
ArchivedCardPrintingTagcurrently has no deletion path at all except FK cascade. Whatever sweeps archived generations must handle all of them, not just the printing grain.Related: #604 (the archive), #575 (retention), #660 (the monolith).