Major infrastructure upgrade to Parquet format with comprehensive performance improvements, simplified data management, and instant setup via GitHub downloads.
- **Quick Build**: One-click automation button in New Deck wizard with live progress tracking (5 phases: Creatures, Spells, Lands, Final Touches, Summary).
- **Skip Controls**: Granular stage-skipping toggles in New Deck wizard (21 flags: all land steps, creature stages, spell categories).
- Skeleton placeholders now accept `data-skeleton-label` microcopy and only surface after ~400 ms on the build wizard, stage navigator, and alternatives panel.
- Must-have toggle API (`/build/must-haves/toggle`), telemetry ingestion route (`/telemetry/events`), and structured logging helpers for include/exclude state changes and frontend beacons.
- Commander catalog results wrap in a deferred skeleton list, and commander art lazy-loads via a new `IntersectionObserver` helper in `code/web/static/app.js`.
- Alternative suggestions in the build wizard now surface the replacement card preview immediately and reload the list after a swap.
### Added
- Alternatives panel includes a "New pool" button so you can request a fresh batch of suggestions without rerunning the stage.
### Changed
- Alternative suggestion buttons expose role, mana, and rarity metadata to hover previews for better at-a-glance context.
### Fixed
- Previewing an alternative card now shows the replacement instead of the currently slotted card, and the list refreshes automatically after choosing an alternative.
- Partner suggestion service and API power Step 2 suggestion chips for partner, background, and Doctor pairings when `ENABLE_PARTNER_SUGGESTIONS` is active.
- Headless runner now honors partner/background inputs behind the `ENABLE_PARTNER_MECHANICS` feature flag and carries regression coverage for dry-run resolution.
- Web builder Step 2 exposes partner/background pairing when `ENABLE_PARTNER_MECHANICS` is active, including live previews and warnings for invalid combinations.
- Quick-start modal mirrors the Step 2 partner/background controls so fast deck builds can choose a secondary commander or background without leaving the modal.
- Partner mechanics UI auto-enables for eligible commanders, renames the secondary picker to “Partner commander,” layers in Partner With defaults with opt-out chips, adds Doctor/Doctor’s Companion pairing, and keeps modal/theme previews in sync.
- Deck exports now surface combined commander metadata across CSV/TXT headers and JSON summaries so dual-command builds stay in sync for downstream tooling.
### Added
- Partner suggestion dataset loader, scoring service (`code/web/services/partner_suggestions.py`), FastAPI endpoint, UI chips, dataset override env (`PARTNER_SUGGESTIONS_DATASET`), auto-regeneration when the dataset is missing, and tests covering dataset flattening plus API responses.
- CLI regression coverage (`code/tests/test_cli_partner_config.py`) verifying partner/background dry-run payloads and `ENABLE_PARTNER_MECHANICS` env gating in the headless runner.
- Web build wizard toggle for partner mechanics with partner/background selectors, auto-pair hints, warnings, and combined color preview behind the feature flag.
- Partner and background selections now render card art previews (with Scryfall links) in the quick-start wizard, Step 2 form, and deck summary so builders can confirm the secondary pick at a glance.
- Quick-start modal now renders shared partner/background controls (reusing `_partner_controls.html`) whenever a commander that supports the mechanic is inspected.
- Background catalog loader (`code/deck_builder/background_loader.py`) with memoized parsing, typed entries, and a generator utility (`python -m code.scripts.generate_background_cards`) plus coverage to ensure only legal backgrounds enter the catalog.
- Shared `CombinedCommander` aggregation and partner/background selection helper wired through deck builds, exports, and partner preview endpoints with accompanying regression tests.
- Script `python -m code.scripts.build_partner_suggestions` materializes commander metadata, theme indexes, and observed pairings into `config/analytics/partner_synergy.json` to seed the partner suggestion engine.
- Partner suggestion scoring helper (`code/deck_builder/suggestions.py`) with mode-specific weights and regression tests ensuring canonical pairings rank highest across partner, background, and Doctor flows.
- Export regression coverage (`code/tests/test_export_commander_metadata.py`) verifies commander metadata is embedded in CSV/TXT headers and summary payloads while preserving existing columns.
- Partner suggestion telemetry emits `partner_suggestions.generated` and `partner_suggestions.selected` logs (via `code/web/services/telemetry.py`) so adoption metrics and dataset diagnostics can be monitored.
### Changed
- Partner controls hydrate suggestion chips on the web builder and quick-start modal, fetching ranked partner/backdrop recommendations while respecting active partner mode and session locks when `ENABLE_PARTNER_SUGGESTIONS=1`.
- Partner suggestion scoring now filters out broad "Legends Matter", "Historics Matter", and Kindred themes when computing overlap or synergy so recommendations emphasize distinctive commander pairings.
- Headless runner parsing now resolves `--secondary-commander` and `--background` inputs (mutually exclusive), applies the shared partner selection helper ahead of deck assembly, and surfaces flag-controlled behavior in exported dry-run payloads.
- Step 2 submission now validates partner inputs, stores combined commander previews/warnings in the session, and clears prior partner state when the toggle is disabled.
- Quick-start `/build/new` submission resolves partner selections, persists the combined commander payload, and re-renders the modal with inline partner errors when inputs conflict.
- Partner controls mount automatically for eligible commanders, replace the manual toggle with a hidden enable flag, rename the select to “Partner commander,” and expose an opt-out chip when Partner With suggests a default.
- Commander catalog metadata now flags Doctors and Doctor’s Companions so selectors present only legal pairings and annotate each option with its role.
- Partner detection now distinguishes the standalone “Partner” keyword from Partner With/Doctor’s Companion/restricted variants, and the web selector filters plain-partner pools to exclude those mechanics while keeping direct Partner With pairings intact.
- Structured partner selection logs now emit `partner_mode_selected` with commander color deltas, capturing colors before and after pairing for diagnostics parity.
- Structured partner selection logs now tag suggestion-driven selections with a `selection_source` attribute to differentiate manual picks from suggestion chip adoption.
- Commander setup now regenerates `background_cards.csv` alongside `commander_cards.csv`, ensuring the background picker stays synchronized after catalog refreshes or fresh installs.
- Setup/tagging auto-refresh now runs the partner suggestion dataset builder so `config/analytics/partner_synergy.json` tracks the latest commander catalog and deck exports without manual scripts.
- CSV/TXT deck exports append commander metadata columns, text headers include partner mode and colors, and summary sidecars embed serialized combined commander details without breaking legacy consumers.
- Partner commander previews in Step 2 and the build summary now mirror the primary commander card layout (including hover metadata and high-res art) so both selections share identical interactions.
- Editorial governance CI stages lightweight catalog fixtures when `EDITORIAL_TEST_USE_FIXTURES=1`, avoiding the need to sync `config/themes/catalog` into source control.
- Regenerated `background_cards.csv` and tightened background detection so the picker only lists true Background enchantments, preventing "Choose a Background" commanders from appearing as illegal partners and restoring background availability when the CSV was missing.
- Restricted partner commanders with dash-based keywords (e.g., Partner - Survivors, Partner - Father & Son) now register as partners and surface their matching group pairings in the web selector.
- Quick-start modal partner previews now merge theme tags with Step 2 so chips stay consistent after commander inspection.
- Step 5 summary and quick-start commander preview now surface merged partner color identity and theme tags so pairings like Halana + Alena display both colors.
- Partner and background builds now inject the secondary commander card automatically, keeping deck libraries, exports, and Step 5 summaries in sync with the chosen pairing.
- Partner With commanders now restrict the dropdown to their canon companion and the preview panel adopts the wizard theme colors for better readability while live-selection previews render immediately.
- Manual partner selections now persist across the wizard and quick-start modal, keeping recommendations and theme chips in sync without needing an extra apply step.
- Background picker now falls back to the commander catalog when `background_cards.csv` is missing so “Choose a Background” commanders remain selectable in the web UI.
- Partner hover previews now respect the secondary commander data so the popup matches the card you’re focusing.
- Step 5 summary and finished deck views now surface the deck’s chosen themes (and commander hover metadata) without flooding the UI with every commander tag.
- Doctor’s Companion commanders now surface only legal Doctor pairings, direct Partner With matches (e.g., Amy & Rory) remain available, and escaped newline text no longer breaks partner detection.
- Partner suggestion refresh now re-attempts dataset generation when triggered from the UI and ensures the builder script loads project packages inside Docker, so missing `partner_synergy.json` files can be recreated without restarting the web app.
- Compose manifests and docs include new environment toggles for random reroll throttling, telemetry/logging, homepage commander tile, and optional random rate limiting.
- Script `python -m code.scripts.generate_theme_catalog` emits a normalized `theme_catalog.csv` with commander/card counts, deterministic ordering, and a reproducible version hash for supplemental theme inputs.
- Unit tests cover catalog generation on fixture CSVs and verify normalization removes duplicate theme variants.
- Loader `load_theme_catalog()` memoizes CSV parsing, validates required columns, and exposes typed entries plus version metadata for runtime integrations.
- Unit tests exercise loader success, empty-file fallback, and malformed-column scenarios.
- Fuzzy theme matcher builds a trigram-backed index with Levenshtein + Sequence similarity scoring, threshold constants, and resolution utilities for supplemental theme inputs.
- Unit tests validate normalization, typo recovery, suggestion quality, and enforce a basic performance ceiling for 400+ theme catalogs.
- Headless configs accept `additional_themes` + `theme_match_mode` with catalog-backed fuzzy resolution, strict/permissive enforcement, and persistence into exported run configs and diagnostics.
- Added targeted tests for additional theme parsing, strict failure handling, and permissive warning coverage.
- Web New Deck modal renders an “Additional Themes” HTMX partial supporting add/remove, suggestion adoption, mode switching, limit enforcement, and accessible live messaging (gated by `ENABLE_CUSTOM_THEMES`).
- Supplemental theme telemetry now records commander/user/merged theme payloads, exposes `/status/theme_metrics` for diagnostics, and surfaces user theme weighting via structured `user_theme_applied` logs and the diagnostics dashboard panel.
- Run-config exports now surface `userThemes` and `themeCatalogVersion` metadata while retaining legacy fields; headless imports accept both aliases without changing hash-equivalent payloads when no user themes are present.
### Fixed
- Additional Themes now falls back to `theme_list.json` when `theme_catalog.csv` is absent, restoring resolution, removal, and build application for user-supplied themes across web and headless flows.
- Wrapped the Multi-Faced Card Handling roadmap (tag merge, commander eligibility, land accounting) so double-faced cards now share tags, respect primary-face commander legality, and surface accurate land/MDFC diagnostics across web, CLI, and exports.
- Closed out MDFC follow-ups: deck summary now highlights double-faced lands with badges, per-face mana metadata flows through reporting, exports include annotations, and diagnostics can emit per-face snapshots for catalog QA.
- Surfaced commander exclusion warnings and automatic corrections in the builder so players are guided toward the legal front face whenever only a secondary face meets commander rules.
- Diagnostics dashboard now displays a multi-face merge snapshot plus live MDFC telemetry so catalog rebuilds and deck summaries can be verified in one place.
- Automated commander catalog refresh now ships with `python -m code.scripts.refresh_commander_catalog`, producing merged and compatibility snapshots alongside updated documentation for downstream consumers.
- Deck exporter regression coverage ensuring MDFC annotations (`DFCNote`) appear in CSV/TXT outputs, plus documentation for adding new double-faced cards to authoring workflows.
- Optional MDFC diagnostics snapshot toggled via `DFC_PER_FACE_SNAPSHOT` (with `DFC_PER_FACE_SNAPSHOT_PATH` override) to capture merged per-face metadata for observability.
- Structured observability for DFC merges: `multi_face_merger.py` now captures merge metrics and persists `logs/dfc_merge_summary.json` for troubleshooting.
- Land accounting coverage: `test_land_summary_totals.py` exercises MDFC totals, CLI output, and the deck summary HTMX fragment; shared fixtures added to `code/tests/conftest.py` for reuse.
- Tests: added `test_commander_primary_face_filter.py` to cover primary-face commander eligibility and secondary-face exclusions.
- Tests: added `test_commander_exclusion_warnings.py` to ensure commander exclusion guidance appears in the web builder and protects against regressions.
- Diagnostics: added a multi-face merge panel (with MDFC telemetry counters) to `/diagnostics`, powered by `summary_telemetry.py` and new land summary hooks.
- Commander wizard toggle to swap a matching basic land whenever modal double-faced lands are added, plus regression coverage in `test_mdfc_basic_swap.py`.
- Automation: `python -m code.scripts.refresh_commander_catalog` refreshes commander catalogs with MDFC-aware tagging, writing both merged output and `csv_files/compat_faces/commander_cards_unmerged.csv` for downstream validation; README and commander onboarding docs updated with migration guidance.
- Documentation: added `docs/qa/mdfc_staging_checklist.md` outlining MDFC staging QA (now updated for the always-on merge with optional compatibility snapshots).
- Deck summary UI renders modal double-faced land badges and per-face face details so builders can audit mana contributions at-a-glance.
- MDFC merge flag removed: `ENABLE_DFC_MERGE` no longer gates the multi-face merge; the merge now runs unconditionally with optional `DFC_COMPAT_SNAPSHOT` compatibility snapshots.
- New Deck modal commander search now flags secondary-face-only entries, shows inline guidance, and auto-fills the eligible face before starting a build.
- New Deck modal Preferences block now surfaces "Use only owned", "Prefer owned", and "Swap basics for MDFC lands" checkboxes with session-backed defaults so the wizard mirrors Step 4 behavior.
- Deck summary now surfaces "Lands: X (Y with DFC)" with an MDFC breakdown panel, and CLI summaries mirror the same copy so web/CLI diagnostics stay in sync.
- Deck summary builder now records MDFC land telemetry for diagnostics snapshots, enabling quick verification of land contributions across builds.
- Roadmap documentation now summarizes remaining DFC follow-ups (observability, rollout gating, and exporter/UI enhancements) with next steps and ownership notes.
- Commander CSV enrichment now backfills `themeTags`, `creatureTypes`, and `roleTags` from the color-tagged catalogs so primary-face enforcement keeps merged tag coverage for multi-face commanders.
- Commander CSV generation now enforces primary-face legality, dropping secondary-face-only records, writing `.commander_exclusions.json` diagnostics, and surfacing actionable headless errors when configs reference removed commanders.
- Commander browser now paginates results in 20-commander pages with accessible navigation controls and range summaries to keep the catalog responsive.
- Commander hover preview collapses to a card-only view when browsing commanders, and all theme chips display without the previous “+ more” overflow badge.
- Added a Content Security Policy upgrade directive so proxied HTTPS deployments safely rewrite commander pagination requests to HTTPS, preventing mixed-content blocks.
- Commander thumbnails use a fixed-width 160px frame (scaling down on small screens) to eliminate inconsistent image sizing across the catalog.
- Commander browser search now separates commander name and theme inputs, introduces fuzzy theme suggestions, and tightens commander name matching to near-exact results.
- Commander browser no longer auto-scrolls when typing in search fields, keeping focus anchored near the filters.
- Commander theme chips feature larger typography, multi-line wrapping, and a mobile-friendly tap dialog for reading summaries.
- Theme dialog now prefers full editorial descriptions, so longer summaries display completely on mobile.
- Commander theme labels now unescape leading punctuation (e.g., +2/+2 Counters) to avoid stray backslashes in the UI.
- Theme summary dialog now opens when clicking theme chips on desktop as well as mobile.
- Commander list pagination controls now appear above and below the results and automatically scroll to the top when switching pages for quicker navigation.
- Mobile commander rows now feature larger thumbnails and a centered preview modal with expanded card art for improved readability.
- Documentation now captures the MDFC staging plan: README and DOCKER guide highlight the always-on MDFC merge and the optional `DFC_COMPAT_SNAPSHOT=1` workflow for downstream QA.
- Setup filtering now applies security-stamp exclusions case-insensitively so Acorn and Heart promo cards stay out of Commander-legal pools, with a regression test covering the behavior.
- Preview performance GitHub Actions workflow (`.github/workflows/preview-perf-ci.yml`) retired after persistent cold-start failures; run the regression helper script manually as needed.
- Theme catalog pagination reprocesses HTMX fragments after Ajax loads so the “Next” button behaves correctly in the picker and simple catalog views.
- Docker entrypoint now seeds the default `config/themes` files (e.g., `synergy_pairs.yml`, `theme_clusters.yml`, `theme_whitelist.yml`) into mounted volumes so Docker Hub images start with the expected theme catalog baseline.
- Tests: added `test_headless_skips_owned_prompt_when_files_present` to guard the headless runner against regressions when owned card lists are present.
- Configuration docs: docker compose manifests, `.env.example`, and README now enumerate all supported random-mode environment variables with sensible defaults and refreshed flag documentation for the headless runner.
- Owned Cards library tiles now use larger thumbnails and wider columns, and virtualization only activates when more than 800 cards are present to keep scrolling smooth.
- CI installs `httpx` with the rest of the web stack and runs pytest via `python -m pytest` so FastAPI tests resolve the local `code` package correctly.
- Deck summary list view now includes inline flip controls for double-faced cards, keeping text mode feature parity with thumbnail mode.
- Hover panel theme chips now highlight only the themes that triggered a card’s inclusion while the full theme list displays as a muted footer without legacy bracket formatting.
- Finished deck summaries now surface overlap chips using sanitized saved metadata with a themed fallback so exported decks match the live builder UI, and hover overlap pills adopt larger, higher-contrast styling on desktop and mobile.
- Builder card tiles now reserve the card art tap/click for previewing; locking is handled exclusively by the dedicated 🔒 button so mobile users can open the hover panel without accidentally changing locks.
- Builder hover tags now surface normalized theme labels (e.g., “Card Advantage”) and suppress internal `creature_add • tag:` prefixes so build-stage pills match the final deck experience.
- Builder Step 5 commander preview now reuses the in-app hover panel (removing the external Scryfall link) and the hover reasons list auto-expands without an embedded scrollbar for easier reading on desktop and mobile.
- Finished deck commander preview now mirrors builder hover behavior with deck-selected overlap chips, the full commander theme list, and suppresses the external Scryfall link so tapping the thumbnail consistently opens the in-app panel across desktop and mobile.
- Hover card role badge is hidden when no role metadata is available, eliminating the empty pill shown in owned library popovers.
- Random Mode fallback warning no longer displays when all theme inputs are blank.
- Reinstated flip controls for double-faced cards in the hover preview and ensured the overlay button stays in sync with card faces.
- Hover card panel adapts for tap-to-open mobile use with centered positioning, readable scaling, and an explicit close control.
- Mobile hover layout now stacks theme chips beneath the artwork for better readability and cleans up theme formatting.
- Duplicate overlap highlighting on desktop hover has been removed; theme pills now render once without stray bullets even when multiple overlaps are present.
- Headless runner no longer loops on the power bracket prompt when owned card files exist; scripted responses now auto-select defaults with optional `HEADLESS_USE_OWNED_ONLY` / `HEADLESS_OWNED_SELECTION` overrides for automation flows.
- Regenerated `logs/perf/theme_preview_warm_baseline.json` to repair preview performance CI regressions caused by a malformed baseline file and verified the regression gate passes with the refreshed data.
- Tests: added `test_random_reroll_throttle.py` to enforce reroll throttle behavior and `test_random_metrics_and_seed_history.py` to validate opt-in telemetry counters plus seed history exposure.
- Random Mode curated theme pool now documents manual exclusions (`config/random_theme_exclusions.yml`) and ships a reporting script `code/scripts/report_random_theme_pool.py` (`--write-exclusions` emits Markdown/JSON) alongside `docs/random_theme_exclusions.md`. Diagnostics now show manual categories and tag index telemetry.
- Performance guard: `code/scripts/check_random_theme_perf.py` compares the multi-theme profiler output to `config/random_theme_perf_baseline.json` and fails if timings regress beyond configurable thresholds (`--update-baseline` refreshes the file).
- Random Modes UI/API: separate auto-fill controls for Secondary and Tertiary themes with full session, permalink, HTMX, and JSON API support (per-slot state persists across rerolls and exports, and Tertiary auto-fill now automatically enables Secondary to keep combinations valid).
- Random Mode UI gains a lightweight “Clear themes” button that resets all theme inputs and stored preferences in one click for fast Surprise Me reruns.
- Diagnostics: `/status/random_theme_stats` exposes cached commander theme token metrics and the diagnostics dashboard renders indexed commander coverage plus top tokens for multi-theme debugging.
- Random Mode sidecar metadata now records multi-theme details (`primary_theme`, `secondary_theme`, `tertiary_theme`, `resolved_themes`, `combo_fallback`, `synergy_fallback`, `fallback_reason`, plus legacy aliases) in both the summary payload and exported `.summary.json` files.
- Tests: added `test_random_multi_theme_filtering.py` covering triple success, fallback tiers (P+S, P+T, Primary-only, synergy, full pool) and sidecar metadata emission for multi-theme builds.
- Tests: added `test_random_multi_theme_webflows.py` to exercise reroll-same-commander caching and permalink roundtrips for multi-theme runs across HTMX and API layers.
- Random Modes diagnostics: surfaced attempts, timeout_hit, and retries_exhausted in API responses and the HTMX result fragment (gated by SHOW_DIAGNOSTICS); added tests covering retries-exhausted and timeout paths and enabled friendly labels in the UI.
- Random Full Build export parity: random full deck builds now produce the standard artifact set — `<stem>.csv`, `<stem>.txt`, `<stem>_compliance.json` (bracket policy report), and `<stem>.summary.json` (summary with `meta.random` seed/theme/constraints). The random full build API response now includes `csv_path`, `txt_path`, and `compliance` keys (paths) for immediate consumption.
- Environment toggle (opt-out) `RANDOM_BUILD_SUPPRESS_INITIAL_EXPORT` (defaults to active automatically) lets you revert to legacy double-export behavior for debugging by setting `RANDOM_BUILD_SUPPRESS_INITIAL_EXPORT=0`.
- Tests: added random full build export test ensuring exactly one CSV/TXT pair (no `_1` duplicates) plus sidecar JSON artifacts.
- Taxonomy snapshot CLI (`code/scripts/snapshot_taxonomy.py`): writes an auditable JSON snapshot of BRACKET_DEFINITIONS to `logs/taxonomy_snapshots/` with a deterministic SHA-256 hash; skips duplicates unless forced.
- Optional adaptive splash penalty (feature flag): enable with `SPLASH_ADAPTIVE=1`; tuning via `SPLASH_ADAPTIVE_SCALE` (default `1:1.0,2:1.0,3:1.0,4:0.6,5:0.35`).
- Splash penalty analytics: counters now include total off-color cards and penalty reason events; structured logs include event details to support tuning.
- Tests: color identity edge cases (hybrid, colorless/devoid, MDFC single, adventure, color indicator) using synthetic CSV injection via `CARD_INDEX_EXTRA_CSV`.
- Core Refactor Phase A (initial): extracted sampling pipeline (`sampling.py`) and preview cache container (`preview_cache.py`) from `theme_preview.py` with stable public API re-exports.
- Adaptive preview cache eviction heuristic replacing FIFO with env-tunable weights (`THEME_PREVIEW_EVICT_W_HITS`, `_W_RECENCY`, `_W_COST`, `_W_AGE`) and cost thresholds (`THEME_PREVIEW_EVICT_COST_THRESHOLDS`); metrics include eviction counters and last event metadata.
- Performance CI gate: warm-only p95 regression threshold (default 5%) enforced via `preview_perf_ci_check.py`; baseline refresh policy documented.
- Adaptive preview cache eviction heuristic replacing FIFO: protection score combines log(hit_count), recency, build cost bucket, and age penalty with env-tunable weights (`THEME_PREVIEW_EVICT_W_HITS`, `_W_RECENCY`, `_W_COST`, `_W_AGE`) plus cost thresholds (`THEME_PREVIEW_EVICT_COST_THRESHOLDS`). Metrics now include total evictions, by-reason counts (`low_score`, `emergency_overflow`), and last eviction metadata.
- Scryfall name normalization regression test (`test_scryfall_name_normalization.py`) ensuring synergy annotation suffix (` - Synergy (...)`) never leaks into fuzzy/image queries.
- Optional multi-pass performance CI variant (`preview_perf_ci_check.py --multi-pass`) to collect cold vs warm pass stats when diagnosing divergence.
- Deck builder theme spell filler now consumes the shared ThemeContext weighting so user-supplied supplemental themes influence both creature and non-creature selections, with user weight multipliers boosting spell picks in parity with creatures.
- Random theme pool builder loads manual exclusions and always emits `auto_filled_themes` as a list (empty when unused), while enhanced metadata powers diagnostics telemetry.
- Random build summaries normalize multi-theme metadata before embedding in summary payloads and sidecar exports (trimming whitespace, deduplicating/normalizing resolved theme lists).
- Random Mode strict-theme toggle is now fully stateful: the checkbox and hidden field keep session/local storage in sync, HTMX rerolls reuse the flag, and API/full-build responses plus permalinks carry `strict_theme_match` through exports and sidecars.
- Multi-theme filtering now pre-caches lowercase tag lists and builds a reusable token index so AND-combos and synergy fallback avoid repeated pandas `.apply` passes; profiling via `code/scripts/profile_multi_theme_filter.py` shows mean ~9.3ms / p95 ~21ms for cascade checks (seed 42, 300 iterations).
- Random reroll (locked commander) export flow: now reuses builder-exported artifacts when present and records `last_csv_path` / `last_txt_path` inside the headless runner to avoid duplicate suffixed files.
- Summary sidecars for random builds include `locked_commander` flag when rerolling same commander.
- Splash analytics recognize both static and adaptive penalty reasons (shared prefix handling), so existing dashboards continue to work when `SPLASH_ADAPTIVE=1`.
- Random full builds now internally force `RANDOM_BUILD_SUPPRESS_INITIAL_EXPORT=1` (if unset) ensuring only the orchestrated export path executes (eliminates historical duplicate `*_1.csv` / `*_1.txt`). Set `RANDOM_BUILD_SUPPRESS_INITIAL_EXPORT=0` to intentionally restore the legacy double-export (not recommended outside debugging).
- Multi-theme Random UI polish: fallback notices now surface high-contrast icons, focus outlines, and aria-friendly copy; diagnostics badges gain icons/labels; help tooltip converted to an accessible popover with keyboard support; Secondary/Tertiary inputs persist across sessions.
- Core Refactor Phase A continued: separated sampling + cache container; card index & adaptive TTL/background refresh extraction planned (roadmap updated) to further reduce `theme_preview.py` responsibilities.
- Eviction: removed hard 50-entry minimum to support low-limit unit tests; production should set `THEME_PREVIEW_CACHE_MAX` accordingly.
- Governance: README governance appendix now documents taxonomy snapshot usage and rationale.
- Removed hard minimum (50) floor in eviction capacity logic to allow low-limit unit tests; operational environments should set `THEME_PREVIEW_CACHE_MAX` appropriately.
- Performance gating formalized: CI fails if warm p95 regression > configured threshold (default 5%). Baseline refresh policy: only update committed warm baseline when (a) intentional performance improvement >10% p95, or (b) unavoidable drift exceeds threshold and is justified in CHANGELOG entry.
- Random UI Surprise Me rerolls now keep user-supplied theme inputs instead of adopting fallback combinations, and reroll-same-commander builds reuse cached resolved themes without re-running the filter cascade.
- Random build duplicate export issue resolved: suppression of the initial builder auto-export prevents creation of suffixed duplicate decklists.
- Random Mode UI regressions (deck summary toggle & hover preview) fixed by replacing deferred script execution with inline handlers and an HTMX load hook.
- Phase D close-out: strict alias enforcement promoted to hard fail in CI (`validate_theme_catalog.py --strict-alias`) removing previous soft warning behavior.
- Phase D close-out: minimum example commander enforcement (>=5) now mandatory; failing themes block CI.
- Tagging: Added archetype detection for Pillowfort, Politics, Midrange, and Toolbox with new pattern & specific card heuristics.
- Tagging orchestration: Extended `tag_by_color` to execute new archetype taggers in sequence before bracket policy application.
- Governance workflows: Introduced `.github/workflows/editorial_governance.yml` and `.github/workflows/editorial_lint.yml` for isolated lint + governance checks.
- Editorial schema: Added `editorial_quality` to both YAML theme model and catalog ThemeEntry Pydantic schemas.
- Editorial data artifacts: Added `config/themes/description_mapping.yml`, `synergy_pairs.yml`, `theme_clusters.yml`, `theme_popularity_metrics.json`, `description_fallback_history.jsonl`.
- Editorial tooling: `synergy_promote_fill.py` new flags `--no-generic-pad` (allow intentionally short example_cards without color/generic padding), `--annotate-color-fallback-commanders` (explain color fallback commander selections), and `--use-master-cards` (opt-in to consolidated `cards.csv` sourcing; shard `[color]_cards.csv` now default).
- Name canonicalization for card ingestion: duplicate split-face variants like `Foo // Foo` collapse to `Foo`; when master enabled, prefers `faceName`.
- Commander rebuild annotation: base-first rebuild now appends ` - Color Fallback (no on-theme commander available)` to any commander added purely by color identity.
- Randomizer groundwork: added a small seeded RNG utility (`code/random_util.py`) and determinism unit tests; threaded RNG through Phase 3 (creatures) and Phase 4 (spells) for deterministic sampling when seeded.
- Random Modes (alpha): thin wrapper entrypoint `code/deck_builder/random_entrypoint.py` to select a commander deterministically by seed, plus a tiny frozen dataset under `csv_files/testdata/` and tests `code/tests/test_random_determinism.py`.
- Theme Editorial: automated example card/commander suggestion + enrichment (`code/scripts/generate_theme_editorial_suggestions.py`).
- Synergy commanders: derive 3/2/1 candidates from top three synergies with legendary fallback; stored in `synergy_commanders` (annotated) separate from `example_commanders`.
- Per-synergy annotations: `Name - Synergy (Synergy Theme)` applied to promoted example commanders and retained in synergy list for transparency.
- Augmentation flag `--augment-synergies` to repair sparse `synergies` arrays (e.g., inject `Counters Matter`, `Proliferate`).
- Phase D (Deferred items progress): enumerated `deck_archetype` list + validation, derived `popularity_bucket` classification (frequency -> Rare/Niche/Uncommon/Common/Very Common), deterministic editorial seed (`EDITORIAL_SEED`) for stable inference ordering, aggressive fill mode (`EDITORIAL_AGGRESSIVE_FILL=1`) to pad ultra-sparse themes, env override `EDITORIAL_POP_BOUNDARIES` for bucket thresholds.
- Catalog backfill: build script can now write auto-generated `description` and derived/pinned `popularity_bucket` back into individual YAML files via `--backfill-yaml` (or `EDITORIAL_BACKFILL_YAML=1`) with optional overwrite `--force-backfill-yaml`.
- Catalog output override: new `--output <path>` flag on `build_theme_catalog.py` enables writing an alternate JSON (used by tests) without touching the canonical `theme_list.json` or performing YAML backfill.
- Editorial lint escalation: new flags `--require-description` / `--require-popularity` (or env `EDITORIAL_REQUIRE_DESCRIPTION=1`, `EDITORIAL_REQUIRE_POPULARITY=1`) to enforce presence of description and popularity buckets; strict mode also treats them as errors.
- Tests: added `test_theme_catalog_generation.py` covering deterministic seed reproducibility, popularity boundary overrides, absence of YAML backfill on alternate output, and presence of descriptions.
- Editorial fallback summary: optional inclusion of `description_fallback_summary` in `theme_list.json` via `EDITORIAL_INCLUDE_FALLBACK_SUMMARY=1` for coverage metrics (generic vs specialized descriptions) and prioritization.
- External description mapping (Phase D): curators can now add/override auto-description rules via `config/themes/description_mapping.yml` without editing code (first match wins, `{SYNERGIES}` placeholder supported).
- Example card population now sources exclusively from shard color CSV files by default (avoids variant noise from master `cards.csv`). Master file usage is explicit opt-in via `--use-master-cards`.
- Heuristic text index aligned with shard-only sourcing and canonical name normalization to prevent duplicate staple leakage.
- Terminology migration: internal model field `provenance` fully migrated to `metadata_info` across code, tests, and 700+ YAML catalog files via automated script (`migrate_provenance_to_metadata_info.py`). Backward-compatible aliasing retained temporarily; deprecation window documented.
- Example card duplication suppression: `synergy_promote_fill.py` adds `--common-card-threshold` and `--print-dup-metrics` to filter overly common generic staples based on a pre-run global frequency map.
- Synergy noise suppression: "Legends Matter" / "Historics Matter" pairs are now stripped from every other theme (they were ubiquitous due to all legendary & historic cards carrying both tags). Only mutual linkage between the two themes themselves is retained.
- Theme merge build now always forces per-theme YAML export so `config/themes/catalog/*.yml` stays synchronized with `theme_list.json`. New env `THEME_YAML_FAST_SKIP=1` allows skipping YAML regeneration only on fast-path refreshes (never on full builds) if desired.
-`synergy_commanders` now excludes any commanders already promoted into `example_commanders` (deduped by base name after annotation).
- Promotion logic ensures a configurable minimum (default 5) example commanders via annotated synergy promotions.
- Regenerated per-theme YAML files are environment-dependent (card pool + tags); README documents that bulk committing the entire regenerated catalog is discouraged to avoid churn.
- Lint enhancements: archetype enumeration expanded (Combo, Aggro, Control, Midrange, Stax, Ramp, Toolbox); strict mode now promotes cornerstone missing examples to errors; popularity bucket value validation.
- Regression thresholds tightened for generic description fallback usage (see `test_theme_description_fallback_regression.py`), lowering allowed generic total & percentage to drive continued specialization.
- build script now auto-exports Phase A YAML catalog if missing before attempting YAML backfill (safeguard against accidental directory deletion).
- Legendary Artifact Vehicles / Spacecraft that have printed power & toughness
- Any card whose rules text contains "can be your commander" (covers specific planeswalkers, artifacts, others)
are auto‑eligible. Plain Legendary Enchantments (non‑creature), Legendary Planeswalkers without the explicit text, and generic Legendary Artifacts are no longer incorrectly included.
-`provenance` catalog/YAML key: retained as read-only alias; will be removed after two minor releases in favor of `metadata_info`. Warnings to be added prior to removal.
- **CLI enhancement: Enhanced help text with type indicators** - All CLI arguments now show expected value types (PATH, NAME, INT, BOOL) and organized into logical groups
- **CLI enhancement: Theme tag name support** - Theme selection by name instead of index: `--primary-tag`, `--secondary-tag`, `--tertiary-tag` as alternatives to numeric choices
- **CLI enhancement: Include/exclude CLI support** - Full CLI parity for include/exclude with `--include-cards`, `--exclude-cards`, `--enforcement-mode`, `--allow-illegal`, `--fuzzy-matching`
- **CLI enhancement: Console summary printing** - Detailed include/exclude summary output for headless builds with diagnostics and validation results
- Docker image: expanded entrypoint seeding now copies all default card list JSON files (extra_turns, game_changers, mass_land_denial, tutors_nonland, etc.) and brackets.yml when missing, preventing missing list issues with mounted blank config volumes
- Bracket policy enforcement: global pool-level prune for disallowed categories when limits are 0 (e.g., Game Changers in Brackets 1–2). Applies to both Web and headless runs.
- Inline enforcement UI: violations surface before the summary; Continue/Rerun disabled until you replace or remove flagged cards. Alternatives are role-consistent and exclude commander/locked/in-deck cards.
- Auto-enforce option: `WEB_AUTO_ENFORCE=1` to apply the enforcement plan and re-export when compliance fails.
- Summary/export mismatch in headless JSON runs where disallowed cards could be pruned from exports but appear in summaries; global prune ensures consistent state across phases and reports.
- These lists underpin the bracket enforcement feature introduced in 2.2.5; shipping them as a follow-up release ensures consistent results across Web and headless runs.
## [2.2.5] - 2025-09-03
### Added
- Bracket WARN thresholds: `config/brackets.yml` supports optional `<category>_warn` keys (e.g., `tutors_nonland_warn`, `extra_turns_warn`). Compliance now returns PASS/WARN/FAIL; low brackets (1–2) conservatively WARN on presence of tutors/extra_turns when thresholds aren’t provided.
- Web UI compliance polish: the panel auto-opens on non-compliance (WARN/FAIL) and shows a colored overall status chip (green/WARN amber/red). WARN items now render as tiles with a subtle amber style and a WARN badge; tiles and enforcement actions remain FAIL-only.
- Tests: added coverage to ensure WARN thresholds from YAML are applied and that fallback WARN behavior appears for low brackets.
### Changed
- Web: flagged metadata now includes WARN categories with a `severity` field to support softer UI rendering for advisory cases.
- Web: Setup/Refresh prompt modal shown on Create when environment is missing or stale; routes to `/setup/running` (force on stale) and transitions into the progress view. Template: `web/templates/build/_setup_prompt_modal.html`.
- Orchestrator helpers: `is_setup_ready()` and `is_setup_stale()` for non-invasive readiness/staleness checks from the UI.
- Env flags for setup behavior: `WEB_AUTO_SETUP` (default 1) to enable/disable auto setup, and `WEB_AUTO_REFRESH_DAYS` (default 7) to tune staleness.
- Mobile UI scaling and layout fixed across steps; overlap in DevTools emulation resolved with CSS variable offsets for sticky elements.
- Multi-Copy is now explicitly opt-in from the New Deck modal; suggestions are filtered to only show archetypes whose matched tags intersect the user-selected themes (e.g., Rabbit Kindred shows only Hare Apparent).
- Staged build context creation centralized via `web/services/build_utils.start_ctx_from_session` and applied across Step 5 flows in `web/routes/build.py` (New submit, Continue, Start, Rerun, Rewind).
- Owned-cards set creation centralized via `web/services/build_utils.owned_set()` and used in `web/routes/build.py`, `web/routes/configs.py`, and `web/routes/decks.py`.
- Step 5: replaced ad-hoc empty context assembly with `web/services/build_utils.step5_empty_ctx()` in GET `/build/step5` and `reset-stage`.
- Builder introspection: adopted `builder_present_names()` and `builder_display_map()` helpers in `web/routes/build.py` for locked-cards and alternatives, reducing duplication and improving casing consistency.
- Alternatives endpoint now renders the new partial (`build/_alternatives.html`) via Jinja and caches the HTML (no more string-built HTML in the route).
- Continue button responsiveness on mobile fixed (eliminated sticky overlap); Multi-Copy application preserved across New Deck submit; emulator misclicks resolved.
- Banner subtitle now stays inline inside the header when the menu is collapsed (no overhang/wrap to a new row).
- Combos & Synergies: detect curated two-card combos/synergies and surface them in a chip-style panel with badges (cheap/early, setup) on Step 5 and Finished Decks.
- Dual hover previews for combo rows: hovering a combo shows both cards side-by-side in the standard preview popout; individual names still preview a single card.
- Headless (Web Configs): JSON configs now persist and honor combo preferences:
-`prefer_combos` (bool)
-`combo_target_count` (int)
-`combo_balance` ("early" | "late" | "mix")
Exported interactive run-config JSON includes these fields when used.
- Finished Deck summary includes detected combos/synergies and curated list version badges.
- When `prefer_combos` is enabled, Auto-Complete Combos runs before theme fill/monolithic spells so partners aren’t clamped away. Existing completed pairs count toward the target before adding partners.
- Step 5 Combos panel updated to the same chip-style as Finished Decks for consistency.
- Auto-combos respect color identity by resolving from the filtered pool only; off-color/unavailable partners are skipped.
- Added type/mana enrichment for auto-added partners and lock placeholders to avoid “Other” category leakage.
- Multi-copy archetypes (Web): opt-in modal suggests packages like Persistent Petitioners, Dragon's Approach, and Shadowborn Apostle when viable; choose quantity and optionally add Thrumming Stone. Applied as the first stage with ideal count adjustments and a per-stage 100-card safety clamp. UI surfaces adjustments and a clamp chip.
### Changed
- Multi-copy modal now appears immediately after commander selection (pre-build) in Step 2. This reduces surprise and lets users make a choice earlier.
- Stage order updated so the Multi-Copy package is applied first in Step 5, with land steps following on the next Continue. Lands now account for the package additions when filling.
### Fixed
- Ensured apostrophes in multi-copy card names remain safe in templates while rendering correctly in the UI.
- Web UI performance: optional virtualized grids/lists in Step 5 and Owned (enable with `WEB_VIRTUALIZE=1`).
- Virtualization diagnostics overlay (when `SHOW_DIAGNOSTICS=1`); press `v` to toggle per‑grid overlays and a global summary bubble with visible range, totals, render time, and counters.
- Image polish: lazy‑loading with responsive `srcset/sizes` and LQIP blur/fade‑in for Step 5 and Owned thumbnails and the commander preview image.
- Short‑TTL fragment caching for template partials (e.g., finished deck summaries and config run summaries) to reduce re‑render cost.
- Prefer-owned option in Review: bias selection toward owned cards while allowing unowned fallback (stable reorder + gentle weight boosts applied across creatures and spells)
- Owned page enhancements: export TXT/CSV, sort controls, live "N shown," color identity dots, exact color-identity combo filters (incl. 4-color), viewport-filling list, and scrollbar styling
- Finished Decks: theme filters converted to a dropdown with shareable state
- Staged build: optional "Show skipped stages" toggle to surface stages that added no cards with a clear annotation
- Owned/Not-owned badges visible across views; consolidated CSS for consistent placement
- Visual summaries: Mana Curve, Color Pips and Sources charts with cross-highlighting to cards; tooltips show per-color card lists and include a Copy action
- Source detection: include non-land mana producers and colorless 'C'; basic lands reliably counted; fetch lands excluded as sources
- Favicon support: `/favicon.ico` served (ICO with PNG fallback)
- Theme UX: Header includes a Reset Theme control to clear browser preference and reapply server default (THEME) or system mapping. Diagnostics page shows resolved theme and stored preference with a reset action.
- 404s from Starlette now render the HTML 404 page when requested from a browser (Accept: text/html)
- Owned page UX: full-size preview now pops on thumbnail hover (not the name); selection highlight tightened to the thumbnail only and changed to white for better contrast; Themes in the hover popout render as a larger bullet list with a brighter "THEMES" label
- Image robustness: standardized `data-card-name` on all Scryfall images and centralized retry logic (thumbnails + previews) with version fallbacks (small/normal/large) and a single cache-bust refresh on final failure; removed the previous hover-image cache to reduce complexity and overhead
- Layout polish: fixed sidebar remains full-height under the banner with a subtle right-edge shadow for depth; grid updated to prevent content squish; extra scroll removed; footer pinned when content is short.
- Deck Summary list view: rows use fixed tracks for count, ×, name, and owned columns (monospace tabular numerals) to ensure perfect alignment; highlight is an inset box-shadow on the name to avoid layout shifts; long names ellipsize with a tooltip; list starts directly under the type header and remains stable on full-screen widths
- Deck summary alignment issues in some sections (e.g., Enchantments) fixed by splitting the count and the × into separate columns and pinning the owned flag to a fixed width; prevents drift across responsive breakpoints
- Banned list filtering applied consistently to all color/guild CSV generation paths with exact, case-insensitive matching on name/faceName (e.g., Hullbreacher, Dockside Extortionist, and Lutri are excluded)