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
+22 -9
View File
@@ -14,10 +14,29 @@ def build_daily_brief(
window_days: int = 3,
) -> int:
target_date = brief_date or date.today().isoformat()
# Compose the selection first so we can tell whether anything actually
# changed. A calm daily brief shouldn't repeatedly hand the reader a locked
# door: push paywalled candidates below readable ones (stable sort) first.
rows = _candidate_articles(conn, target_date, window_days)
rows = sorted(rows, key=lambda r: is_paywalled(r["canonical_url"]))
selected = _select_diverse(rows, limit)
selected_ids = [row["id"] for row in selected]
existing = conn.execute("SELECT id FROM daily_briefs WHERE brief_date = ?", (target_date,)).fetchone()
if existing and not replace:
return int(existing["id"])
if existing and replace:
if existing:
existing_ids = [
r["article_id"]
for r in conn.execute(
"SELECT article_id FROM daily_brief_items WHERE brief_id = ? ORDER BY rank",
(existing["id"],),
)
]
# Idempotent: if the selection is unchanged, leave the brief (and its
# created_at freshness stamp) alone — a 15-minute rebuild with no new
# data is a no-op, so a reader's pinned view holds.
if existing_ids == selected_ids or not replace:
return int(existing["id"])
conn.execute("DELETE FROM daily_briefs WHERE id = ?", (existing["id"],))
brief_id = conn.execute(
@@ -25,12 +44,6 @@ def build_daily_brief(
(target_date, f"Highlights from Today - {target_date}"),
).lastrowid
rows = _candidate_articles(conn, target_date, window_days)
# A calm daily brief shouldn't repeatedly hand the reader a locked door:
# push paywalled candidates below readable ones (stable, so composite order
# is preserved within each group) before selecting the five.
rows = sorted(rows, key=lambda r: is_paywalled(r["canonical_url"]))
selected = _select_diverse(rows, limit)
for index, row in enumerate(selected, start=1):
conn.execute(
"""