docs: documentation overhaul - archive, user guides, env parity (#62)
Some checks failed
CI / build (push) Has been cancelled

* docs: archive CLI runner scripts and Windows Docker guide, update web runner scripts

* docs: overhaul README and DOCKER.md, add 10 user guides

- README: Budget Mode section, corrected Further Reading links, theme badge descriptions, diagnostics expansion
- DOCKER.md: Windows path note, Budget Mode + Include/Exclude sections, env table additions
- docs/user_guides/: 10 new feature guides covering budget mode, include/exclude, locks/replace/permalinks, batch build, theme browser, random build, owned cards, partner mechanics, bracket compliance, quick build & skip controls

* fix: map PriceTimeoutError→503, add budget exceptions to status map; update error_handling.md

* docs: env var parity — add missing vars to .env.example and README table
This commit is contained in:
mwisnowski 2026-03-23 22:00:50 -07:00 committed by GitHub
parent 537f5d3834
commit ac6c9f4daa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1074 additions and 23 deletions

View file

@ -0,0 +1,69 @@
# Build X and Compare
Generate multiple deck variants from the same configuration and compare them side by side.
---
## Overview
Build X and Compare lets you run 110 deck builds with the same commander, themes, and settings in parallel. Use it to explore variance across builds, find the most consistent card inclusions, and produce an optimized "best-of" deck via the Synergy Builder.
Enable with `ENABLE_BATCH_BUILD=1` (default: on).
---
## Starting a Batch Build
1. In the **New Deck modal**, increase the **Build count** slider from 1 to your desired number (max 10).
2. Configure the rest of the build (commander, themes, bracket, budget) as normal.
3. Click **Build**. Builds run in parallel (max 5 concurrent) with a real-time progress bar and dynamic time estimates.
---
## Results View
After all builds complete, the results page shows:
- Individual build cards, each with a summary (card count, estimated cost, theme coverage).
- **Card overlap statistics**: how many cards appeared in N of N builds, sorted by frequency.
- A **Rebuild** button to re-run with the same configuration (locks and include/exclude lists are preserved).
- A **ZIP export** button to download all builds (CSV, TXT, and summary JSON for each).
---
## Synergy Builder
The Synergy Builder analyzes all builds in the batch and produces an optimized single deck scored by three factors:
| Factor | Description |
|--------|-------------|
| Frequency | Cards that appeared in more builds score higher |
| EDHREC rank | Lower EDHREC rank (more popular) scores higher |
| Theme tags | Cards matching more of the selected themes score higher |
### How to Use
1. From the batch results view, click **Synergy Builder**.
2. Review the scored card list. The top candidates per category are pre-selected.
3. Adjust selections if needed, then click **Build Synergy Deck**.
4. The result is a standard deck that can be exported, locked, and permalinked like any other build.
---
## Compare View
Access the compare view from **Finished Decks** to diff any two completed builds:
- Card overlap count and percentage.
- Cards unique to Build A, cards unique to Build B, cards in both.
- Individual build summaries side by side.
- **Copy summary** button for plain-text export of the diff.
- **Swap A/B** to reverse the comparison direction.
- **Latest two** quick action selects the two most recent builds automatically.
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `ENABLE_BATCH_BUILD` | `1` | Show the build count slider in the New Deck modal. Set to `0` to hide and restrict to single builds. |

View file

@ -0,0 +1,109 @@
# Bracket Compliance
Select a power level for your deck and get a detailed compliance report against the official Commander bracket rules.
---
## Overview
Commander brackets define five power tiers from Exhibition (casual) to cEDH (competitive). The builder checks your finished deck against the rules for your selected bracket and surfaces a PASS / WARN / FAIL report per category.
Bracket selection lives in the **New Deck modal**. The compliance report appears in Step 5 and is exported to the compliance JSON sidecar (`deck_files/*_compliance.json`).
---
## Bracket Tiers
| Bracket | Name | Key restrictions |
|---------|------|-----------------|
| 1 | Exhibition | No Game Changers; no two-card infinite combos; no mass land denial; extra turns discouraged; tutors sparse |
| 2 | Core | No Game Changers; no two-card infinite combos; no mass land denial; extra turns not chained; tutors sparse |
| 3 | Upgraded | Up to 3 Game Changers; no mass land denial; no early/cheap two-card combos; extra turns not chained |
| 4 | Optimized | No bracket restrictions (banned list still applies) |
| 5 | cEDH | No bracket restrictions (banned list still applies; competitive mindset) |
Bracket rules follow the official Wizards of the Coast Commander bracket definitions. The card lists used for compliance checks are stored in `config/card_lists/` and can be updated as WotC revises the lists.
---
## Compliance Categories
For each build, the compliance report checks:
| Category | What is checked |
|----------|----------------|
| **Game Changers** | Cards on the official Game Changers list (`config/card_lists/game_changers.json`) |
| **Extra turns** | Cards that grant extra turns (`extra_turns.json`) |
| **Mass land denial** | Cards that destroy, exile, or bounce many lands (`mass_land_denial.json`) |
| **Non-land tutors** | Cards that search the library for non-land cards |
| **Two-card combos** | Known two-card infinite combos (`combos.json`), with a flag for early/cheap combos |
Each category returns a PASS / WARN / FAIL verdict and lists the flagged cards with links.
If the **commander itself** is on the Game Changers list, it is surfaced separately at the top of the report.
---
## Enforcement Mode
Set `enforcement_mode` in your JSON config to control how the builder handles bracket violations:
| Mode | Behavior |
|------|----------|
| `validate` | Build freely, then report violations. No cards are blocked. _(default)_ |
| `prefer` | During selection, avoid adding disallowed categories; cap Game Changers for Bracket 3. |
| `strict` | Block additions that would violate the bracket. Build fails with a clear message if unavoidable. |
```json
{
"bracket": "core",
"enforcement_mode": "prefer"
}
```
---
## Rule Zero Notes
The `rule_zero_notes` field in the JSON config lets you document table agreements that override standard bracket rules:
```json
{
"bracket": "upgraded",
"rule_zero_notes": "Mass land denial allowed by table agreement. Two-card combos capped at 1."
}
```
Rule zero notes appear in the compliance report header and are exported to the compliance JSON.
---
## Web UI
- The bracket dropdown in the New Deck modal defaults to **Core (Bracket 2)** when no bracket is set.
- The compliance banner in Step 5 shows a color-coded overall verdict (green=PASS, yellow=WARN, red=FAIL) and expandable per-category details.
- `WEB_AUTO_ENFORCE=1` re-runs compliance export automatically after each completed build.
---
## Maintaining the Card Lists
The Game Changers list and companion lists are static JSON files in `config/card_lists/`. Each file includes `source_url` and `generated_at` metadata. Update them manually when WotC publishes revisions. Unknown cards in lookups are skipped with a note in the compliance report — they do not cause a hard failure.
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `WEB_AUTO_ENFORCE` | `0` | Auto-run compliance export after every build. |
---
## JSON Config Keys
| Key | Values | Purpose |
|-----|--------|---------|
| `bracket` | `exhibition` \| `core` \| `upgraded` \| `optimized` \| `cedh` | Bracket selection. Defaults to `core` if unset. |
| `enforcement_mode` | `validate` \| `prefer` \| `strict` | How violations are handled during building. |
| `rule_zero_notes` | string | Optional table agreement notes included in the compliance report. |

View file

@ -0,0 +1,89 @@
# Budget Mode
Build decks within a price target using per-card cost filtering and a soft-review summary.
---
## Overview
Budget Mode filters the card selection pool to cards at or near a per-card price ceiling you set. Cards that exceed the ceiling are excluded from the randomized pool, but the final deck is never hard-rejected — over-budget cards that slip through are flagged for your review in the build summary.
Enable with `ENABLE_BUDGET_MODE=1` (default: on).
---
## Setting a Budget
In the **New Deck modal**, enter a per-card price ceiling in the Budget field. This ceiling is a UI input — it is not set via an environment variable.
- Leave the field blank or set it to `0` to disable per-card filtering for that build.
- The ceiling applies to all non-land cards drawn from the selection pool.
---
## How Filtering Works
1. Price data is loaded from a local cache sourced from Scryfall bulk data.
2. Cards whose price exceeds `ceiling * (1 + BUDGET_POOL_TOLERANCE)` are excluded from the pool before selection begins.
3. `BUDGET_POOL_TOLERANCE` (default `0.15`) adds a 15% headroom above the ceiling. This smooths results for borderline-priced cards and avoids over-aggressive filtering.
4. Selection then runs normally on the filtered pool — locked cards and Must Include cards are inserted before filtering.
**Example:** Ceiling = $2.00, tolerance = 0.15 → cards priced above $2.30 are excluded from the pool.
---
## Build Summary
After a build completes, the budget summary panel shows:
- Total estimated deck cost
- Per-category cost breakdown (creatures, spells, lands, etc.)
- Cards flagged as over-budget (above the ceiling, not the tolerance threshold)
- Price badges on individual card rows: green (under ceiling), yellow (at ceiling), red (over ceiling)
- A stale price indicator (clock icon) when a cached price is older than `PRICE_STALE_WARNING_HOURS`
The summary JSON export (`deck_files/*.summary.json`) includes the price breakdown per category.
---
## Price Data & Caching
Prices are sourced from the Scryfall bulk data API and cached locally.
| Behavior | Setting |
|----------|---------|
| Background per-card refresh (7-day TTL) | `PRICE_LAZY_REFRESH=1` (default) |
| Full cache rebuild daily at 01:00 UTC | `PRICE_AUTO_REFRESH=1` |
| Stale indicator threshold | `PRICE_STALE_WARNING_HOURS=24` (set to `0` to disable) |
If a card has no price data, it is treated as free (fail-open) and selected normally. No card is hard-rejected due to a missing price.
To manually check cache status: `/api/price/stats` (when `SHOW_DIAGNOSTICS=1`).
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `ENABLE_BUDGET_MODE` | `1` | Enable budget controls and price display. Set to `0` to hide all budget UI. |
| `BUDGET_POOL_TOLERANCE` | `0.15` | Fractional overhead above the per-card ceiling before exclusion. |
| `PRICE_AUTO_REFRESH` | `0` | Rebuild the full price cache daily at 01:00 UTC. |
| `PRICE_LAZY_REFRESH` | `1` | Refresh stale per-card prices in the background. |
| `PRICE_STALE_WARNING_HOURS` | `24` | Hours before a cached price shows a stale indicator. `0` = disable. |
---
## FAQ
**Why is a card over my ceiling still in the deck?**
The pool tolerance allows cards slightly above the ceiling into the selection pool to avoid overly thin pools. Cards that land in your deck despite being over ceiling are flagged in red in the budget summary. You can swap them using the Replace feature in Step 5.
**Prices look stale — how do I refresh?**
Set `PRICE_LAZY_REFRESH=1` (default) to refresh automatically in the background. For an immediate full refresh, set `PRICE_AUTO_REFRESH=1` and restart the container, or trigger a manual refresh via the price stats API.
**Does budget mode affect the commander?**
No. The commander is never filtered by price — only cards drawn from the selection pool are subject to the ceiling.
**Can I use budget mode with Must Include cards?**
Yes. Must Include cards bypass the pool filter and are always added. They may appear as over-budget in the summary if they exceed the ceiling.

View file

@ -0,0 +1,89 @@
# Include / Exclude Lists
Pin specific cards into every build or permanently block them from selection.
---
## Overview
The Include / Exclude feature (also called "Must-Haves") lets you control card selection at the individual card level, outside the normal theme and pool logic.
- **Must Include**: cards are always added to the deck, before pool selection runs.
- **Must Exclude**: cards are never added, regardless of theme, bracket, or pool filtering.
Requires `ALLOW_MUST_HAVES=1` (default: on).
---
## Enabling the UI Controls
The include/exclude buttons are hidden by default to keep the Step 5 interface clean. To show them:
```
SHOW_MUST_HAVE_BUTTONS=1
```
When enabled, each card row in Step 5 gains a Must Include (+) and Must Exclude () button. You can also manage lists via the quick-add input at the top of the section.
---
## Adding Cards
### Via Step 5 UI
1. Enable `SHOW_MUST_HAVE_BUTTONS=1`.
2. In Step 5, click **+** on a card to add it to Must Include, or **** to add it to Must Exclude.
3. Use the quick-add input at the top to add cards by name without scrolling.
### Via JSON Config
Supply lists in your deck config file:
```json
{
"must_include": ["Sol Ring", "Arcane Signet"],
"must_exclude": ["Thassa's Oracle"]
}
```
Both keys accept an array of card names. Names are matched case-insensitively.
---
## Priority Order
When include/exclude interacts with other filters, this order applies:
1. **Must Exclude** — always wins; card is never selected.
2. **Must Include** — card is always added, before pool selection.
3. **Budget filter** — applied to the remaining pool after Must Include cards are inserted.
4. **Bracket filter** — applied after budget.
Must Include cards are inserted directly and are not subject to budget pool filtering. They may appear as over-budget in the summary if they exceed the ceiling, and they may trigger bracket warnings if they violate bracket rules.
---
## Multi-Copy Archetypes
If a card is in Must Include and the builder detects it supports multi-copy (e.g., Relentless Rats), a count picker dialog appears. Set the desired copy count before confirming.
---
## Conflict Resolution
| Scenario | Behavior |
|----------|----------|
| Card is in both Must Include and Must Exclude | A conflict dialog prompts you to resolve before building. Exclude wins if unresolved. |
| Must Include card not in color identity | Build silently skips the card (does not hard-fail). Verify color identity before including. |
| Must Include card violates bracket rule | Card is added; bracket compliance report flags it. Resolve in Step 5 or adjust bracket. |
---
## Headless / CLI
Set include and exclude lists in the JSON config. Environment variable overrides are not supported for per-card lists; use the config file.
```json
{
"must_include": ["Swiftfoot Boots"],
"must_exclude": ["Demonic Tutor"]
}
```

View file

@ -0,0 +1,73 @@
# Locks, Replace & Permalinks
Core Step 5 tools for refining, sharing, and restoring deck builds.
---
## Locks
Lock cards to pin them in place across reruns. Locked cards are always kept in the deck regardless of theme changes, pool reshuffles, or bracket adjustments.
### How to Lock
- In Step 5, click the lock icon on any card row. The card is immediately locked and visually marked.
- Alternatively, click the card name to open its detail panel, then toggle the lock there.
### Behavior
- Locked cards persist when you click **Rebuild** (re-runs the build with the same settings).
- Locks are stored in the session and exported to the summary JSON sidecar (`*.summary.json`).
- When restoring a deck via permalink, locks are restored alongside the rest of the build state.
- Locking has no interaction with Must Include/Exclude lists — a locked card and a must-include card are both always present. Avoid locking a card that is also in Must Exclude.
### Unlocking
Click the lock icon again to unlock. The card re-enters the pool on the next rebuild.
---
## Replace
Swap any card in the current deck for an alternative from the same category pool.
### How to Use
1. In Step 5, enable **Replace mode** (toggle at the top of the card list).
2. Click any card to open the **Alternatives panel**.
3. Browse or filter alternatives. Toggle **Owned only** to restrict candidates to your uploaded library.
4. Click a card in the alternatives panel to swap it in. The replaced card moves out of the deck.
5. The replacement is locked automatically to prevent it from being displaced on the next rebuild.
### Notes
- Replace respects category boundaries: land replacements come from the land pool, creature replacements from the creature pool, etc.
- Combo-paired cards (flagged in the Combos section of Step 5) will surface a warning if you replace one half of a known combo pair.
- Replace history is included in the summary JSON.
---
## Permalinks
Permalinks encode the full build state into a shareable URL so you or anyone else can restore that exact deck later.
### What a Permalink Encodes
- Commander (and partner/background if applicable)
- Primary, secondary, and tertiary themes
- Bracket selection
- Locked cards
- Must Include / Must Exclude lists
- Budget ceiling (if set)
- Build name (if set)
### Creating a Permalink
- In Step 5, click **Copy Permalink**.
- From the Finished Decks page, click the permalink icon on any completed build.
### Restoring from a Permalink
1. Click **Open Permalink…** on the homepage or Build a Deck modal.
2. Paste the permalink URL or token.
3. The builder restores the commander, themes, bracket, locks, and lists, then lands in Step 5 with the saved state ready to rebuild or export.
### Sharing
Permalink URLs are self-contained — no server state is required. Anyone with the URL can restore the build as long as the card data in the application is compatible (same catalog version or later).
---
## Combos (Step 5 Panel)
The Combos section in Step 5 lists known two-card combo pairs detected in the current deck. This is informational — no cards are added or removed automatically. Use locks to preserve combo pairs across rebuilds, or add individual combo cards to Must Include if you want them guaranteed.

View file

@ -0,0 +1,79 @@
# Owned Cards
Upload your card collection and build decks using only — or preferring — cards you already own.
---
## Overview
The Owned Cards feature lets you upload lists of cards you own. Once uploaded, these lists integrate into the build pipeline so the builder can filter or bias card selection toward your collection.
---
## Uploading Your Library
Go to `/owned` in the web UI to manage your owned card library.
### Supported Formats
| Format | Notes |
|--------|-------|
| `.txt` | One card name per line. Optionally prefix with a count: `4x Sol Ring` or `4 Sol Ring`. |
| `.csv` | Must include at minimum a `name` column. A `count` column is optional. |
Cards are enriched and deduplicated automatically on upload. Near-duplicate names (e.g., different printings) are resolved against the card catalog.
### Multiple Files
You can upload multiple files. All are merged into a single owned library for the session. To replace the library, delete existing files and re-upload.
---
## Build Modes
Select the owned card mode in the **New Deck modal**:
| Mode | Behavior |
|------|----------|
| **No filter** (default) | Owned cards have no special weight; all on-theme cards are eligible. |
| **Prefer owned** | Cards you own are weighted higher in the selection pool. Non-owned cards are still eligible if the pool would otherwise be too thin. |
| **Owned only** | Only cards in your owned library are eligible for selection. Builds may be thinner if your library doesn't cover a theme well. |
---
## Alternatives Panel (Replace)
When using **Replace** in Step 5, toggle **Owned only** in the Alternatives panel to restrict replacement candidates to cards in your library.
---
## Performance
For large libraries (1,000+ cards), enable list virtualization to improve scroll performance in the Owned Library page:
```
WEB_VIRTUALIZE=1
```
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `OWNED_CARDS_DIR` / `CARD_LIBRARY_DIR` | `/app/owned_cards` | Override the directory where owned card files are stored. Mount this volume to persist across container restarts. |
| `WEB_VIRTUALIZE` | `1` | Enable virtualized lists for large owned libraries and Step 5 card grids. |
---
## Headless / CLI
In headless mode, set the owned card mode via JSON config:
```json
{
"owned_only": true,
"prefer_owned": false
}
```
Use `"prefer_owned": true` for soft weighting, `"owned_only": true` for hard filtering. The two are mutually exclusive; `owned_only` takes precedence if both are set.

View file

@ -0,0 +1,105 @@
# Partner Mechanics
Build with dual-commander configurations: Partners, Partner With, Doctor/Doctor's Companion, and Backgrounds.
---
## Overview
Partner mechanics allow you to pair two commanders into a single deck, combining their color identities and theme tags. The builder auto-detects which partner type applies to a selected commander and adjusts Step 2 accordingly.
Enable with `ENABLE_PARTNER_MECHANICS=1` (default: on in Docker Compose; disabled by default in headless/CLI).
---
## Partner Types
| Type | How it works |
|------|-------------|
| **Partner** | Both commanders have the Partner keyword. Any two Partner commanders can be paired. |
| **Partner With** | One commander specifically names their partner (e.g., "Partner with Cazur"). The canonical partner is pre-filled; you can opt out and swap. |
| **Doctor / Doctor's Companion** | Doctors list legal companions and vice versa. Role labels are shown beside each option. |
| **Background** | Choose a Background enchantment instead of a second commander. The Background picker replaces the partner selector when applicable. |
---
## Selecting a Partner in the Web UI
1. In the **New Deck modal** or **Step 2**, select a commander that supports a partner type.
2. The appropriate partner input appears automatically:
- A filtered partner dropdown (Partner)
- A pre-filled name with an opt-out chip (Partner With, Doctor)
- A Background dropdown (Background)
3. For **Partner With** and **Doctor** pairings, an opt-out chip lets you keep the canonical suggestion or clear it and choose a different partner.
4. Previews, color identity warnings, and theme chips update in real time as you make partner selections.
### Partner Suggestions
With `ENABLE_PARTNER_SUGGESTIONS=1`, ranked suggestion chips appear beside the partner selector. These are backed by the partner synergy analytics dataset (`config/analytics/partner_synergy.json`, auto-generated when missing). Selecting a chip populates the partner field.
---
## Color Identity
The combined color identity of both commanders is used for all card pool filtering. Cards outside the combined identity are excluded, just as they would be for a single commander.
---
## Theme Tags
Theme tags from both commanders are merged. This means a partner pair may unlock themes neither commander could access individually.
---
## Headless / CLI
Supply partner settings in the JSON config or as CLI flags:
```json
{
"commander": "Halana, Kessig Ranger",
"secondary_commander": "Alena, Kessig Trapper",
"enable_partner_mechanics": true
}
```
CLI flags:
```
--secondary-commander "Alena, Kessig Trapper" --enable-partner-mechanics true
```
For Background pairings, use `background` instead of `secondary_commander`:
```json
{
"commander": "Raised by Giants",
"background": "Acolyte of Bahamut",
"enable_partner_mechanics": true
}
```
`secondary_commander` and `background` are mutually exclusive. `background` takes precedence if both are set.
### Dry Run
Add `--dry-run` to the CLI command to echo the resolved pairing (names, color identity, partner mode) without running a full build:
```powershell
python code/main.py --dry-run --secondary-commander "Alena, Kessig Trapper" --enable-partner-mechanics true
```
---
## Headless JSON Export
Exported configs (`HEADLESS_EXPORT_JSON=1`) include the resolved partner fields:
- `secondary_commander` or `background`
- `combined_color_identity`
- `partner_mode` (partner | partner_with | doctor | background)
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `ENABLE_PARTNER_MECHANICS` | `0` | Unlock partner/background inputs in the web builder and headless runner. |
| `ENABLE_PARTNER_SUGGESTIONS` | `0` | Show ranked partner suggestion chips in the web builder. |
| `PARTNER_SUGGESTIONS_DATASET` | _(auto)_ | Override path to `partner_synergy.json` inside the container. |

View file

@ -0,0 +1,108 @@
# Quick Build & Skip Controls
Speed up the build workflow with one-click automation or granular per-stage skipping.
---
## Overview
The Step-by-Step builder presents each stage for your approval before moving on. Two tools let you bypass this:
- **Quick Build** — one click from the New Deck modal runs all stages automatically and lands in Step 5 with a complete deck ready to review.
- **Skip Controls** — per-stage toggles in Step 5 let you auto-accept any combination of the 21 granular build stages while still stepping through the others manually.
---
## Quick Build
Click **Quick Build** in the New Deck modal (next to the standard **Build** button) to run the full workflow without any approval prompts. Progress is shown in real time as each stage completes.
Use Quick Build when:
- You want to iterate quickly and refine in Step 5 rather than stepping through each stage.
- You are running Batch Build and want all variants generated without interruption.
- You already know what you want and just need a starting point to lock and tweak.
Quick Build respects all normal build settings (bracket, budget, include/exclude, owned filters) and honors any locks from a previous build.
---
## Stage Order
The default build order (`WEB_STAGE_ORDER=new`) runs creatures and spells before lands. This allows the builder to see what pip costs are needed before finalizing the mana base.
| Mode | Order |
|------|-------|
| `new` (default) | Multi-Copy → Creatures → Spells → Lands → Theme Fill → Adjustments |
| `legacy` | Multi-Copy → Lands → Creatures → Spells → Theme Fill → Adjustments |
Set `WEB_STAGE_ORDER=legacy` to restore the original lands-first order.
---
## Skip Controls
In Step 5, the **Skip Controls** section shows 21 per-stage toggles. Enabling a toggle for a stage causes that stage to be auto-accepted on the next build (or rebuild), skipping the approval prompt.
### Land Stages
| Stage | What it auto-accepts |
|-------|---------------------|
| Basic land fill | Automatically approve the basic land distribution |
| Nonbasic fill | Automatically approve nonbasic land selection |
| Land count adjustment | Automatically approve the final land count tuning |
### Creature Stages
| Stage | What it auto-accepts |
|-------|---------------------|
| Early-game creatures | Auto-approve 12 CMC creatures |
| Mid-game creatures | Auto-approve 34 CMC creatures |
| Late-game creatures | Auto-approve 5+ CMC creatures |
| Synergy creatures | Auto-approve creatures selected for synergy with themes |
| Creature theme fill | Auto-approve remaining creature theme fill |
### Spell Stages
| Stage | What it auto-accepts |
|-------|---------------------|
| Ramp | Auto-approve ramp spells |
| Removal | Auto-approve single-target removal |
| Board wipes | Auto-approve mass removal |
| Card draw | Auto-approve draw spells |
| Protection | Auto-approve protection/counterspell package |
| Spell theme fill | Auto-approve remaining spell theme fill |
Additional adjustment and fill stages are also individually toggleable in the UI.
---
## Combining Quick Build and Skip Controls
You can use skip controls as a "remembered preference" that persists for the session:
- Enable skip controls for stages you never want to review (e.g., basic land fill, ramp).
- Use **Build** (not Quick Build) to still step through the remaining stages manually.
Quick Build always auto-accepts all stages regardless of skip control settings — it is the equivalent of all 21 toggles enabled at once.
---
## Session Persistence
Skip control settings are stored in the browser session. They persist across multiple builds in the same browser tab but reset when the session ends (tab close, browser restart, or session timeout).
---
## Ideal Counts UI
The stage tuning interface (where you set target counts for each category before a stage runs) uses range sliders by default. Switch to numeric text inputs with:
```
WEB_IDEALS_UI=input
```
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `WEB_STAGE_ORDER` | `new` | Build stage execution order: `new` (creatures→spells→lands) or `legacy` (lands→creatures→spells). |
| `WEB_IDEALS_UI` | `slider` | Stage tuning interface: `slider` (range inputs) or `input` (text boxes). |

View file

@ -0,0 +1,89 @@
# Random Build
Generate surprise Commander decks with a single click — deterministic when you need repeatability.
---
## Overview
The Random Build tile spins up a fully randomized deck: a random commander is picked, then themes are assigned randomly from the catalog and matched to that commander's color identity and tag profile. All normal build rules (bracket, budget, owned filters, include/exclude) still apply.
Enable:
- `RANDOM_MODES=1` — expose backend random endpoints
- `RANDOM_UI=1` — show the Random Build tile on the homepage
---
## Using Random Build
1. Click the **Random Build** tile on the homepage.
2. Optionally set a theme override in the tile's inputs (primary, secondary, tertiary). Leave blank for fully random.
3. Click **Surprise me**. The builder picks a commander and fills theme slots automatically.
4. Use the **Reroll** button to generate a fresh random combination without leaving the tile.
5. Confirm to proceed through the normal build stages (or use Quick Build for one-click automation).
---
## Theme Auto-Fill
When theme slots are left blank, `RANDOM_AUTO_FILL=1` (default) fills them automatically from themes compatible with the randomly selected commander. Set `RANDOM_AUTO_FILL_SECONDARY` or `RANDOM_AUTO_FILL_TERTIARY` to override auto-fill behavior for individual slots while leaving others random.
If a specific theme combination cannot be satisfied (too few on-theme cards for the selected commander), the builder tries alternative themes up to `RANDOM_MAX_ATTEMPTS` times before surfacing an error.
---
## Reproducible Builds (Seeds)
Set `RANDOM_SEED` to any integer or string to produce the same commander + theme combination every time:
```
RANDOM_SEED=my_deck_seed_2026
```
Seeds are also shareable — include the seed in the permalink or pass it via the UI seed input to reproduce a specific random outcome.
---
## Reroll Throttle
To prevent accidental rapid-fire rerolls, a minimum interval of `RANDOM_REROLL_THROTTLE_MS` (default: `350` ms) is enforced client-side between reroll requests.
---
## Constraints
Theme and commander constraints can be passed as inline JSON or a JSON file for headless random builds:
```
RANDOM_CONSTRAINTS='{"colors": ["G","W"], "max_cmc": 4}'
RANDOM_CONSTRAINTS_PATH=/app/config/random_constraints.json
```
File path takes precedence over the inline `RANDOM_CONSTRAINTS` value.
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `RANDOM_MODES` | _(unset)_ | Enable random build endpoints. |
| `RANDOM_UI` | _(unset)_ | Show the Random Build tile. |
| `RANDOM_MAX_ATTEMPTS` | `5` | Retry budget when theme constraints cannot be satisfied. |
| `RANDOM_TIMEOUT_MS` | `5000` | Per-attempt timeout in milliseconds. |
| `RANDOM_REROLL_THROTTLE_MS` | `350` | Minimum ms between reroll requests (client guard). |
| `RANDOM_SEED` | _(blank)_ | Deterministic seed for reproducible builds. |
| `RANDOM_AUTO_FILL` | `1` | Auto-fill missing theme slots. |
| `RANDOM_AUTO_FILL_SECONDARY` | _(blank)_ | Override secondary slot auto-fill behavior. |
| `RANDOM_AUTO_FILL_TERTIARY` | _(blank)_ | Override tertiary slot auto-fill behavior. |
| `RANDOM_PRIMARY_THEME` | _(blank)_ | Fix the primary theme (random commander still selected). |
| `RANDOM_SECONDARY_THEME` | _(blank)_ | Fix the secondary theme. |
| `RANDOM_TERTIARY_THEME` | _(blank)_ | Fix the tertiary theme. |
| `RANDOM_STRICT_THEME_MATCH` | `0` | Require strict theme matching for commanders (1=strict). |
| `RANDOM_CONSTRAINTS` | _(blank)_ | Inline JSON constraints (e.g., color limits). |
| `RANDOM_CONSTRAINTS_PATH` | _(blank)_ | Path to a JSON constraints file (takes precedence). |
| `RANDOM_OUTPUT_JSON` | _(blank)_ | Path or directory for outputting the random build payload (headless). |
| `RANDOM_STRUCTURED_LOGS` | `0` | Emit structured JSON logs for random builds. |
| `RANDOM_TELEMETRY` | `0` | Enable lightweight timing and attempt count metrics. |
For rate limiting random endpoints see the [Docker guide](../../DOCKER.md) — Random rate limiting section.

View file

@ -0,0 +1,104 @@
# Theme Browser & Quality System
Explore, filter, and evaluate the theme catalog before building.
---
## Overview
The Theme Browser at `/themes` displays all available themes with synergy data, editorial quality scores, and pool size information. Use it to discover themes, understand what cards a theme covers, and filter by quality before selecting themes for a build.
Enable the theme selector and browser with `ENABLE_THEMES=1` (default: on).
---
## Badge System
Each theme card in the browser displays up to three badge types:
### Quality Badge (`SHOW_THEME_QUALITY_BADGES=1`)
Editorial quality score based on synergy depth, card count, and thematic coherence. Assigned during catalog curation.
| Badge | Meaning |
|-------|---------|
| Excellent | Strong synergy, large pool, well-curated |
| Good | Solid theme with reasonable card support |
| Fair | Usable but limited pool or marginal synergy |
| Poor | Sparse pool or weak theme coherence |
### Pool Size Badge (`SHOW_THEME_POOL_BADGES=1`)
Number of on-theme cards available in the catalog.
| Badge | Approximate range |
|-------|------------------|
| Vast | 200+ cards |
| Large | 100199 cards |
| Moderate | 5099 cards |
| Small | 2049 cards |
| Tiny | Under 20 cards |
Pool size is affected by `THEME_MIN_CARDS`: themes with fewer cards than this threshold are stripped from the catalog entirely during setup/tagging (default: `5`).
### Popularity Badge (`SHOW_THEME_POPULARITY_BADGES=1`)
How frequently the theme appears across builds in the system. Higher popularity themes have more real-world data behind their synergy rankings.
---
## Filtering
Filter chips appear above the theme grid when `SHOW_THEME_FILTERS=1` (default: on). You can combine filters:
- Filter by Quality: Excellent, Good, Fair, Poor (multi-select)
- Filter by Pool Size: Vast, Large, Moderate, Small, Tiny (multi-select)
- Filter by Popularity
Multiple active filters use AND logic — a theme must match all active badge filters to appear.
---
## Theme Detail Pages
Click any theme card to open its detail page. Each page shows:
- The full on-theme card list with EDHREC rank, CMC, and synergy score
- Badge explanations for that theme
- Related themes (by tag overlap)
---
## Quality Dashboard
`/diagnostics/quality` (requires `SHOW_DIAGNOSTICS=1`) provides a catalog-level health overview:
- Distribution of themes by quality tier
- Average pool size per quality tier
- Themes flagged for editorial review (e.g., very low card count, no quality score)
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `ENABLE_THEMES` | `1` | Keep the theme browser and theme selector active. |
| `SHOW_THEME_QUALITY_BADGES` | `1` | Show quality badges in the theme catalog. |
| `SHOW_THEME_POOL_BADGES` | `1` | Show pool size badges in the theme catalog. |
| `SHOW_THEME_POPULARITY_BADGES` | `1` | Show popularity badges in the theme catalog. |
| `SHOW_THEME_FILTERS` | `1` | Show filter chips in the theme catalog. |
| `THEME_MIN_CARDS` | `5` | Minimum cards required for a theme to appear in the catalog. |
| `WEB_THEME_PICKER_DIAGNOSTICS` | `1` | Unlock `/themes/metrics`, uncapped synergies, and extra metadata. |
| `THEME_MATCH_MODE` | `permissive` | Fuzzy match mode for supplemental themes: `permissive` continues on unresolved themes, `strict` stops the build. |
---
## Rebuilding the Theme Catalog
If you update card data or theme YAML files, rebuild the merged catalog:
```powershell
# Docker Compose:
docker compose run --rm --entrypoint bash web -lc "python -m code.scripts.build_theme_catalog"
# Local:
python -m code.scripts.build_theme_catalog
```