Files
python-tdd/src/apps/lyric/tests
Disco DeDisco f107522b20 A.0 image-rendering schema + RWS rename + canonical-Earthman suit collapse — TDD. Sprint A.0 of [[project-image-based-deck-face-rendering]]. Adds three DeckVariant fields: has_card_images (BooleanField default=True — Earthman keeps False until its artwork ships, every new deck defaults True), family (CharField choices=[earthman, italian, english, playing] default=earthman — drives per-family display + filename slug mapping per [[reference-card-image-naming-convention]]), is_polarized (BooleanField default=False — Earthman is True today; Sprint A.4 game_kit applet will render "(×2)" in --terUser for polarized decks; Sprint C+B segment model uses it for segment-count logic). TarotCard.SUIT_CHOICES collapses from 8 values to 4 canonical Earthman values (BRANDS / CROWNS / GRAILS / BLADES); WANDS / CUPS / SWORDS / PENTACLES dropped — they were duplicative at the structural level since sig_deck_cards + levity/gravity_sig_cards already treated [WANDS, BRANDS, CROWNS] as one segment and [SWORDS, BLADES, CUPS, GRAILS] as another (so the project already *functionally* equated them; the lock just makes that explicit). Per-family display vocab (batons for Italian, wands for English, clubs for Playing) lives in Sprint A.2's display_suit_name property, not in the enum. Audit 2026-05-25 revealed the existing fiorentine-minchiate DeckVariant is actually 78-card RWS Tarot in disguise (22 majors numbered 0-21 w. RWS names: The Fool / The Magician / ... / The World; 56 minors in 4 suits × 14 cards) — NOT Minchiate (which has 40 trumps + 1 Il Matto + 56 minors = 97 cards). Migration 0012 renames the slug → tarot-rider-waite-smith, name → "Tarot (Rider-Waite-Smith)", sets family='english', has_card_images=False, is_polarized=False — and revocabs its 56 minor cards' suits in-place (WANDS→BRANDS, CUPS→GRAILS, SWORDS→BLADES, PENTACLES→CROWNS) so they match the new canonical enum. FKs (User.equipped_deck, User.unlocked_decks, TableSeat.deck_variant, etc.) survive untouched — slug-only changes don't break referential integrity. Earthman fields set explicitly in 0012 too (family=earthman, has_card_images=False, is_polarized=True). Companion code simplifications: sig_deck_cards + _sig_unique_cards_for_deck queries shrink from suit__in=[3 values] and [4 values] to [2 values] each (one per segment); TarotCard.suit_icon mapping shrinks from 8 entries to 4; gameboard.views.tarot_fan._suit_order shrinks from 8 keys to 4. Existing test files updated: test_game_room_tray.py (largest update — self.fiorentineself.rws, id_kit_fiorentine_deckid_kit_tarot_deck (template-id derives from deck.short_key = first slug segment), assertion "Fiorentine" → "Rider-Waite-Smith"); test_game_room_deck_contrib.py (same pattern, smaller); lyric/test_models.py + gameboard/test_views.py (slug literal swaps only); epic/test_models.py _make_sig_card test fixtures: "WANDS"→"BRANDS", "CUPS"→"GRAILS". 14 new ITs in DeckSchemaA0Test cover the schema additions + migration outcomes (field existence + choice values + earthman has all three fields set correctly + RWS rename verified + RWS cards use canonical suits + dropped enum values absent from SUIT_CHOICES). Tests: 14 new green; 1255/1255 IT+UT total green (38s); no regressions. Out of scope: Sprint A.1 will seed the actual Minchiate Fiorentine 1860-1890 (97-card) DeckVariant + TarotCard rows w. family='italian', has_card_images=True; A.2 adds the image_filename + display_suit_name properties that consume the new family field; A.3+ wires the render branches across 6 surfaces
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:25:26 -04:00
..
A.0 image-rendering schema + RWS rename + canonical-Earthman suit collapse — TDD. Sprint A.0 of [[project-image-based-deck-face-rendering]]. Adds three DeckVariant fields: has_card_images (BooleanField default=True — Earthman keeps False until its artwork ships, every new deck defaults True), family (CharField choices=[earthman, italian, english, playing] default=earthman — drives per-family display + filename slug mapping per [[reference-card-image-naming-convention]]), is_polarized (BooleanField default=False — Earthman is True today; Sprint A.4 game_kit applet will render "(×2)" in --terUser for polarized decks; Sprint C+B segment model uses it for segment-count logic). TarotCard.SUIT_CHOICES collapses from 8 values to 4 canonical Earthman values (BRANDS / CROWNS / GRAILS / BLADES); WANDS / CUPS / SWORDS / PENTACLES dropped — they were duplicative at the structural level since sig_deck_cards + levity/gravity_sig_cards already treated [WANDS, BRANDS, CROWNS] as one segment and [SWORDS, BLADES, CUPS, GRAILS] as another (so the project already *functionally* equated them; the lock just makes that explicit). Per-family display vocab (batons for Italian, wands for English, clubs for Playing) lives in Sprint A.2's display_suit_name property, not in the enum. Audit 2026-05-25 revealed the existing fiorentine-minchiate DeckVariant is actually 78-card RWS Tarot in disguise (22 majors numbered 0-21 w. RWS names: The Fool / The Magician / ... / The World; 56 minors in 4 suits × 14 cards) — NOT Minchiate (which has 40 trumps + 1 Il Matto + 56 minors = 97 cards). Migration 0012 renames the slug → tarot-rider-waite-smith, name → "Tarot (Rider-Waite-Smith)", sets family='english', has_card_images=False, is_polarized=False — and revocabs its 56 minor cards' suits in-place (WANDS→BRANDS, CUPS→GRAILS, SWORDS→BLADES, PENTACLES→CROWNS) so they match the new canonical enum. FKs (User.equipped_deck, User.unlocked_decks, TableSeat.deck_variant, etc.) survive untouched — slug-only changes don't break referential integrity. Earthman fields set explicitly in 0012 too (family=earthman, has_card_images=False, is_polarized=True). Companion code simplifications: sig_deck_cards + _sig_unique_cards_for_deck queries shrink from suit__in=[3 values] and [4 values] to [2 values] each (one per segment); TarotCard.suit_icon mapping shrinks from 8 entries to 4; gameboard.views.tarot_fan._suit_order shrinks from 8 keys to 4. Existing test files updated: test_game_room_tray.py (largest update — self.fiorentineself.rws, id_kit_fiorentine_deckid_kit_tarot_deck (template-id derives from deck.short_key = first slug segment), assertion "Fiorentine" → "Rider-Waite-Smith"); test_game_room_deck_contrib.py (same pattern, smaller); lyric/test_models.py + gameboard/test_views.py (slug literal swaps only); epic/test_models.py _make_sig_card test fixtures: "WANDS"→"BRANDS", "CUPS"→"GRAILS". 14 new ITs in DeckSchemaA0Test cover the schema additions + migration outcomes (field existence + choice values + earthman has all three fields set correctly + RWS rename verified + RWS cards use canonical suits + dropped enum values absent from SUIT_CHOICES). Tests: 14 new green; 1255/1255 IT+UT total green (38s); no regressions. Out of scope: Sprint A.1 will seed the actual Minchiate Fiorentine 1860-1890 (97-card) DeckVariant + TarotCard rows w. family='italian', has_card_images=True; A.2 adds the image_filename + display_suit_name properties that consume the new family field; A.3+ wires the render branches across 6 surfaces
2026-05-24 23:25:26 -04:00
feat: Token.BAND (Wristband) — non-admin variant of PASS, admin-awarded via Django admin to any user (NOT auto-granted on signal, NO is_staff coupling, NO model-layer guard). Mirrors PASS at runtime — fills 1 gate slot, never consumed, stays equipped, no current_room tie, no expiry, no In-Use microtooltip — but separates the policy concerns so PASS stays a deliberate staff-only trinket while BAND becomes the regular-user version (promotional / play-reward / staging give-away). Tooltip prose: name "Wristband", desc "Admit All Entry" (shared w. PASS — phrasing reflects the never-depleted lifetime, not multi-slot semantics), shoptalk "Unlimited free entry (BYOB)", expiry "no expiry". fa-ring icon across all 4 surfaces (Game Kit applet #id_kit_wristband between PASS + CARTE, gk-trinkets section, kit-bag dialog Trinket slot, wallet PASS→BAND→COIN elif chain). Priority chain — PASS → BAND → COIN → FREE → TITHE — wired identically into both apps.epic.models.select_token (room gatekeeper) + apps.gameboard.models._select_my_sea_token (my-sea gatekeeper); BAND wins over consumables for any holder while PASS still wins for staff who happen to hold both. debit_token + debit_my_sea_token treat BAND same as PASS: slot marked FILLED w. debited_token_type=BAND, token row preserved, current_room untouched, equipped_trinket unchanged. View contexts (gameboard, toggle_game_applets, _game_kit_context, wallet, toggle_wallet_applets) pass a band key — universal lookup, NO is_staff filter. Migration lyric/0007_alter_token_token_type — choices-only AlterField. TDD — 5 FTs in test_trinket_wristband.py (test_band_not_auto_equipped_after_award, test_band_tooltip_renders_full_prose, test_band_uses_fa_ring_icon, test_equipped_band_shows_equipped_mini_tooltip, test_equipped_band_shows_doff_active_don_disabled); 4 tooltip UTs (BandTokenTooltipTest); 5 model ITs (BandTokenAdminAwardTest — no-auto-grant for non-staff + staff, admin-can-award to either branch, not-auto-equipped); 2 priority-chain ITs (test_returns_band_when_held_and_no_pass, test_pass_still_wins_over_band_for_staff); 1 debit IT (test_debit_band_does_not_consume_or_unequip). 1145 IT/UT + 5 FT green. A boost-pass / promo-band w. richer semantics (multi-slot admit, time-window, etc.) lands as YET-ANOTHER token_type later — keep BAND the minimal "PASS minus admin gate" trinket so the policy axis stays clean. Captured in [[sprint-band-trinket-may21]] alongside the standing auto-commit rule [[feedback-auto-commit-after-build]]
2026-05-21 12:33:09 -04:00