Fix stale pinned-brief images; enrich all 7 + retry failures

Root cause (Codex audit): the client pins the brief by generated_at, but image
enrichment populates image_url AFTER the brief is built without bumping
generated_at — so a verbatim pinned copy stays imageless even once the server
has the image. The reclassify rebuilt the brief and the early pin stuck.

- Frontend: when reusing a pinned brief (same generated_at), refresh server-owned
  metadata by article id (esp. image_url) while preserving the user's order and
  replacements. Re-saves the merged view so it stays current.
- enrich_brief_images: default limit 5 -> 7 (any brief item can become the hero
  via the client fallback or a replace, so cover the whole brief).
- Don't cache image failures forever: retry brief items still missing an image
  after a TTL (retry_days=2) instead of stamping them imageless permanently.

Pairs with the hero image fallback (dd0087b). 99 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-02 07:38:54 +00:00
parent dd0087b8b3
commit 6d5bcb13e5
2 changed files with 27 additions and 10 deletions
+9 -1
View File
@@ -119,7 +119,15 @@
const sameServerBrief =
view && view.generated_at && fetched.generated_at && view.generated_at === fetched.generated_at;
if (!fresh && sameServerBrief && Array.isArray(view.items) && view.items.length) {
brief = { ...fetched, items: view.items };
// Same server brief: keep the user's pinned order + replacements, but
// refresh server-owned metadata by id. image_url especially is enriched
// AFTER the brief is built (without bumping generated_at), so a verbatim
// pinned copy can stay imageless forever. Items the user swapped in
// (absent from the fresh brief) keep their own data.
const freshById = new Map(fetched.items.map((a) => [a.id, a]));
const items = view.items.map((it) => freshById.get(it.id) ?? it);
brief = { ...fetched, items };
P.saveJSON(BRIEF_VIEW_KEY, { generated_at: fetched.generated_at, items });
} else {
brief = fetched;
P.saveJSON(BRIEF_VIEW_KEY, { generated_at: fetched.generated_at, items: fetched.items });