Skip to content

feat(decks): check a deck's size, copy limits and owned plus proxy (spec 079, #258) - #341

Merged
plainprogrammer merged 24 commits into
mainfrom
079-deck-checks
Nov 13, 2026
Merged

plainprogrammer merged 24 commits into
mainfrom
079-deck-checks

Conversation

@plainprogrammer

Copy link
Copy Markdown
Owner

Closes #258. Spec 079 (spec, verification).

What it does

A Check deck link in the deck page's head opens /decks/:id/check: the same page with a "Deck check" section between the head and the zones. It is advisory and on demand. Nothing is stored, nothing is exported, and no finding blocks an edit.

  • Deck size. Standard, Pioneer, Modern, Legacy, Vintage and Pauper: Main at least 60, Sideboard at most 15. Commander: Commander and Main together exactly 100.
  • Copy limits. 4 of a card (1 in Commander), counted by card across printings and zones, owned plus proxy. Vintage restricted cards: 1. Basic lands and "any number" cards are exempt; a stated number ("up to seven") is held to that number in every format. Considering is never counted.
  • Owned plus proxy. A card with owned copies and a proxy in one zone is flagged, for every deck including Casual. This is where a forgotten proxy shows up.
  • A deck with no rules (Casual, or a format no longer registered) gets "Casual has no rules to check." and still gets the owned-plus-proxy result.

How

  • Catalog::DeckChecks is the collectible-agnostic core (a third optional answer on the collecting vocabulary, after deck_card and deck_shape). Its header comment is the extension contract. It names no collectible, format or zone key (spec/deck_core_spec.rb).
  • MTG::DeckRules and MTG::Collecting.deck_rules hold Magic's rules, read from the printings the page already loaded.
  • Decks::ChecksController#show is a read-only GET (no form, no redirect). The results page carries turbo-cache-control: no-cache, so Back asks for a fresh check and never shows results from before an edit.
  • The check adds no database statement beyond the plain deck page (13 for a 5-row and a 500-row deck alike); an example proves it and fails when a per-row query is added.
  • New design-system pattern DeckCheck (c-deckcheck), documented in docs/design-system/components/DeckCheck.md.

Evidence

Screenshots are from headless Firefox with factory data and placeholder printings (no card art). Phone shots are a 360px viewport frame.

Light Dark
Results, desktop
Results, phone
Vintage, a restricted card
Commander, short, phone
Casual, no rules, phone
Plain page head, phone (three buttons)

Walkthrough (plain page, Check deck, results, remove a proxy, plain page again):

Verification

  • Spec reviewed twice and the plan once, each read-only on Opus. Implementation review (Opus, Mode B): SPEC-ALIGNED; every AC from 1.1 to 7.6 has a proving example, listed in verification.md with the mutation proofs that show the examples can fail.
  • bin/ci passed on 8c64f8ff (4,614 examples, 0 failures, 1 pending: the existing art-agreement spec that needs COLLECTOR_ART_AGREEMENT). Later commits are documents only.
  • Scryfall's wording for the "any number" and "up to N" cards, and that name is the English name for a Japanese printing, were checked against api.scryfall.com and are quoted in verification.md.
  • No migration, no export, import or backup change; export format stays at version 4.

Things to look at

  • Deviations from the plan (all in verification.md): the component's classes are c-deckcheck because c-check is Form's checkbox-row class; a zone-head wrapping rule that was added and then dropped as out of scope; a plan expectation that was wrong in one example.
  • Cosmetic, left: the focus ring on the "Deck check" heading spans the full content width on a direct visit.
  • Pre-existing, not touched: a 60-character zone name with no spaces overflows the zone head on the plain deck page (no registered format has one).
  • Housekeeping noticed, not done here: docs/architecture/README.md's "Specs it covers" cell for collection and lots stops at 073.
  • One existing system example (menu_tab_bar_clearance_spec.rb:334, scroll restored from Turbo's cache) failed once in the pre-push bin/ci and passed on every rerun, alone and with the failing seed; it looks like a timing flake.

🤖 Generated with Claude Code

plainprogrammer and others added 24 commits November 12, 2026 15:50
Spec 079 (Approved v0.2.1) and its plan, each reviewed read-only on Opus: the spec twice, the plan once. Adds the specs index row.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…258)

Adds the core seam of spec 079: the contract types (SizeRule, Limit,
CopyRule, Rules, Line, Report), the extension contract in the module's
comment, the no-rules line for a vocabulary, type or format with no
rules, and the owned-plus-proxy check that runs for every deck. The
deck core guard now covers the new file (+1).

The ordering example drops the leading no-rules line before comparing:
the plan's expected list left it out, though no rules are registered.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Judges each size rule a vocabulary answers: the deck's collectible's
cards in the rule's zones, owned plus proxy, against a minimum, a
maximum or an exact size, worded as Short, Over or Pass with the true
count. Zones are named in the rule's key order, by the zone's shown
name, the format's registration or the key.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds the copy check after the size results: copies of a card counted by
catalog identity over the rule's zones, owned plus proxy, held to the
lowest limit its printings answer (nil above every number, the first
printing in page order on a tie), one Over the limit finding per card
in zone, name and identity order, or one pass line.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
MTG::DeckRules answers each format's size and copy rules and each
printing's limit (basic lands and any-number text exempt, a stated
number, Vintage's restriction, else the ordinary limit), read from the
loaded printing only. MTG::Collecting.deck_rules is the vocabulary
answer Catalog::DeckChecks asks for.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Real Magic decks through Deck::Page and Catalog::DeckChecks.report:
size per constructed format and Commander, the no-rules line, copies
across printings, zones and proxies, the English name, basic-land,
any-number, stated-number and Vintage limits, and another collectible
left out. These are characterisations of behaviour T2.1 delivers; they
passed on first run. Mutation proof: MAIN_MINIMUM set to 61 failed the
three examples that judge Main's minimum; restored before this commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
GET /decks/:deck_id/check renders the deck page with a "Deck check"
section between the head and the zones: the summary and one line per
result. The head gains a Check deck link (no prefetch); the results
page is marked turbo-cache-control no-cache. The deck guard counts the
new controller and partial, and the head now has three buttons.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
AC-5.1: the check page runs the plain deck page's statements, for a
5-row and a 500-row deck alike (13 each). A characterisation: it passed
on first run. Mutation proof: one Catalog::Entry.find per card in
split_lines made it fail (13 vs 18, 13 vs 513, 18 vs 513); reverted.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The c-check block styles the Check deck results between the deck page's
head and its zones: a status word with its 12px icon (stroke 2.5) and a
sentence per row, wrapping inside a word, on one column below 640px and
two from there. The root rule is scoped to .c-deckpage because .c-check
is also Form's checkbox row. A second block wraps a long zone name in a
zone's head and the shape fold's facts line, which pushed the results
page to 743px at 360px (AC-6.1, AC-6.4).

DeckCheck.md documents the pattern; DeckPage.md and the README list it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…258)

Five examples in headless Firefox: focus lands on the Deck check heading
after Enter on Check deck (AC-6.3), Back to the results asks for the
check again (AC-1.8), the results come before the zones (AC-6.3), the
results page fits a 360px viewport with 60-character names in both
themes (AC-6.1, AC-6.4), and every status is a word with an icon
(AC-6.2). Each of the first, second and fourth fails when its rule is
removed (autofocus, the no-cache tag, the row's overflow-wrap).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…c-check (#258)

The results section's classes no longer share a name with Form's checkbox
row, so the deck-page scoping and its resets are gone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…idth (#258)

Drops the zone-head and shape-fold wrapping block; a new example proves a
60-character zone name wraps inside the results section at 360px.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…h nothing (#258)

Assert the check's rows, statuses and long finding exist before counting them, so zero matches fails.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Factory data with placeholder printings and no card art, shot in headless
Firefox from a throwaway system spec: desktop at a 1400px window, phone in a
360px viewport frame (its 15px test scrollbar cropped off).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…the roadmap (#258)

collection-and-lots.md: Check deck and its page, the seam Catalog::DeckChecks
(pointing to its comment for the contract), Magic's rules, what is counted,
owned plus proxy for every deck, the words and the order; the head's actions,
the fourth vocabulary answer, the deck guard's count (68), the statement-count
invariant, MTG::DeckRules constants, a decision, two accepted-limit lines and
Sources. CLAUDE.md: one Project Status clause and the guard sentence's seam.
Roadmap: 14n, #258 built.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every AC with its proving example (gaps said plainly: AC-3.15, AC-3.14's
record-less decks, AC-2.8's 59), the supersession grep, AC-5.3's runs
(58 and 120 examples green, FORMAT_VERSION 4, no db diff), the Scryfall
facts, six mutation proofs, the screenshots, findings and deviations.
The implementation review is left for the Opus review.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… own numbers (#258)

Eleven characterisations on real Magic decks for AC-2.3, 2.8, 2.11, 3.2, 3.3, 3.14, 3.15, 4.6 and 4.9; each group fails under a recorded mutation.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The AC rows for 2.3, 2.8, 2.11, 3.2, 3.3, 3.14, 3.15, 4.6 and 4.9 name 848c654's examples; mutation proof 7 records how each fails.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…#258)

The contract comment of Catalog::DeckChecks said Considering is counted
by no size or copy rule, but the core only filtered by each rule's zone
keys: a vocabulary that named Considering in a rule would have had its
cards counted. The core now leaves Considering out of every size and
copy count (spec 079 decision 3) while a size line still names it; the
owned-plus-proxy check still judges it (decision 5). Two core examples
prove it, red before the change.

Two characterisations close proof gaps the review found: AC-3.1's exact
sentence for 5 copies in a Standard Main, and AC-4.5 for a Magic deck
whose format is no longer registered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hover prefetch would compute a check nobody asked for; nothing is
stored, so it would be wasted work that could show results from before
an edit (spec 079 decision 7). The reason was only in the plan, which
retires after the merge; it now sits beside the link, in the area doc
and in the DeckPage component doc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#258)

Records the Mode B review (SPEC-ALIGNED, round 1, HEAD 4144d1f) and how
each of its four follow-ups was resolved; names the new AC-3.1 and AC-4.5
proofs and the core's Considering examples; rewrites mutation 7's
Considering entry, re-run after the core change, and adds mutation 8;
the index's 079 row says it does 069's "deck rules" non-goal in part.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The area doc already carried the deck checks from T5.1. Distillation adds
the glossary's check, finding and pass (spec 079 Terms); says the core
leaves Considering out of a size or copy count even when a rule names its
key (decision 3, the review's first follow-up, 56b26fb); corrects the
deck page's count of optional vocabulary answers to three; and adds the
Distillation log row. The index marks 079 frozen by the area doc, its
results section's rule DeckCheck in the design system, plan retirement
owed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… (spec 027 D4)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…258)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@plainprogrammer plainprogrammer self-assigned this Nov 12, 2026
@plainprogrammer
plainprogrammer marked this pull request as ready for review November 13, 2026 00:04
@plainprogrammer
plainprogrammer merged commit d774d5c into main Nov 13, 2026
9 checks passed
@plainprogrammer
plainprogrammer deleted the 079-deck-checks branch November 13, 2026 00:04
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.

Deck checks: deck size, copy limits, and owned plus proxy of one card

1 participant