dashboard: cut page sync latency to ~scan-time + ≤5 s

Three display-side fixes, no signal-logic changes:
- run_cycle publishes snapshot.json BEFORE the news refresh — news I/O
  (FF + 4 RSS + possible DeepSeek calls) took 20-30 s on TTL-expired
  cycles and kept fresh signals off the page (publish_all split into
  publish_snapshot/publish_news, kept for boot)
- page polls every 5 s (was 20 s) using conditional GETs: dropped the
  ?t= cache-buster + no-store so Apache answers unchanged files with
  bodyless 304s
- boundary refetch sniper re-armed: fires 2 s after next_scan_epoch
  regardless of distance (the old delay<POLL_MS guard would have been
  dead code at 5 s polling)

New Module 20 test locks the snapshot-before-news ordering. 352 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017mbXs9HRawZhD1uZ3ze7zQ
This commit is contained in:
Harwinder Sekhon
2026-08-06 22:35:13 -04:00
parent b831001cfb
commit c70f58a572
3 changed files with 43 additions and 10 deletions
+16 -5
View File
@@ -371,18 +371,29 @@ class DashboardPublisher:
except Exception:
logger.exception("news refresh failed — keeping last payload")
def publish_all(self) -> None:
now = datetime.now(timezone.utc)
publish(build_snapshot_payload(self.snapshots, now),
os.path.join(self.out_dir, SNAPSHOT_FILE))
def publish_snapshot(self) -> None:
publish(build_snapshot_payload(
self.snapshots, datetime.now(timezone.utc)),
os.path.join(self.out_dir, SNAPSHOT_FILE))
def publish_news(self) -> None:
if self.news_payload is not None:
publish(self.news_payload, os.path.join(self.out_dir, NEWS_FILE))
def publish_all(self) -> None:
self.publish_snapshot()
self.publish_news()
def run_cycle(self, tfs: List[Timeframe]) -> None:
for tf in tfs: # sequential — see module docstring (cache dedup)
self.scan_tf(tf)
# Snapshot lands BEFORE the news refresh: when the news TTL has
# expired, refresh_news does FF + 4 RSS fetches (and possibly
# DeepSeek calls) — 20-30 s that must not keep fresh signals off
# the page.
self.publish_snapshot()
self.refresh_news()
self.publish_all()
self.publish_news()
def run_forever(self) -> None:
self.run_cycle(DASHBOARD_TFS) # populate the page at boot