fix(rss): derive feed lastBuildDate from item content dates, not _meta

Follow-up to v2.9.19: deriving lastBuildDate from _meta.last_updated only moved
the drift one level — that analysis timestamp is itself bumped on every publish
even when the ranked content is unchanged, so feed.xml kept differing between
develop and master. Use the freshest item's content date instead (the items only
change when the ranking actually changes). Verified: deterministic across reruns,
and the develop and master digests both yield 2026-06-18, so feed.xml is now
byte-identical across branches — the drift is fully eliminated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Nubenetes Bot
2026-06-20 12:46:52 +02:00
parent 4d25baee03
commit 01c514b07b
2 changed files with 20 additions and 21 deletions

View File

@@ -61,28 +61,27 @@ def generate_rss() -> None:
)
items = items[:ITEMS_PER_FEED]
# Deterministic build date: derive from the digest's content timestamp
# (_meta.last_updated, the actual analysis date) instead of wall-clock now().
# Using now() rewrote <lastBuildDate> on every republish, so a regenerated-
# but-unchanged feed still differed between develop and master (perpetual
# cosmetic drift). Mirrors the v2_optimizer pattern that prefers
# _meta.last_updated over file mtime. Falls back to the most recent item
# date, then a fixed constant, so the feed is always a pure function of input.
build_dt = None
meta_updated = digest.get("_meta", {}).get("last_updated", "")
if meta_updated:
# Deterministic build date = the freshest item's content date, so the feed
# is a pure function of its items. (This used wall-clock now() — rewritten on
# every republish — then _meta.last_updated, but that analysis timestamp is
# itself bumped on each publish even when the ranked content is unchanged, so
# the feed still drifted between develop and master. The item dates only move
# when the content actually changes.) Falls back to _meta.last_updated, then
# a fixed constant, only when no item carries a parseable date.
item_dates = []
for _it in items:
try:
build_dt = datetime.fromisoformat(meta_updated)
item_dates.append(datetime.strptime(_it.get("date", ""), "%Y-%m-%d"))
except Exception:
build_dt = None
if build_dt is None:
item_dates = []
for _it in items:
try:
item_dates.append(datetime.strptime(_it.get("date", ""), "%Y-%m-%d"))
except Exception:
pass
build_dt = max(item_dates) if item_dates else datetime(2026, 1, 1)
pass
if item_dates:
build_dt = max(item_dates)
else:
meta_updated = digest.get("_meta", {}).get("last_updated", "")
try:
build_dt = datetime.fromisoformat(meta_updated) if meta_updated else datetime(2026, 1, 1)
except Exception:
build_dt = datetime(2026, 1, 1)
build_date = _rfc822(build_dt)
lines = [