refactor(regime): collapse the monitor page, fix the OAS rebuild window

The page had twelve stacked blocks, several of them different views of the
same numbers. The quadrant plot and the score-history chart drew the same two
series from the same query key, which read as two datasets; they are now one
card with a Time | Path toggle. The two pillar disclosures become one grouped
table, and three prose blocks (data quality, basket, coverage) become one
provenance chip strip. Page text is now limited to what changes how the reader
interprets today's number; the rest moved to the methodology doc.

Removes three stale-threshold bugs of one class. The quadrant fell back to v2's
60/60 dividers when quadrant_config was absent -- the real values are 50/40 and
they feed alert_service, so the chart could disagree with what actually fires.
The gauge fell back to v2's 30/60/80 band ticks, and drew a divider line that
always landed on its own "elevated" tick. The time series' reference lines were
at 30/60/80, which correspond to nothing in v3; they are now per-axis dashed
lines read from the same quadrant_config. Rendering also surfaced a live
clipping bug inherited from the old chart: margin.left -18 against YAxis
width 28 left ~10px for a 3-digit label, so every Y tick was cut off.

HY_OAS_WINDOW_DAYS was 400 *calendar* days while a rebuild replays
REBUILD_SESSIONS = 400 *trading* sessions (~579 calendar days), so the oldest
~180 days of any rebuild got no OAS at all and both credit sensors returned
None. State then lands at 80% coverage and Warning at exactly MIN_COVERAGE, so
both still publish bands -- a series that looks homogeneous while its oldest
rows were scored without credit. Widened to 700. This needs no methodology
bump: C1 reads [-1] and W3 reads [-21], both from the end, so widening only
prepends and every live score is bit-identical. Sequenced deliberately, since
acting on the open findings below bumps METHODOLOGY and fires the rebuild.

A just-collected fundamental observation was hidden until its effective date --
one day, three over a weekend -- because the live reading called the
point-in-time function, so refreshing appeared to do nothing. That was the
opposite of what the doc claimed. fundamental_overlay stays the gated record
(it runs for every replayed date during a rebuild); current_observation is the
live reading and reports the effective date instead of blanking the content.
Nothing in the overlay is scored, so showing it early cannot reach a published
number.

Documents four calculation findings. Three are not implemented, since each
changes a published score and so requires a v4 cut: State's top band is a
credit-event band (credit returns 0.0 rather than None below the 3.5 anchor, so
it is pinned at zero at weight 20 -- with everything else pegged State computes
to exactly 80.0, the breaking threshold); V1 saturates at VIX 30; and the
deliberate max(P1,P2,P3) defeats P3's anchoring because P1 is binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 17:53:45 +02:00
co-authored by Claude Opus 5
parent 23de8c9540
commit 46ace501a2
8 changed files with 646 additions and 434 deletions
+41
View File
@@ -27,6 +27,7 @@ from app.services.regime_monitor_service import (
breadth_level_score,
drawdown_pct,
f2_credit_spreads,
current_observation,
fundamental_overlay,
p1_trend_break,
p2_death_cross,
@@ -238,6 +239,46 @@ def test_fundamental_overlay_never_replays_before_effective_date_and_expires():
assert expired["available"] is False
def test_live_observation_is_visible_before_its_effective_date():
"""Refreshing must not look like it did nothing.
The stored snapshot keeps the effective-date gate so a rebuild cannot
backdate an observation, but the live card reports that date instead of
blanking the content -- otherwise a Friday refresh stays invisible until
Monday.
"""
overrides = {
"f1_score": 50.0,
"f3_score": 100.0,
"capex": {"GOOGL": "holding"},
"good_news_stock_down": "yes",
"reasoning": "fresh read",
"fetched_at": "2026-06-01T10:00:00+00:00",
"effective_date": "2026-06-02",
}
config = {**DEFAULT_CONFIG, "fundamental_staleness_days": 80}
before = date(2026, 6, 1)
record = fundamental_overlay(overrides, config, before)
now = current_observation(overrides, config, before)
# Same day, same observation: the record hides it, the live reading shows it.
assert record["capex"] is None and record["reasoning"] is None
assert now["capex"] == {"GOOGL": "holding"}
assert now["reasoning"] == "fresh read"
assert now["capex_stress"] == 50.0
assert now["earnings_stress"] == 100.0
# ...while still reporting when the stored record picks it up.
assert now["pending"] is True
assert now["effective_date"] == "2026-06-02"
assert now["available"] is True
# Staleness still expires the live reading.
assert current_observation(overrides, config, date(2026, 8, 22))["stale"] is True
assert current_observation(overrides, config, date(2026, 8, 22))["available"] is False
def test_fundamentals_do_not_move_the_warning_score():
"""The v3 complaint: a maxed-out LLM read must not silently do nothing.