fix: QOTD/WOTD freshness — pick within the freshest cohort, not the rotated pool

Both selectors ordered candidates least-recently-shown, then daily.seeded_order()
ROTATED the whole list and took [0] — an arbitrary date-hashed item, undoing the
ordering. Result: repeats (quote id 2 on 6/28+6/29; word "harmony" on 6/25+6/28),
no guarantee a pool item is shown before it recurs.

Fix: daily.freshest(rows) returns the freshest cohort only — every NEVER-shown
item while any remain, else the oldest-shown group. quote/wotd _candidates use it;
seeded_order now picks deterministically WITHIN that cohort. So every pool item is
featured once before any repeat, then cycles oldest-first. Dropped the unused
_NO_REPEAT_POOL window. Tests: no-repeat-until-exhausted (quote + wotd) + a
freshest() unit test. 428 backend tests green.

(Separate follow-up: expand the QOTD pool from 16 → 90+ vetted public-domain
quotes for a longer no-repeat window.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-29 05:39:06 -04:00
parent 414a4c4b8b
commit 0ae789752e
5 changed files with 64 additions and 13 deletions
+25
View File
@@ -56,3 +56,28 @@ def test_get_today_never_empty(conn):
def test_run_daily_seeds_then_picks(conn):
r = quote.run_daily(conn)
assert r["pool"] == len(quote.SEED) and r["picked"]
def test_no_repeat_until_pool_exhausted(conn):
"""Every quote is featured exactly once before ANY repeat; then the oldest-shown
repeats first. (Regression for the rotate-the-whole-pool selector bug.)"""
import datetime
quote.seed(conn)
n = len(quote.SEED)
d0 = datetime.date(2026, 1, 1)
picks = [quote.pick_daily(conn, feature_date=(d0 + datetime.timedelta(days=i)).isoformat())["pool_id"]
for i in range(n)]
assert len(set(picks)) == n # full coverage, no repeat within the pool
nxt = (d0 + datetime.timedelta(days=n)).isoformat()
assert quote.pick_daily(conn, feature_date=nxt)["pool_id"] == picks[0] # oldest repeats first
def test_freshest_cohort():
from goodnews import daily
# never-shown win outright (the oldest shown item is ignored while any never-shown remain)
assert daily.freshest([{"id": 1, "shown_at": "2026-01-02"},
{"id": 2, "shown_at": None}, {"id": 3, "shown_at": None}]) == [2, 3]
# all shown → only the oldest-shown cohort
assert daily.freshest([{"id": 1, "shown_at": "2026-01-03"},
{"id": 2, "shown_at": "2026-01-01"}, {"id": 3, "shown_at": "2026-01-01"}]) == [2, 3]
assert daily.freshest([]) == []
+16
View File
@@ -142,3 +142,19 @@ def test_polish_returns_none_with_empty_examples():
def chat_text(self, m):
return '{"gloss": "A warm clear gloss.", "examples": []}'
assert wotd._polish(C(), "serene", "adjective", "x") is None
def test_no_repeat_until_pool_exhausted(conn):
"""Same freshness guarantee as QOTD: every word featured once before any repeat,
then the oldest-shown repeats first. (Regression for 'harmony' repeating after 3 days.)"""
import datetime
for w in ["alpha", "bravo", "charlie", "delta"]:
conn.execute("INSERT INTO wotd_pool (word, definition) VALUES (?, 'a definition')", (w,))
conn.commit()
n = 4
d0 = datetime.date(2026, 2, 1)
picks = [wotd.pick_daily(conn, feature_date=(d0 + datetime.timedelta(days=i)).isoformat())["pool_id"]
for i in range(n)]
assert len(set(picks)) == n
nxt = (d0 + datetime.timedelta(days=n)).isoformat()
assert wotd.pick_daily(conn, feature_date=nxt)["pool_id"] == picks[0]