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
+4
View File
@@ -82,6 +82,7 @@ def test_word_add_and_repick(api_app, monkeypatch):
"audio_url": None, "definition": "Giving off light; radiant.", "examples": []}}
monkeypatch.setattr(wotd, "_lookup", lambda w, prefer_pos=None: fake.get(w))
monkeypatch.setattr(wotd, "_cache_audio", lambda url, word: None)
monkeypatch.setattr(wotd, "_polish", lambda c, w, pos, d: {"gloss": f"{w} means lovely.", "examples": [f"What a {w} morning."]})
tc = _admin(api_app)
assert tc.post("/api/admin/joys/word/add", json={"word": "serene"}).json()["ok"]
@@ -90,6 +91,9 @@ def test_word_add_and_repick(api_app, monkeypatch):
assert {"serene", "luminous"} <= {it.get("word") for it in items}
assert tc.post("/api/admin/joys/word/repick").json()["picked"] is True
first = tc.get("/api/word/today").json()
# the LLM-polished gloss + sentence are what the page shows (not the raw dictionary def)
assert first["definition"] == f"{first['word']} means lovely."
assert first["examples"] == [f"What a {first['word']} morning."]
assert tc.post("/api/admin/joys/word/repick").json()["picked"] is True
second = tc.get("/api/word/today").json()
assert second["word"] != first["word"]