Add topic/flavor categorization and category browsing

- 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>
This commit is contained in:
jay
2026-05-30 11:21:53 +00:00
parent f4842ed100
commit 38057d0354
5 changed files with 165 additions and 6 deletions
+15
View File
@@ -56,6 +56,8 @@ CREATE TABLE IF NOT EXISTS article_scores (
accepted INTEGER,
reason_code TEXT,
reason_text TEXT,
topic TEXT,
flavor TEXT,
model_name TEXT,
scored_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
@@ -102,4 +104,17 @@ def connect(db_path: Path | str) -> sqlite3.Connection:
def init_db(conn: sqlite3.Connection) -> None:
conn.executescript(SCHEMA)
_migrate(conn)
conn.commit()
def _migrate(conn: sqlite3.Connection) -> None:
"""Add columns introduced after the initial schema to existing databases.
CREATE TABLE IF NOT EXISTS never alters an existing table, so new columns
need an explicit, idempotent ALTER guarded by the current column set.
"""
cols = {row["name"] for row in conn.execute("PRAGMA table_info(article_scores)")}
for column in ("topic", "flavor"):
if column not in cols:
conn.execute(f"ALTER TABLE article_scores ADD COLUMN {column} TEXT")