Skip to content

Document Part 4's missing batch-flush checkpointing - #122

Merged
WilfordGrimley merged 1 commit into
masterfrom
part4-batch-flush-lesson
Jul 19, 2026
Merged

Document Part 4's missing batch-flush checkpointing#122
WilfordGrimley merged 1 commit into
masterfrom
part4-batch-flush-lesson

Conversation

@WilfordGrimley

Copy link
Copy Markdown

Summary

Part 4's write run has no per-batch flush (bulk_create fires once, after the full 39,707-card loop) - deviating from Part 1's own run-cohort-safety rails. Documents the risk and the rule for successor runs (Part 4b, Part 5) in docs/lessons.md. No code change.

🤖 Generated with Claude Code

@WilfordGrimley
WilfordGrimley merged commit a2b2730 into master Jul 19, 2026
5 checks passed
@WilfordGrimley
WilfordGrimley deleted the part4-batch-flush-lesson branch July 19, 2026 03:42
WilfordGrimley added a commit that referenced this pull request Jul 29, 2026
…e bulk file we already parse

Closes the open question in #600. Stacked on #601.

WHAT WAS WRONG
--------------
`deductive_backfill.select_d1_candidates` cast votes at confidence=0.95 on a
documented guarantee - "cross-verified against Scryfall's own printings_count
(not just 'our table happens to have one row')" - that no code implemented.
The condition it ran, `catalogued_printings_count == 1`, is entailed by the
`len(matches) == 1` test one line above it: that column counts OUR rows per
oracle id, rows sharing an oracle id share a name, so a count above 1 means
the card was already rejected. 137 D1 candidates before the condition, 137
after, measured against the live catalogue.

WHAT THIS DOES
--------------
1. `CanonicalPrintingMetadata.scryfall_default_cards_printings_count`
   (migration 0099, nullable): how many rows Scryfall's `default_cards` export
   lists for this row's oracle card. Populated by
   `import_scryfall_printing_metadata` as a second `Counter`, over the
   `oracle_id` of rows it ALREADY parses - zero extra Scryfall requests. The
   per-card API alternative would be ~113k of them; the standing instruction
   is not to hammer their servers, and the existing diff/freshness check
   (`_is_fresh`) is untouched.
2. D1 gates on `catalogued_printings_count == scryfall_default_cards_...`.
   The entailed condition is deleted, not softened.
3. NULL means "Scryfall publishes no count for this row" and FAILS the gate.
   That covers the 81 rows with a NULL `canonical_id` - which are exactly the
   81 bulk rows Scryfall itself ships with no `oracle_id`, and which the old
   code handed a fabricated `1`, i.e. an assertion of the very completeness
   being checked - and any oracle id absent from the bulk file (0 today).
   Ours > Scryfall's (the stale-file direction) also fails: equality, not >=.

WHY THE NAME SAYS `default_cards`
---------------------------------
`default_cards` is ~one row per printing, English-preferred. The count
INCLUDES every distinct printing/variant Scryfall knows of - reprints,
promos, showcase/borderless variants, tokens, and digital-only Arena/MTGO
printings (9,354 digital rows of 116,254) - and EXCLUDES the per-language
duplicates of a printing that also exists in English, which live only in
`all_cards` (not downloaded here). It is not a paper-only count and not an
all-languages count, and the field name, docstring and docs all say so. The
defect being fixed was a second field acquiring a claim it could not support.

MEASURED EFFECT (live catalogue, read-only, 2026-07-29)
-------------------------------------------------------
Running the SHIPPED predicate against production, with the Scryfall side
supplied from the on-disk bulk file:

  D1 candidates reaching the gate (len(matches) == 1):  137
  passing the OLD condition (catalogued == 1):          137
  passing the NEW gate (both known and equal):          137
  excluded by the new gate:                               0

THE REAL CHECK EXCLUDES 0 TODAY, and that is reported as the result rather
than hunted past: the guarantee currently holds for every D1 candidate. It is
not inert like its predecessor and it is not a stable property. Of the 14,893
normalised names with exactly one CanonicalCard row, 2 already disagree with
Scryfall and are excluded the moment an eligible Card names either -
counterfactually verified against the shipped predicate:

  'Chandra, Chill of Compliance': catalogued=1 scryfall=2 -> EXCLUDED
  'Tragic Trajectory':            catalogued=1 scryfall=2 -> EXCLUDED
  'Kusari-Gama':                  catalogued=1 scryfall=1 -> VOTE

Both hand-checked against the bulk rows: Chandra has two `fra` printings
(#212 and #297), we hold #212; Tragic Trajectory has `eoe` #122 and promo
`pw26` #22, we hold the former. 82 of our 35,990 oracle ids hold fewer
printings than Scryfall lists; 0 hold more; 0 are absent from the file.

TESTS THAT CAN GO RED
---------------------
`TestD1ScryfallPrintingCountGate`, including one end-to-end case that drives
the REAL importer over a bulk file listing two printings of an oracle card we
hold one of - the exact live production shape, which the deleted
fixture-only test could not produce. Reverting the gate to
`catalogued_printings_count == 1` turns 3 of them red (demonstrated).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NhYmT1PxCcyemA16dFDxN
WilfordGrimley added a commit that referenced this pull request Jul 29, 2026
…e bulk file we already parse (#614)

Closes the open question in #600. Stacked on #601.

WHAT WAS WRONG
--------------
`deductive_backfill.select_d1_candidates` cast votes at confidence=0.95 on a
documented guarantee - "cross-verified against Scryfall's own printings_count
(not just 'our table happens to have one row')" - that no code implemented.
The condition it ran, `catalogued_printings_count == 1`, is entailed by the
`len(matches) == 1` test one line above it: that column counts OUR rows per
oracle id, rows sharing an oracle id share a name, so a count above 1 means
the card was already rejected. 137 D1 candidates before the condition, 137
after, measured against the live catalogue.

WHAT THIS DOES
--------------
1. `CanonicalPrintingMetadata.scryfall_default_cards_printings_count`
   (migration 0099, nullable): how many rows Scryfall's `default_cards` export
   lists for this row's oracle card. Populated by
   `import_scryfall_printing_metadata` as a second `Counter`, over the
   `oracle_id` of rows it ALREADY parses - zero extra Scryfall requests. The
   per-card API alternative would be ~113k of them; the standing instruction
   is not to hammer their servers, and the existing diff/freshness check
   (`_is_fresh`) is untouched.
2. D1 gates on `catalogued_printings_count == scryfall_default_cards_...`.
   The entailed condition is deleted, not softened.
3. NULL means "Scryfall publishes no count for this row" and FAILS the gate.
   That covers the 81 rows with a NULL `canonical_id` - which are exactly the
   81 bulk rows Scryfall itself ships with no `oracle_id`, and which the old
   code handed a fabricated `1`, i.e. an assertion of the very completeness
   being checked - and any oracle id absent from the bulk file (0 today).
   Ours > Scryfall's (the stale-file direction) also fails: equality, not >=.

WHY THE NAME SAYS `default_cards`
---------------------------------
`default_cards` is ~one row per printing, English-preferred. The count
INCLUDES every distinct printing/variant Scryfall knows of - reprints,
promos, showcase/borderless variants, tokens, and digital-only Arena/MTGO
printings (9,354 digital rows of 116,254) - and EXCLUDES the per-language
duplicates of a printing that also exists in English, which live only in
`all_cards` (not downloaded here). It is not a paper-only count and not an
all-languages count, and the field name, docstring and docs all say so. The
defect being fixed was a second field acquiring a claim it could not support.

MEASURED EFFECT (live catalogue, read-only, 2026-07-29)
-------------------------------------------------------
Running the SHIPPED predicate against production, with the Scryfall side
supplied from the on-disk bulk file:

  D1 candidates reaching the gate (len(matches) == 1):  137
  passing the OLD condition (catalogued == 1):          137
  passing the NEW gate (both known and equal):          137
  excluded by the new gate:                               0

THE REAL CHECK EXCLUDES 0 TODAY, and that is reported as the result rather
than hunted past: the guarantee currently holds for every D1 candidate. It is
not inert like its predecessor and it is not a stable property. Of the 14,893
normalised names with exactly one CanonicalCard row, 2 already disagree with
Scryfall and are excluded the moment an eligible Card names either -
counterfactually verified against the shipped predicate:

  'Chandra, Chill of Compliance': catalogued=1 scryfall=2 -> EXCLUDED
  'Tragic Trajectory':            catalogued=1 scryfall=2 -> EXCLUDED
  'Kusari-Gama':                  catalogued=1 scryfall=1 -> VOTE

Both hand-checked against the bulk rows: Chandra has two `fra` printings
(#212 and #297), we hold #212; Tragic Trajectory has `eoe` #122 and promo
`pw26` #22, we hold the former. 82 of our 35,990 oracle ids hold fewer
printings than Scryfall lists; 0 hold more; 0 are absent from the file.

TESTS THAT CAN GO RED
---------------------
`TestD1ScryfallPrintingCountGate`, including one end-to-end case that drives
the REAL importer over a bulk file listing two printings of an oracle card we
hold one of - the exact live production shape, which the deleted
fixture-only test could not produce. Reverting the gate to
`catalogued_printings_count == 1` turns 3 of them red (demonstrated).


Claude-Session: https://claude.ai/code/session_013NhYmT1PxCcyemA16dFDxN

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant