Hero guardrail (core to the promise, not cosmetic):
- New hero.py: the lead story is chosen with a stricter filter than the rest of
the brief — very low cortisol/ragebait and no grief/medical/violence terms
(cancer, glioblastoma, death, diagnosis, ...). Such constructive-but-charged
stories stay among the five; they just never lead by default.
- /api/brief applies user avoid-terms FIRST, then lead_with_gentle, so personal
boundaries always take precedence over the general guardrail.
- Verified live: the brief no longer leads with a glioblastoma story.
Card polish (per review):
- Secondary cards with no real image are now text-first (no row of empty media
bands); hero still always shows media or a typographic fallback.
- Inline tuning actions are quiet until hover/focus on pointer devices, and stay
visible (softer) on touch — less interface machinery.
Tests: hero safety + lead reordering (70 total).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- queries.py: shared read-only query helpers (feed, brief, category counts)
returning plain dicts, used by the API and available to the CLI.
- api.py: FastAPI service with Pydantic response models (the companion-app
contract), CORS, and endpoints for categories, feed, brief, and health;
mounts a static site at /.
- static/index.html: minimal dependency-free site rendering the daily five
and topic/flavor category browsing.
- 'goodnews serve' command launches uvicorn (lazy import; core CLI stays
pure-stdlib). Web deps live behind the optional [web] extra.
- Dockerfile + .dockerignore + build-system metadata so the service installs
and deploys cleanly, with the DB mounted as a shared volume.
- README: web/API and deployment docs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New taxonomy module: single source of truth for 6 topics x 5 flavors,
shared by the LLM response schema (enum-constrained) and validation.
- Classifier now assigns one topic + one flavor per article; json_schema
enums force valid values, with coercion as a safety net.
- article_scores gains topic/flavor columns via an idempotent migration.
- New 'list-category' command to browse by topic and/or flavor, ranked by
composite score.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>