# Macro Regime Architecture — 2026-04-28

**Lane:** thv2-lane-34-macro-regime-dashboard
**Owner:** NEXUS COMMAND background agent
**Status:** Built additively. No canonical engines modified. No commits, no deploy.

## Module dependency graph

```
                    ┌──────────────────────────────┐
                    │ FRED API                     │
                    │ (UNRATE, T10Y3M, T10Y2Y,     │
                    │  USSLIND/CB-LEI)             │
                    └──────────┬───────────────────┘
                               │
                       /api/macro-regime  ◄─ NEW (Phase 2)
                               │
                ┌──────────────┴──────────────┐
                ▼                             ▼
   engine/recession-probability.js     engine/macro-mood.js (canonical, READ-ONLY)
        │ NEW                                 │
        │ - sahmComponent                     │ - 5-factor risk-on/off
        │ - nyFedComponent                    │ - regime: risk-on|risk-off|transition
        │ - leiComponent                      │
        │ - ycComponent                       │
        │ - compute (composite)               │
        │ - recommendedActions                │
        └─────────┬───────────────────────────┘
                  │
                  ▼
   engine/regime-conditional-weights.js  ◄─ NEW
        │ - resolveRegimeKey(5-state, riskMood) → 9-regime taxonomy v2
        │ - getFactorMultipliers(regime)
        │ - getAssetClassTilts(regime)
        │ - applyToFactor(name, baseline, regime)
        │ - compute(input)
        │
        ▼
   pages/macro-regime.html  ◄─ NEW
        + pages/macro-regime-render.js  ◄─ NEW (CSP-clean)

   tests/macro-regime.test.js  ◄─ NEW (52 vitest cases, all passing)
   tests/macro-regime-backtest.js  ◄─ NEW (10 cases, 8/8 true + 2/2 FP)
```

## File inventory

### NEW modules
- `engine/recession-probability.js` (≈ 380 lines, pure-fn)
- `engine/regime-conditional-weights.js` (≈ 260 lines, pure-fn)

### NEW page
- `pages/macro-regime.html` — full dashboard
- `pages/macro-regime-render.js` — CSP-clean renderer

### NEW tests
- `tests/macro-regime.test.js` — 52 vitest cases (all passing)
- `tests/macro-regime-backtest.js` — Node-script backtest, persists JSON to `tests/reports/macro-regime-backtest.json`

### NEW docs
- `docs/methodology-macro-regime.md` — formula + per-component derivation
- `docs/macro-regime-architecture-2026-04-28.md` — this file
- `docs/macro-regime-cockpit-integration-2026-04-28.md` — wire-spec for lane 1

### Phase 2 (TODO, NOT in this lane — flagged for downstream lane)
- `api/macro-regime.js` — Vercel serverless handler that combines FRED fetches + recession-probability + macro-mood + 24mo trajectory.

## Read-only canonical contract

This lane touches NO canonical engine. `engine/macro-mood.js` is consumed via shape (`{ regime: 'risk-on' | 'risk-off' | 'transition' }`) only. `engine/regime.js` is unchanged. `api/fred-data.js` is unchanged (read-only — already has `recession-indicators` action).

## Shadow path

Per scope item B6, the regime taxonomy v2 ships as a SHADOW path. Cockpit Now strip will **only** display the v2 chip when `localStorage.THV2_REGIME_V2 === '1'`. Default behavior stays canonical until explicit opt-in.

## Coordination with other in-flight lanes

|Lane|Subject|Coordination|
|---|---|---|
|11|Inheritance graph|Macro-regime can be a parent node feeding security-level scores. Read-only; no writes to `inheritance-graph.js`.|
|14|Insights catalog|Source of recommendation #3 (this lane delivers).|
|15|Notifications|Regime-change alert (ACTION-tier) — wire-spec drafted in cockpit-integration doc.|
|18|Macro prescience (lead indicators)|Sibling. Lane 18 = forward LEAD; this lane = current STATE. Both consume FRED. No file overlap.|
|26-33|Modal/backtest/track-record/universe/nav/sync/replay/commentary|None of these touch the same files.|

## Test status

```
$ npx vitest run tests/macro-regime.test.js
Test Files: 1 passed (1)
     Tests: 52 passed (52)

$ node tests/macro-regime-backtest.js
True-recession cases: 8/8 hits
False-positive cases: 2/2 acceptable
Composite Brier:      0.31 (n=10)
```

## Honesty disclosures

- All hit rates carry n + 95% Wilson CI (8/8 → CI [0.65, 1.00], not "100%").
- COVID 2020 is acknowledged as a case the model misses (`knownLimitation` field surfaced).
- No silent failures: missing data → `ok: false` + `reason` field.
- Page surfaces caveats from composite output as first-class content.
- Brier 0.31 reported alongside hit-rate; not just the favourable metric.

## Constraints respected

- READ-ONLY canonical engines. ✓
- NEW files only. ✓
- FRED only (FREE). No paid feeds purchased. ✓
- No deploy, no commits. ✓
- In-flight files (lanes 26-33) untouched. ✓
- T0 directives respected: `feedback_scientific_rigor_portfolio_wide.md`, `feedback_silent_failures.md`, `feedback_no_defaults.md`, `feedback_first_principles_audit.md`, `feedback_actionable_first.md`, `feedback_capital_allocation_framework.md`, `feedback_ai_scoring_evolution.md`. ✓
