diff --git a/CHANGELOG.md b/CHANGELOG.md index ffeb7bf..54c9995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [Unreleased] ### Added +- Tests: added `test_headless_skips_owned_prompt_when_files_present` to guard the headless runner against regressions when owned card lists are present. - Included the tiny `csv_files/testdata` fixture set so CI fast determinism tests have consistent sample data. ### Changed @@ -22,6 +23,7 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - Relaxed fast-path catalog validation to allow empty synergy lists while still warning on missing or malformed data types. ### Fixed +- 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. ## [2.3.0] - 2025-09-26 diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 618539b..85f8eb9 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -8,6 +8,7 @@ - Delivered multi-theme random builds with deterministic cascade, strict match support, and polished HTMX/UI flows. - Added opt-in telemetry counters, reroll throttling safeguards, and structured diagnostics exports. - Expanded tooling, documentation, and QA coverage for theme governance, performance profiling, and seed history management. +- Hardened the headless runner against owned-card prompt loops with optional automation overrides and regression coverage. ## Highlights ### Multi-theme random builds @@ -29,6 +30,7 @@ - Diagnostics badge polish, recent/favorite seeds panel, seed history API, and structured logging for random builds. - Sidecar exports include multi-theme metadata and locked commander indicators with consistent artifact sets. - Manual QA checklist updates and broader pytest coverage for multi-theme flows, reroll behavior, performance, and telemetry. +- Headless runner now auto-resolves owned-card prompts in headless mode with env-overridable defaults and a regression test. ### Maintenance & CI - Theme catalog schema now accepts optional IDs and the preview performance warm baseline was regenerated to restore the regression gate. @@ -44,6 +46,7 @@ - `test_random_multi_theme_webflows.py` covering reroll-same-commander caching and permalink round-trips for multi-theme runs. - `test_random_multi_theme_filtering.py` ensuring deterministic cascade across success tiers and sidecar metadata. - `test_random_surprise_reroll_behavior.py` protecting Surprise Me input preservation and locked-commander cache reuse. + - `test_headless_skips_owned_prompt_when_files_present.py` ensuring headless builds stay non-interactive when owned card lists are present. - **Random mode tooling & docs** - Curated theme pool exclusions at `config/random_theme_exclusions.yml`, reporting helper `code/scripts/report_random_theme_pool.py --write-exclusions`, and companion docs in `docs/random_theme_exclusions.md`. - Performance guard `code/scripts/check_random_theme_perf.py` comparing profiler output (`code/scripts/profile_multi_theme_filter.py`) with `config/random_theme_perf_baseline.json` (`--update-baseline` refreshes the file). @@ -84,6 +87,7 @@ - Removed ultra-rare themes (frequency ≤1) unless protected via whitelist, keeping results focused on supported experiences. - Corrected commander eligibility rules to restrict non-creature legendary permanents and honor “can be your commander” text. - Refreshed `logs/perf/theme_preview_warm_baseline.json` to fix preview performance CI failures stemming from malformed baseline data. +- Prevented the headless runner from looping on bracket selection when owned card files exist by scripting prompt responses and exposing `HEADLESS_USE_OWNED_ONLY` / `HEADLESS_OWNED_SELECTION` overrides. ## Upgrade notes - Enable multi-theme random builds via existing Random Mode flags; strict matching persists automatically across UI, API, permalink, and export contexts. diff --git a/code/headless_runner.py b/code/headless_runner.py index b3d6578..530369b 100644 --- a/code/headless_runner.py +++ b/code/headless_runner.py @@ -40,6 +40,31 @@ def _write_tagging_flag(tagging_json): with open(tagging_json, 'w', encoding='utf-8') as f: json.dump({'tagged_at': datetime.now().isoformat(timespec='seconds')}, f) + +def _headless_owned_cards_dir() -> str: + env_dir = os.getenv("OWNED_CARDS_DIR") or os.getenv("CARD_LIBRARY_DIR") + if env_dir: + return env_dir + if os.path.isdir("owned_cards"): + return "owned_cards" + if os.path.isdir("card_library"): + return "card_library" + return "owned_cards" + + +def _headless_list_owned_files() -> List[str]: + folder = _headless_owned_cards_dir() + entries: List[str] = [] + try: + if os.path.isdir(folder): + for name in os.listdir(folder): + path = os.path.join(folder, name) + if os.path.isfile(path) and name.lower().endswith((".txt", ".csv")): + entries.append(path) + except Exception: + return [] + return sorted(entries) + def run( command_name: str = "", add_creatures: bool = True, @@ -68,6 +93,17 @@ def run( seed: Optional[int | str] = None, ) -> DeckBuilder: """Run a scripted non-interactive deck build and return the DeckBuilder instance.""" + owned_prompt_inputs: List[str] = [] + owned_files_available = _headless_list_owned_files() + if owned_files_available: + use_owned_flag = _parse_bool(os.getenv("HEADLESS_USE_OWNED_ONLY")) + if use_owned_flag: + owned_prompt_inputs.append("y") + selection = (os.getenv("HEADLESS_OWNED_SELECTION") or "").strip() + owned_prompt_inputs.append(selection) + else: + owned_prompt_inputs.append("n") + scripted_inputs: List[str] = [] # Commander query & selection scripted_inputs.append(command_name) # initial query @@ -85,6 +121,7 @@ def run( scripted_inputs.append("0") else: scripted_inputs.append("0") # stop at primary + scripted_inputs.extend(owned_prompt_inputs) # Bracket (meta power / style) selection; default to 3 if not provided scripted_inputs.append(str(bracket_level if isinstance(bracket_level, int) and 1 <= bracket_level <= 5 else 3)) # Ideal count prompts (press Enter for defaults). Include fetch_lands if present. diff --git a/code/tests/test_seeded_builder_minimal.py b/code/tests/test_seeded_builder_minimal.py index 8413082..37184da 100644 --- a/code/tests/test_seeded_builder_minimal.py +++ b/code/tests/test_seeded_builder_minimal.py @@ -15,4 +15,27 @@ def test_headless_seed_threads_into_builder(monkeypatch): assert getattr(out1, "seed", None) == getattr(out2, "seed", None) == 999 # Basic sanity: commander selection should have occurred assert isinstance(getattr(out1, "commander_name", ""), str) - assert isinstance(getattr(out2, "commander_name", ""), str) \ No newline at end of file + assert isinstance(getattr(out2, "commander_name", ""), str) + + +def test_headless_skips_owned_prompt_when_files_present(monkeypatch, tmp_path): + monkeypatch.setenv("CSV_FILES_DIR", os.path.join("csv_files", "testdata")) + owned_dir = tmp_path / "owned" + owned_dir.mkdir() + (owned_dir / "my_cards.txt").write_text("1 Sol Ring\n", encoding="utf-8") + monkeypatch.setenv("OWNED_CARDS_DIR", str(owned_dir)) + + builder = run( + command_name="Krenko", + add_lands=False, + add_creatures=False, + add_non_creature_spells=False, + add_ramp=False, + add_removal=False, + add_wipes=False, + add_card_advantage=False, + add_protection=False, + ) + + assert getattr(builder, "bracket_level", None) in {None, 3} + assert getattr(builder, "use_owned_only", False) is False \ No newline at end of file diff --git a/logs/roadmaps/roadmap_4_5_theme_refinement.md b/logs/roadmaps/roadmap_4_5_theme_refinement.md deleted file mode 100644 index 021da04..0000000 --- a/logs/roadmaps/roadmap_4_5_theme_refinement.md +++ /dev/null @@ -1,479 +0,0 @@ -# Roadmap: Theme Refinement (M2.5) - -This note captures gaps and refinements after generating `config/themes/theme_list.json` from the current tagger and constants. - - - -## Unified Task Ledger (Single Source of Truth) -Legend: [x]=done, [ ]=open. Each line starts with a domain tag for quick filtering. - -### Completed (Retained for Traceability) -[x] PHASE Extraction prototype: YAML export script, per-theme files, auto-export, fallback path -[x] PHASE Merge pipeline: analytics regen, normalization, precedence merge, synergy cap, fallback -[x] PHASE Validation & tests: models, schemas, validator CLI, idempotency tests, strict alias pass, CI integration -[x] PHASE Editorial enhancements: examples & synergy commanders, augmentation heuristics, deterministic seed, description mapping, lint, popularity buckets -[x] PHASE UI integration: picker APIs, filtering, diagnostics gating, archetype & popularity badges, stale refresh -[x] PREVIEW Endpoint & sampling base (deterministic seed, diversity quotas, role classification) -[x] PREVIEW Commander bias (color identity filter, overlap/theme bonuses, diminishing overlap scaling initial) -[x] PREVIEW Curated layering (examples + curated synergy insertion ordering) -[x] PREVIEW Caching: TTL cache, warm index build, cache bust hooks, size-limited eviction -[x] PREVIEW UX: grouping separators, role chips, curated-only toggle, reasons collapse, tooltip