Two hardening fixes from Codex's audit:
- _pick_answer falls back to the curated baseline if the live pool is empty,
so an admin tombstoning every answer in a variant can't divide-by-zero the
daily picker. Test added (test_picker_survives_empty_live_pool). Chosen over
a minimum-count block: robust without refusing legitimate removals.
- Removal copy is now honest — "Removed from future puzzles (today's answer is
already set)" — since a tombstone doesn't rewrite today's generated
daily_puzzles row. Panel intro updated to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Daily Word pool curation, full add/delete/import — no redeploys to fix tone:
- Remove ANY pool word, curated or admin-added, via a word_pool_removed
tombstone table. Runtime pool = (static ∪ added) − removed, so even a
baked-in word can be pulled on negative feedback. Reversible: a "Removed"
list with one-tap Restore lifts the tombstone. Lookup now surfaces a Remove
button when in-pool, Restore when removed.
- Import a vetted list (paste or .txt/.csv upload, read client-side): validates
each word (alpha · 5–6 · in guess dictionary), ignores duplicates, and reports
rejects with reasons. Re-adding/importing a removed word lifts its tombstone.
- Word Search theme delete already existed (Edit/Remove per theme) — verified.
Pool stays the clean 251/224; today's noisy LLM enrichment is discarded.
Tests: +tests/test_pool_admin.py, extended test_word_pool_admin. 222 pytest +
11 vitest green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>