diff --git a/frontend/src/routes/admin/+page.svelte b/frontend/src/routes/admin/+page.svelte
index 22e0289..0c79079 100644
--- a/frontend/src/routes/admin/+page.svelte
+++ b/frontend/src/routes/admin/+page.svelte
@@ -76,7 +76,7 @@
async function removeWord(w) {
try {
wpPool = await delJSON('/api/admin/word/pool/' + encodeURIComponent(w));
- wpMsg = `Removed “${w}”. It won’t appear as an answer — restore it any time below.`;
+ wpMsg = `Removed “${w}” from future puzzles (today’s answer is already set). Restore it any time below.`;
await refreshLookup();
} catch { /* ignore */ }
}
@@ -817,8 +817,9 @@
{:else if section === 'games'}
Daily Word pool
-
Look up a word and add it to the answer pool. Only real, 5- or 6-letter
- words in the guess dictionary qualify, so the daily answer is always solvable.
+
Look up a word to add or remove it from the answer pool. Only real, 5- or 6-letter
+ words in the guess dictionary qualify, so the daily answer is always solvable. Removals take
+ effect for future puzzles and can be restored any time.
set[s
def _pick_answer(conn: sqlite3.Connection, date: str, variant: str) -> str:
pool = answer_pool(conn, variant)
+ if not pool: # safety net: removals must never empty the pool — fall back to curated
+ pool = sorted(_POOL.get(variant, []))
recent = _recent_answers(conn, variant, max(1, len(pool) // 2))
start = _seed(date, "word", variant) % len(pool)
for i in range(len(pool)):
diff --git a/tests/test_pool_admin.py b/tests/test_pool_admin.py
index 80d4462..4c18e5d 100644
--- a/tests/test_pool_admin.py
+++ b/tests/test_pool_admin.py
@@ -85,6 +85,17 @@ def test_import_dedupes_and_validates(conn):
assert new5[1] in games.answer_pool(conn, "5")
+def test_picker_survives_empty_live_pool(conn):
+ """An overzealous admin could tombstone every answer in a variant. The live
+ pool then reads empty, but the daily picker must not divide-by-zero — it falls
+ back to the curated baseline rather than crashing."""
+ for w in list(games._POOL["5"]):
+ games.remove_pool_word(conn, w)
+ assert games.answer_pool(conn, "5") == [] # live pool truly empty
+ ans = games._pick_answer(conn, "2026-06-11", "5") # must not raise
+ assert ans in set(games._POOL["5"]) # fell back to curated
+
+
def test_import_relifts_removed_word(conn):
w = _a_curated("5")
games.remove_pool_word(conn, w)