WOTD #4/#5 content quality + Editorial Asymmetric /word page (CD)

Content quality ("LLM polishes, dictionary anchors"):
- New wotd._polish: rewrites the real dictionary gloss into ONE warm plain
  sentence + two clear everyday example sentences, grounded in the real
  definition (no invented meanings). Stored in new wotd_pool/daily_wotd columns
  gloss + usage, alongside the raw definition/examples which stay the anchor.
- harvest() polishes each new word; pick_daily() lazily polishes + caches back
  any older pooled word that lacks a gloss (client threaded through run_daily).
- Admin word-add polishes on insert; re-pick passes an LLM client so quote
  meaning / word gloss fill on a forced fresh pick.
- /api/word/today now prefers gloss + usage, falling back to the raw dictionary
  def/examples when polish is absent (so it's always safe).
- db._migrate adds gloss/usage to wotd_pool + daily_wotd (idempotent ALTER).

Frontend — /word redesigned to CD's "Editorial Asymmetric": faded oversized
initial bleeding off the right, vertical part-of-speech rail, big Newsreader
word, airy definition, left-ruled italic example sentences, outline Listen
button + date. (Uses our self-hosted Newsreader/Hanken stack rather than the
mockup's Google fonts; the made-up syllable respelling is omitted since we only
have real IPA.)

Tests: _polish parse/trim/cap, harvest stores gloss/usage, pick lazy-polishes
older words, admin gloss flows through to /api/word/today. 403 backend + 27 fe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-23 06:08:14 -04:00
parent e5f3d942e2
commit cebbed58ab
6 changed files with 196 additions and 65 deletions
+33
View File
@@ -85,3 +85,36 @@ def test_propose_words_accepts_dicts_and_strings():
return '{"words": [{"word": "Serene", "pos": "Adjective"}, "dawn", {"word": ""}]}'
out = wotd._propose_words(C(), 3)
assert out == [{"word": "serene", "pos": "adjective"}, {"word": "dawn", "pos": None}]
def test_harvest_stores_polished_gloss_and_usage(conn, monkeypatch):
monkeypatch.setattr(wotd, "_polish", lambda c, w, pos, d: {"gloss": f"{w}: plain.", "examples": [f"A {w} day."]})
wotd.harvest(conn, FakeClient())
row = conn.execute("SELECT gloss, usage FROM wotd_pool WHERE word='serene'").fetchone()
assert row[0] == "serene: plain." and json.loads(row[1]) == ["A serene day."]
def test_pick_lazy_polishes_older_words(conn, monkeypatch):
monkeypatch.setattr(wotd, "_polish", lambda c, w, pos, d: None) # harvested before polish existed
wotd.harvest(conn, FakeClient())
assert conn.execute("SELECT gloss FROM wotd_pool WHERE word='serene'").fetchone()[0] is None
monkeypatch.setattr(wotd, "_polish", lambda c, w, pos, d: {"gloss": "calm and untroubled.", "examples": ["The lake was serene."]})
a = wotd.pick_daily(conn, feature_date="2026-06-22", client=FakeClient())
assert a["gloss"] == "calm and untroubled." and json.loads(a["usage"]) == ["The lake was serene."]
# cached back to the pool so it's only generated once
assert conn.execute("SELECT gloss FROM wotd_pool WHERE id=?", (a["pool_id"],)).fetchone()[0] == "calm and untroubled."
def test_polish_trims_and_caps_two_examples():
class C:
def chat_text(self, m):
return 'sure: {"gloss": " Calm and peaceful. ", "examples": ["One.", "Two.", "Three."]} done'
out = wotd._polish(C(), "serene", "adjective", "x")
assert out["gloss"] == "Calm and peaceful." and out["examples"] == ["One.", "Two."]
def test_polish_returns_none_without_a_gloss():
class C:
def chat_text(self, m):
return '{"examples": ["x"]}'
assert wotd._polish(C(), "serene", None, "x") is None