- document random theme exclusions, perf guard tooling, and roadmap completion - tighten random reroll UX: strict theme persistence, throttle handling, export parity, diagnostics updates - add regression coverage for telemetry counters, multi-theme flows, and locked rerolls; refresh README and notes Tests: pytest -q (fast random + telemetry suites)
49 KiB
- Random Modes (alpha): added env flags RANDOM_MODES, RANDOM_UI, RANDOM_MAX_ATTEMPTS, RANDOM_TIMEOUT_MS.
- Determinism: CSV_FILES_DIR override to point tests to csv_files/testdata; permalink now carries optional random fields (seed/theme/constraints).
Changelog
All notable changes to this project will be documented in this file.
This format follows Keep a Changelog principles and aims for Semantic Versioning.
How we version
- Semantic Versioning: MAJOR.MINOR.PATCH (e.g., v1.2.3). Pre-releases use -alpha/-beta/-rc.
- Tags are created as
vX.Y.Zon the default branch; releases and Docker images use that exact version andlatest. - Change entries prefer the Keep a Changelog types: Added, Changed, Fixed, Removed, Deprecated, Security.
- Link PRs/issues inline when helpful, e.g., (#123) or [#123]. Reference-style links at the bottom are encouraged for readability.
[Unreleased]
Added
- Tests: added
test_random_reroll_throttle.pyto enforce reroll throttle behavior andtest_random_metrics_and_seed_history.pyto 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 scriptcode/scripts/report_random_theme_pool.py(--write-exclusionsemits Markdown/JSON) alongsidedocs/random_theme_exclusions.md. Diagnostics now show manual categories and tag index telemetry. - Performance guard:
code/scripts/check_random_theme_perf.pycompares the multi-theme profiler output toconfig/random_theme_perf_baseline.jsonand fails if timings regress beyond configurable thresholds (--update-baselinerefreshes 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_statsexposes 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.jsonfiles. - Tests: added
test_random_multi_theme_filtering.pycovering 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.pyto exercise reroll-same-commander caching and permalink roundtrips for multi-theme runs across HTMX and API layers. - Random Mode multi-theme groundwork: backend now supports
primary_theme,secondary_theme,tertiary_themewith deterministic AND-combination cascade (P+S+T → P+S → P+T → P → synergy-overlap → full pool). Diagnostics fields (resolved_themes,combo_fallback,synergy_fallback,fallback_reason) added toRandomBuildResult(UI wiring pending). - Tests: added
test_random_surprise_reroll_behavior.pycovering Surprise Me input preservation and locked commander reroll cache reuse. - Locked commander reroll path now produces full artifact parity (CSV, TXT, compliance JSON, summary JSON) identical to Surprise builds.
- Random reroll tests for: commander lock invariance, artifact presence, duplicate export prevention, and form vs JSON submission.
- Roadmap document
logs/roadmaps/random_multi_theme_roadmap.mdcapturing design, fallback strategy, diagnostics, and incremental delivery plan. - 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 withmeta.randomseed/theme/constraints). The random full build API response now includescsv_path,txt_path, andcompliancekeys (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 settingRANDOM_BUILD_SUPPRESS_INITIAL_EXPORT=0. - Tests: added random full build export test ensuring exactly one CSV/TXT pair (no
_1duplicates) plus sidecar JSON artifacts. - Taxonomy snapshot CLI (
code/scripts/snapshot_taxonomy.py): writes an auditable JSON snapshot of BRACKET_DEFINITIONS tologs/taxonomy_snapshots/with a deterministic SHA-256 hash; skips duplicates unless forced. - Optional adaptive splash penalty (feature flag): enable with
SPLASH_ADAPTIVE=1; tuning viaSPLASH_ADAPTIVE_SCALE(default1: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) fromtheme_preview.pywith 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. - ETag header for basic client-side caching of catalog fragments.
- Theme catalog performance optimizations: precomputed summary maps, lowercase search haystacks, memoized filtered slug cache (keyed by
(etag, params)) for sub‑50ms warm queries. - Theme preview endpoint:
GET /themes/api/theme/{id}/preview(and HTML fragment) returning representative sample (curated examples, curated synergy examples, heuristic roles: payoff / enabler / support / wildcard / synthetic). - Commander bias heuristics (color identity restriction, diminishing synergy overlap bonus, direct theme match bonus).
- In‑memory TTL cache (default 600s) for previews with build time tracking.
- Metrics endpoint
GET /themes/metrics(diagnostics gated) exposing preview & catalog counters, cache stats, percentile build times. - Governance metrics:
example_enforcement_active,example_enforce_threshold_pctsurfaced once curated coverage passes threshold (default 90%). - Skeleton loading states for picker list, preview modal, and initial shell.
- Diagnostics flag
WEB_THEME_PICKER_DIAGNOSTICS=1enabling fallback description flag, editorial quality badges, uncapped synergy toggle, YAML fetch, metrics endpoint. - Cache bust hooks on catalog refresh & tagging completion clearing filter & preview caches (metrics include
preview_last_bust_at). - Optional filter cache prewarm (
WEB_THEME_FILTER_PREWARM=1) priming common filter combinations; metrics includefilter_prewarmed. - Preview modal UX: role chips, condensed reasons line, hover tooltip with multiline heuristic reasons, export bar (CSV/JSON) honoring curated-only toggle.
- Server authoritative mana & color identity ingestion (exposes
mana_cost,color_identity_list,pip_colors) replacing client-side parsing. - 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.
Changed
- Random theme pool builder loads manual exclusions and always emits
auto_filled_themesas 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_matchthrough 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
.applypasses; profiling viacode/scripts/profile_multi_theme_filter.pyshows mean ~9.3 ms / p95 ~21 ms 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_pathinside the headless runner to avoid duplicate suffixed files. - Summary sidecars for random builds include
locked_commanderflag 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). SetRANDOM_BUILD_SUPPRESS_INITIAL_EXPORT=0to 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.
- Picker list & API use optimized fast filtering path (
filter_slugs_fast) replacing per-request linear scans. - Preview sampling: curated examples pinned first, diversity quotas (~40% payoff / 40% enabler+support / 20% wildcard), synthetic placeholders only if underfilled.
- Sampling refinements: rarity diminishing weight, splash leniency (single off-color allowance with penalty for 4–5 color commanders), role saturation penalty, refined commander overlap scaling curve.
- Hover / DFC UX unified: single hover panel, overlay flip control (keyboard + persisted face), enlarged thumbnails (110px→165px→230px), activation limited to thumbnails.
- Removed legacy client-side mana & color identity parsers (now server authoritative fields included in preview items and export endpoints).
- Core Refactor Phase A continued: separated sampling + cache container; card index & adaptive TTL/background refresh extraction planned (roadmap updated) to further reduce
theme_preview.pyresponsibilities. - Eviction: removed hard 50-entry minimum to support low-limit unit tests; production should set
THEME_PREVIEW_CACHE_MAXaccordingly. - 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_MAXappropriately. - 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.
Fixed
- 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.
- Removed redundant template environment instantiation causing inconsistent navigation state.
- Ensured preview cache key includes catalog ETag to prevent stale sample reuse after catalog reload.
- Explicit cache bust after tagging/catalog rebuild prevents stale preview exposure.
- 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.
Editorial / Themes
- Enforce minimum
example_commandersthreshold (>=5) in CI; lint fails builds when a non-alias theme drops below threshold. - Added enforcement test
test_theme_editorial_min_examples_enforced.pyto guard regression. - Governance workflow updated to pass
--enforce-min-examplesand setEDITORIAL_MIN_EXAMPLES_ENFORCE=1. - Clarified lint script docstring and behavior around enforced minimums.
- (Planned next) Removal of deprecated alias YAMLs & promotion of strict alias validation to hard fail (post grace window).
Added
-
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_colorto execute new archetype taggers in sequence before bracket policy application. -
Governance workflows: Introduced
.github/workflows/editorial_governance.ymland.github/workflows/editorial_lint.ymlfor isolated lint + governance checks. -
Editorial schema: Added
editorial_qualityto 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: New scripts for enrichment & governance:
augment_theme_yaml_from_catalog.py,autofill_min_examples.py,pad_min_examples.py,cleanup_placeholder_examples.py,purge_anchor_placeholders.py,ratchet_description_thresholds.py,report_editorial_examples.py,validate_description_mapping.py,synergy_promote_fill.py(extension),run_build_with_fallback.py,migrate_provenance_to_metadata_info.py,theme_example_cards_stats.py. -
Tests: Added governance + regression suite (
test_theme_editorial_min_examples_enforced.py,test_theme_description_fallback_regression.py,test_description_mapping_validation.py,test_editorial_governance_phase_d_closeout.py,test_synergy_pairs_and_metadata_info.py,test_synergy_pairs_and_provenance.py,test_theme_catalog_generation.py, updatedtest_theme_merge_phase_b.py& validation Phase C test) for editorial pipeline stability. -
Editorial tooling:
synergy_promote_fill.pynew 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 consolidatedcards.csvsourcing; shard[color]_cards.csvnow default). -
Name canonicalization for card ingestion: duplicate split-face variants like
Foo // Foocollapse toFoo; when master enabled, prefersfaceName. -
Commander rebuild annotation: base-first rebuild now appends
- Color Fallback (no on-theme commander available)to any commander added purely by color identity. -
Roadmap: Added
logs/roadmaps/theme_editorial_roadmap.mddocumenting future enhancements & migration plan. -
Theme catalog Phase B: new unified merge script
code/scripts/build_theme_catalog.py(opt-in via THEME_CATALOG_MODE=merge) combining analytics + curated YAML + whitelist governance with metadata block output. -
Theme metadata:
theme_list.jsonnow includesmetadata_info(formerlyprovenance) capturing generation context (mode, generated_at, curated_yaml_files, synergy_cap, inference version). Legacy key still parsed for backward compatibility. -
Theme governance: whitelist configuration
config/themes/theme_whitelist.yml(normalization, always_include, protected prefixes/suffixes, enforced synergies, synergy_cap). -
Theme extraction: dynamic ingestion of CSV-only tags (e.g., Kindred families) and PMI-based inferred synergies (positive PMI, co-occurrence threshold) blended with curated pairs.
-
Enforced synergy injection for counters/tokens/graveyard clusters (e.g., Proliferate, Counters Matter, Graveyard Matters) before capping.
-
Test coverage:
test_theme_whitelist_and_synergy_cap.pyensuring enforced synergies present and cap (5) respected. -
Dependency: added PyYAML (optional runtime dependency for governance file parsing).
-
CI: additional checks to improve stability and reproducibility.
-
Tests: broader coverage for validation and web flows.
-
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.pyto select a commander deterministically by seed, plus a tiny frozen dataset undercsv_files/testdata/and testscode/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 fromexample_commanders. -
Per-synergy annotations:
Name - Synergy (Synergy Theme)applied to promoted example commanders and retained in synergy list for transparency. -
Augmentation flag
--augment-synergiesto repair sparsesynergiesarrays (e.g., injectCounters Matter,Proliferate). -
Lint upgrades (
code/scripts/lint_theme_editorial.py): validates annotation correctness, filtered synergy duplicates, minimum example_commanders, and base-name deduping. -
Pydantic schema extension (
type_definitions_theme_catalog.py) addingsynergy_commandersand editorial fields to catalog model. -
Phase D (Deferred items progress): enumerated
deck_archetypelist + validation, derivedpopularity_bucketclassification (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 overrideEDITORIAL_POP_BOUNDARIESfor bucket thresholds. -
Catalog backfill: build script can now write auto-generated
descriptionand derived/pinnedpopularity_bucketback into individual YAML files via--backfill-yaml(orEDITORIAL_BACKFILL_YAML=1) with optional overwrite--force-backfill-yaml. -
Catalog output override: new
--output <path>flag onbuild_theme_catalog.pyenables writing an alternate JSON (used by tests) without touching the canonicaltheme_list.jsonor performing YAML backfill. -
Editorial lint escalation: new flags
--require-description/--require-popularity(or envEDITORIAL_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.pycovering 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_summaryintheme_list.jsonviaEDITORIAL_INCLUDE_FALLBACK_SUMMARY=1for 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.ymlwithout editing code (first match wins,{SYNERGIES}placeholder supported).
Changed
- Archetype presence test now gracefully skips when generated catalog YAML assets are absent, avoiding false negatives in minimal environments.
- Tag constants and tagger extended; ordering ensures new archetype tags applied after interaction tagging but before bracket policy enforcement.
- CI strict alias step now fails the build instead of continuing on error.
- 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
provenancefully migrated tometadata_infoacross 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.pyadds--common-card-thresholdand--print-dup-metricsto filter overly common generic staples based on a pre-run global frequency map. - Synergy lists for now capped at 5 entries (precedence: curated > enforced > inferred) to improve UI scannability.
- Curated synergy matrix expanded (tokens, spells, artifacts/enchantments, counters, lands, graveyard, politics, life, tribal umbrellas) with noisy links (e.g., Burn on -1/-1 Counters) suppressed via denylist + PMI filtering.
- 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/*.ymlstays synchronized withtheme_list.json. New envTHEME_YAML_FAST_SKIP=1allows skipping YAML regeneration only on fast-path refreshes (never on full builds) if desired. - Tests: refactored to use pytest assertions and cleaned up fixtures/utilities to reduce noise and deprecations.
- Tests: HTTP-dependent tests now skip gracefully when the local web server is unavailable.
synergy_commandersnow excludes any commanders already promoted intoexample_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).
Fixed
- Commander eligibility logic was overly permissive. Now only:
- Missing secondary synergies (e.g.,
Proliferateon counter subthemes) restored via augmentation heuristic preventing empty synergy follow-ons.- Legendary Creatures (includes Artifact/Enchantment Creatures)
- 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.
- Removed one-off / low-signal themes (global frequency <=1) except those protected or explicitly always included via whitelist configuration.
- Tests: reduced deprecation warnings and incidental failures; improved consistency and reliability across runs.
Deprecated
provenancecatalog/YAML key: retained as read-only alias; will be removed after two minor releases in favor ofmetadata_info. Warnings to be added prior to removal.
[2.2.10] - 2025-09-11
Changed
- Web UI: Test Hand uses a default fanned layout on desktop with tightened arc and 40% overlap; outer cards sit lower for a full-arc look
- Desktop Test Hand card size set to 280×392; responsive sizes refined at common breakpoints
- Theme controls moved from the top banner to the bottom of the left sidebar; sidebar made a flex column with the theme block anchored at the bottom
- Mobile banner simplified to show only Menu, title; spacing and gaps tuned to prevent overflow and wrapping
Fixed
- Prevented mobile banner overflow by hiding non-essential items and relocating theme controls
- Ensured desktop sizing wins over previous inline styles by using global CSS overrides; cards no longer shrink due to flex
[2.2.9] - 2025-09-10
Added
- Dynamic misc utility land EDHREC keep range env docs and theme weighting overrides
- Land alternatives randomization (12 suggestions from random top 60–100 window) and land-only parity filtering
Changed
- Compose and README updated with new misc land tuning environment variables
Fixed
- Step 5 scroll flicker at bottom for small grids (virtualization skip <80 items + overscroll containment)
- Fetch lands excluded from misc land step; mono-color rainbow filtering improvements
[2.2.8] - 2025-09-10
[2.2.7] - 2025-09-10
Added
- Comprehensive structured logging for include/exclude operations with event tracking
- Include/exclude card lists feature with
ALLOW_MUST_HAVES=trueenvironment variable flag - Phase 1 exclude-only implementation: filter cards from deck building pool before construction
- Web UI "Advanced Options" section with exclude cards textarea and file upload (.txt)
- Live validation for exclude cards with count and limit warnings (max 15 excludes)
- JSON export/import support preserving exclude_cards in permalink system
- Fuzzy card name matching with punctuation/spacing normalization
- Comprehensive backward compatibility tests ensuring existing workflows unchanged
- Performance benchmarks: exclude filtering <50ms for 20k+ cards, validation API <100ms
- File upload deduplication and user feedback for exclude lists
- Extended DeckBuilder schema with full include/exclude configuration support
- Include/exclude validation with fuzzy matching, strict enforcement, and comprehensive diagnostics
- Full JSON round-trip functionality preserving all include/exclude configuration in headless and web modes
- Comprehensive test suite covering validation, persistence, fuzzy matching, and backward compatibility
- Engine integration with include injection after lands, before creatures/spells with ordering tests
- Exclude re-entry prevention ensuring blocked cards cannot re-enter via downstream heuristics
- Web UI enhancement with two-column layout, chips/tag UI, and real-time validation
- EDH format compliance checking for include/exclude cards against commander color identity
Changed
- Test organization: Moved all test files from project root to centralized
code/tests/directory for better structure - 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: Ideal count arguments - New CLI flags for deck composition:
--ramp-count,--land-count,--basic-land-count,--creature-count,--removal-count,--wipe-count,--card-advantage-count,--protection-count - CLI enhancement: Theme tag name support - Theme selection by name instead of index:
--primary-tag,--secondary-tag,--tertiary-tagas 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
- Enhanced fuzzy matching with 300+ Commander-legal card knowledge base and popular/iconic card prioritization
- Card constants refactored to dedicated
builder_constants.pywith functional organization - Fuzzy match confirmation modal with dark theme support and card preview functionality
- Include/exclude summary panel showing build impact with success/failure indicators and validation issues
- Comprehensive Playwright end-to-end test suite covering all major user flows and mobile layouts
- Mobile responsive design with bottom-floating build controls for improved thumb navigation
- Two-column grid layout for mobile build controls reducing vertical space usage by ~50%
- Mobile horizontal scrolling prevention with viewport overflow controls and setup status optimization
- Enhanced visual feedback with warning indicators (⚠️ over-limit, ⚡ approaching limit) and color coding
- Performance test framework tracking validation and UI response times
- Advanced list size validation with live count displays and visual warnings
- Enhanced validation endpoint with comprehensive diagnostics and conflict detection
- Chips/tag UI for per-card removal with visual distinction (green includes, red excludes)
- Staging system architecture support with custom include injection runner for web UI
- Complete include/exclude functionality working end-to-end across both web UI and CLI interfaces
- Enhanced list size validation UI with visual warning system (⚠️ over-limit, ⚡ approaching limit) and color coding
- Legacy endpoint transformation maintaining exact message formats for seamless integration with existing workflows
Fixed
- JSON config files are now properly re-exported after bracket compliance enforcement and auto-swapping
- Mobile horizontal scrolling issues resolved with global viewport overflow controls
- Mobile UI setup status stuttering eliminated by removing temporary "Setup complete" message displays
- Mobile build controls accessibility improved with bottom-floating positioning for thumb navigation
- Mobile viewport breakpoint expanded from 720px to 1024px for broader device compatibility
- 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
[2.2.6] - 2025-09-04
Added
- 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=1to apply the enforcement plan and re-export when compliance fails.
Changed
- Spells and creatures phases apply bracket-aware pre-filters to reduce violations proactively.
- Compliance detection for Game Changers falls back to in-code constants when
config/card_lists/game_changers.jsonis empty. - Data refresh: updated static lists used by bracket compliance/enforcement with current card names and metadata:
config/card_lists/extra_turns.jsonconfig/card_lists/game_changers.jsonconfig/card_lists/mass_land_denial.jsonconfig/card_lists/tutors_nonland.jsonEach list includeslist_version: "manual-2025-09-04"andgenerated_at.
Fixed
- 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.
Notes
- 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.ymlsupports optional<category>_warnkeys (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
severityfield to support softer UI rendering for advisory cases.
[2.2.4] - 2025-09-02
Added
- Mobile: Collapsible left sidebar with persisted state; sticky build controls adjusted for mobile header.
- New Deck modal integrates Multi-Copy suggestions (opt-in) and commander/theme preview.
- 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()andis_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, andWEB_AUTO_REFRESH_DAYS(default 7) to tune staleness. - Step 5 error context helper:
web/services/build_utils.step5_error_ctx()to standardize error payloads for_step5.html. - Templates: reusable lock/unlock button macro at
web/templates/partials/_macros.html. - Templates: Alternatives panel partial at
web/templates/build/_alternatives.html(renders candidates with Owned-only toggle and Replace actions).
Tests
- Added smoke/unit tests covering:
summary_utils.summary_ctx()build_utils.start_ctx_from_session()(monkeypatched orchestrator)orchestratorstaleness/setup pathsbuild_utils.step5_error_ctx()shape and flags
Changed
- 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).
- Web cleanup: centralized combos/synergies detection and model/version loading in
web/services/combo_utils.pyand refactored routes to use it:routes/build.py(Combos panel),routes/configs.py(run results),routes/decks.py(finished/compare), and diagnostics endpoint inapp.py.
- Create (New Deck) flow: no longer auto-runs setup on submit; instead presents a modal prompt to run setup/refresh when needed.
- Step 5 builder flow: deduplicated template context assembly via
web/services/build_utils.pyhelpers and refactoredweb/routes/build.pyaccordingly (fewer repeated dicts, consistent fields). - Staged build context creation centralized via
web/services/build_utils.start_ctx_from_sessionand applied across Step 5 flows inweb/routes/build.py(New submit, Continue, Start, Rerun, Rewind). - Owned-cards set creation centralized via
web/services/build_utils.owned_set()and used inweb/routes/build.py,web/routes/configs.py, andweb/routes/decks.py. - Step 5: replaced ad-hoc empty context assembly with
web/services/build_utils.step5_empty_ctx()in GET/build/step5andreset-stage. - Builder introspection: adopted
builder_present_names()andbuilder_display_map()helpers inweb/routes/build.pyfor 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).
Added
- Deck summary: introduced
web/services/summary_utils.summary_ctx()to unify summary context (owned_set, game_changers, combos/synergies, versions). - Alternatives cache helper extracted to
web/services/alts_utils.py.
Changed
- Decks and Configs routes now use
summary_ctx()to render deck summaries, reducing duplication and ensuring consistent fields. - Build: routed owned names via helper and fixed
_rebuild_ctx_with_multicopycontext indentation. - Build: moved alternatives TTL cache into
services/alts_utilsfor readability. - Build: Step 5 start error path now uses
step5_error_ctx()for a consistent UI. - Build: Extended Step 5 error handling to Continue, Rerun, and Rewind using
step5_error_ctx().
Fixed
- 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).
- Docker: normalized line endings for
entrypoint.shduring image build to avoidenv: 'sh\r': No such file or directoryon Windows checkouts.
Removed
- Duplicate root route removed:
web/routes/home.pywas deleted; the app root is served byweb/app.py.
[2.2.3] - 2025-09-01
Fixes
- Bug causing basic lands to no longer be added due to combined dataframe not including basics
Changed
- Logic for removal tagging causing self-targetting cards (e.g. Conjurer's Closet) to be tagged as removal
[2.2.2] - 2025-09-01
Fixed
- Ensure default config files are available when running with bind-mounted config directories:
- Dockerfile now preserves a copy of defaults at
/.defaults/configin the image. - Entrypoint seeds missing files into
/app/configon container start (deck.json,card_lists/combos.json,card_lists/synergies.json). - Adds a back-compat symlink
combo.json -> combos.jsonif missing. This resolves cases where a blank hostconfig/overlay made files appear missing.
- Dockerfile now preserves a copy of defaults at
Changed
- Example compose files updated to use
APP_VERSION=v2.2.2.
[2.2.1] - 2025-09-01
Added
- 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_combosis 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.
[2.1.1] - 2025-08-29
Added
- 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.
[2.0.1] - 2025-08-28
Added
- Web UI performance: optional virtualized grids/lists in Step 5 and Owned (enable with
WEB_VIRTUALIZE=1). - Virtualization diagnostics overlay (when
SHOW_DIAGNOSTICS=1); pressvto toggle per‑grid overlays and a global summary bubble with visible range, totals, render time, and counters. - Image polish: lazy‑loading with responsive
srcset/sizesand 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.
- Web UI: FastAPI + Jinja front-end for the builder; staged build view with per-stage reasons
- New Deck modal consolidating steps 1–3 with optional Name for exports, Enter-to-select commander, and disabled browser autofill
- Locks, Replace flow, Compare builds, and shareable permalinks for finished decks
- Compare page: Copy summary action to copy diffs (Only in A/B and Changed counts) to clipboard
- Finished Decks multi-select → Compare with fallback to "Latest two"; options carry modified-time for ordering
- Permalinks include locks; global "Open Permalink…" entry exposed in header and Finished Decks
- Replace flow supports session-local Undo and lock-aware validation
- New Deck modal: inline summary of selected themes with order (1, 2, 3)
- Theme combine mode (AND/OR) with tooltips and selection-order display in the Web UI
- AND-mode creatures pre-pass: select "all selected themes" creatures first, then fill by weighted overlap; staged reasons show matched themes
- Scryfall attribution footer in the Web UI
- Owned-cards workflow:
- Prompt (only if lists exist) to "Use only owned cards?"
- Support multiple file selection; parse
.txt(1 per line) and.csv(anynamecolumn) - Owned-only mode filters the pool to owned names; commander exempt
- Recommendations export when owned-only deck is incomplete (~1.5× missing) to
deck_files/[stem]_recommendations.csvand.txt
- CSV export includes an
Ownedcolumn when not using owned-only - Windows EXE build via PyInstaller is produced on tag and attached to GitHub Releases
- 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.icoserved (ICO with PNG fallback) - Diagnostics:
/healthzendpoint returns{status, version, uptime_seconds}; responses carryX-Request-ID; unhandled errors return JSON with request_id - Diagnostics page and tools gated by
SHOW_DIAGNOSTICS; Logs page gated bySHOW_LOGS; both off by default - Global error handling: friendly HTML templates for 404/4xx/500 with Request-ID and "Go home" link; JSON structure for HTMX/API
- Request-ID middleware assigns
X-Request-IDto all responses and includes it in JSON error payloads /status/logs?tail=Nendpoint (read-only) to fetch a recent log tail for quick diagnostics- Tooltip Copy action on chart tooltips (Pips/Sources) for quick sharing of per-color card lists
- 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.
Roadmap and usage for Web UI features are tracked in logs/web-ui-upgrade-outline.md.
Changed
- Accessibility: respect OS “reduced motion” by disabling blur/fade transitions and smooth scrolling.
- Static asset caching and compression tuned for the web service (cache headers + gzip) to improve load performance.
- Rename folder from
card_librarytoowned_cards(env override:OWNED_CARDS_DIR; back-compat respected) - Docker assets and docs updated:
- New volume mounts:
./owned_cards:/app/owned_cardsand./config:/app/config - Compose and helper scripts updated accordingly
- New volume mounts:
- Release notes source is
RELEASE_NOTES_TEMPLATE.md;RELEASE_NOTES.mdignored - README/DOCKER/WINDOWS_DOCKER_GUIDE updated for Web UI, headless examples, and PowerShell-friendly commands
- Headless: tag_mode (AND/OR) accepted from JSON and environment and exported in interactive run-config JSON
- Owned lists are enriched at upload-time and persisted in an internal store; header rows skipped and duplicates deduped; per-request parsing removed
- Builder Review (Step 4): "Use only owned cards" toggle moved here; Step 5 is status-only with "Edit in Review" for changes
- Minor UI/CSS polish and consolidation across builder/owned pages
- Deck summary reporting now includes colorless 'C' in totals and cards; UI adds a Show C toggle for Sources
- New Deck modal submits directly to build, removing the intermediate review step
- Finished Decks banner and lists now prefer the custom Name provided in the modal
- Step 5 Replace toggle now includes a tooltip clarifying that reruns will replace picks in that stage when enabled
- Locks are enforced on rerun; the Locked section live-updates on unlock (row removal and chip refresh)
- Compare page shows ▲/▼ indicators on Changed counts and preserves the "Changed only" toggle across interactions
- Bracket selector shows numbered labels (e.g., "Bracket 3: Upgraded") and defaults to bracket 3 on new deck creation
- List view highlight polished to wrap only the card name (no overrun of the row)
- Total sources calculation updated to include 'C' properly
- 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-nameon 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
Fixed
- Docker Hub workflow no longer publishes a
major.minortag (e.g.,1.1); only full semver (e.g.,1.2.3) andlatest - Owned page internal server error resolved via hardened template context and centralized owned context builder
- Web container crash resolved by removing invalid union type annotation in favicon route; route now returns a single Response type
- Source highlighting consistency: charts now correctly cross-highlight corresponding cards in both list and thumbnail views
- Basics handling: ensured basic lands and Wastes are recognized as sources; added fallback oracle text for basics in CSV export
- Fetch lands are no longer miscounted as mana sources
- Web 404s previously returned JSON to browsers in some cases; now correctly render HTML via a Starlette HTTPException handler
- Windows PowerShell curl parsing issue documented with guidance in README
- 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)
For prior releases, see the GitHub Releases page.