Fresh server data overrides a pinned brief; pin holds otherwise

Per the agreed model: the brief is server-authoritative and a client Replace is
a soft override that yields when genuinely new data arrives.
- build_daily_brief is now idempotent: if the composed selection is unchanged it
  leaves the brief (and its created_at) alone, so the timer's 15-min rebuilds are
  no-ops when no new data landed.
- /api/brief exposes generated_at (the brief's created_at = a content-change
  stamp). The client pins its view against generated_at and keeps it across plain
  refreshes, but drops it and shows the fresh server brief when generated_at
  advances. Missed stories remain in the mood feeds.

Tests: idempotent rebuild (no-op vs content change) — 93 total.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-05-31 14:00:08 +00:00
parent f599f9d28e
commit 68a401eed6
5 changed files with 83 additions and 19 deletions
+3 -2
View File
@@ -120,11 +120,11 @@ def brief(conn: sqlite3.Connection, brief_date: str | None = None, limit: int =
return {"brief_date": None, "title": None, "items": []}
header = conn.execute(
"SELECT brief_date, title FROM daily_briefs WHERE brief_date = ?",
"SELECT brief_date, title, created_at FROM daily_briefs WHERE brief_date = ?",
(target_date,),
).fetchone()
if not header:
return {"brief_date": target_date, "title": None, "items": []}
return {"brief_date": target_date, "title": None, "created_at": None, "items": []}
rows = conn.execute(
f"""
@@ -143,6 +143,7 @@ def brief(conn: sqlite3.Connection, brief_date: str | None = None, limit: int =
return {
"brief_date": header["brief_date"],
"title": header["title"],
"created_at": header["created_at"],
"items": [dict(row) for row in rows],
}