fix(regime): reseed stored history on a sensor change, and stop faking an observation

Two review findings on 46ace50.

[P1] Raising HY_OAS_WINDOW_DAYS to 700 only reached newly computed rows. A
routine run recomputes the latest trading date alone, and `rebuilding` was keyed
on "no v3 snapshot exists at all", which is false once the cutover has run --
so every row already written kept the credit gap the wider window exists to
close, indefinitely.

Adds SENSOR_REVISION: stamped into each snapshot, absent on pre-marker rows
(read as 1), and a stored revision below the current one triggers exactly one
reseed. Deliberately not METHODOLOGY, which would partition the history API and
discard the cached event study -- neither warranted, since the study recomputes
its Warning series from source rather than reading snapshots and so cannot be
staled by a reseed.

The reseed is bounded by REBUILD_LOOKBACK_DAYS in calendar days rather than a
session count, because the binding constraint is the OAS fetch: each replayed
row needs W3's 20-business-day lookback inside HY_OAS_WINDOW_DAYS. Replaying by
session count would have left the oldest stored rows unrepaired -- the exact
rows the fix targets. At 672 days the replay covers ~464 sessions, W3's oldest
requirement lands on the first fetched OAS day, and the ~400-session series the
cutover wrote is fully covered. A test asserts that relationship so the two
constants cannot drift back into recreating the gap.

[P2] With nothing ever collected, current_observation returned available=true
and the default placeholders -- "unknown" for every hyperscaler, "mixed" for the
reaction -- so the card announced a reading that never happened. Those are the
absence of an observation, not an observation of absence. Gated on `observed`
(non-null fetched_at, the one field every path writing real content stamps),
which blanks the content and drives a proper empty state naming where an admin
collects one. This was a regression from 46ace50; fundamental_overlay never had
it, since no observation means no effective date means pending.

Also renames the leftover v2 identifiers in the touched paths
(rewrite_existing_v2, latest_v2).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 18:22:25 +02:00
co-authored by Claude Opus 5
parent 7dc804be2b
commit 3483797e75
5 changed files with 233 additions and 25 deletions
+18 -3
View File
@@ -125,15 +125,30 @@ const CAPEX_TONE: Record<CapexState, string> = {
unknown: 'text-gray-500',
};
const OVERLAY_TITLE = 'Fundamental overlay · context, not scored';
function FundamentalOverlayCard({ overlay }: { overlay: RegimeFundamentalOverlay }) {
const capex = overlay.capex ?? {};
const reaction = overlay.good_news_stock_down;
// Nothing collected: the stored default is "unknown" for every hyperscaler
// and "mixed" for the reaction, which are placeholders, not a reading.
if (overlay.observed === false) {
return (
<div className="glass border border-white/[0.06] p-5">
<div className="text-[11px] uppercase tracking-wider text-gray-500">{OVERLAY_TITLE}</div>
<p className="mt-3 text-xs text-gray-500">
No observation collected yet. An admin can collect one under Admin · Monitor settings. It is
context only it never enters State or Warning.
</p>
</div>
);
}
return (
<div className="glass border border-white/[0.06] p-5">
<div className="flex flex-wrap items-baseline justify-between gap-2">
<div className="text-[11px] uppercase tracking-wider text-gray-500">
Fundamental overlay · context, not scored
</div>
<div className="text-[11px] uppercase tracking-wider text-gray-500">{OVERLAY_TITLE}</div>
<div className="flex flex-wrap items-center gap-2 text-[11px] text-gray-500">
{overlay.source && <span>{overlay.source}</span>}
{/* When pending, the line below is the single carrier of this date. */}