From 41240c43980ea5903c641553ce0d660b0bd7089d Mon Sep 17 00:00:00 2001 From: Brian McMahon Date: Thu, 28 May 2026 11:41:46 -0700 Subject: [PATCH] =?UTF-8?q?feat(agent=5Fschemas):=20RegimeLiteral=20to=203?= =?UTF-8?q?-class=20(v0.41.0=20=E2=86=92=20v0.42.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caution-regime retirement Phase 1A — lib chokepoint. Plan doc: alpha-engine-docs/private/caution-regime-retirement-260528.md. The 4-class taxonomy (bull/neutral/bear/caution) double-counted the rule-based macro-agent "caution" override: VIX, HY OAS, and SPY 30d return are already weighted into the continuous regime_intensity_z META_FEATURE consumed by the predictor L2 Ridge and the executor's Stage D' Wire 1/2/3/4/5 sites. Discretizing those signals into a 4th regime category was the band-aid; the continuous SOTA path is already running. Portfolio-protective hysteresis (risk_on/caution/risk_off) is a separate institutional pattern emitted by the predictor drawdown leg (alpha-engine-predictor/regime/drawdown.py); consumers compose the two axes via most-protective override at decision time. The arc Phase 2 separates drawdown_tier from market_regime in the type system to prevent the vocabulary collision that today aliases the drawdown leg's "caution" into the macro-regime label. Changes: - RegimeLiteral: Literal["bull","neutral","bear","caution"] → Literal["bull","neutral","bear"] with docstring naming the 3-class Ang-Bekaert anchor + the separate drawdown_tier axis. - Version bump 0.41.0 → 0.42.0 (pyproject + __init__). - New tests pin caution rejection on MacroEconomistRawOutput + MacroCriticOutput.suggested_regime, and accept-all-3-classes smoke for forward-compat. Suite: 901 → 903 passing. Composes with feedback_no_bandaids_go_big_or_home (architectural fix, not config-block band-aid), feedback_sota_institutional_default_no_shortcuts (3-class Ang-Bekaert is the institutional baseline), and feedback_lift_invariants_to_chokepoint_after_second_recurrence (lib RegimeLiteral is the chokepoint for the cross-repo cascade). Sequencing: Phases 1B-1D (research + data + config) consume this v0.42.0 tag via pin-bump; Phases 2A-2D ship together with Phase 1 to avoid drawdown-leg type-mismatch crash on the first SF. Co-Authored-By: Claude Opus 4.7 (1M context) --- pyproject.toml | 2 +- src/alpha_engine_lib/__init__.py | 2 +- src/alpha_engine_lib/agent_schemas.py | 13 +++++++++++-- tests/test_agent_schemas.py | 27 +++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fdb37b2..56bc5ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "alpha-engine-lib" -version = "0.41.0" +version = "0.42.0" description = "Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README." readme = "README.md" # EC2 still runs Python 3.9 on the always-on micro instance (boto3 drops diff --git a/src/alpha_engine_lib/__init__.py b/src/alpha_engine_lib/__init__.py index ec859c2..f2eba5c 100644 --- a/src/alpha_engine_lib/__init__.py +++ b/src/alpha_engine_lib/__init__.py @@ -1,3 +1,3 @@ """alpha-engine-lib — shared utilities for Alpha Engine modules.""" -__version__ = "0.41.0" +__version__ = "0.42.0" diff --git a/src/alpha_engine_lib/agent_schemas.py b/src/alpha_engine_lib/agent_schemas.py index d3ea8b3..23b8271 100644 --- a/src/alpha_engine_lib/agent_schemas.py +++ b/src/alpha_engine_lib/agent_schemas.py @@ -50,10 +50,19 @@ # ── Literals ───────────────────────────────────────────────────────────── -RegimeLiteral = Literal["bull", "neutral", "bear", "caution"] +RegimeLiteral = Literal["bull", "neutral", "bear"] """Macro market regime — output of the macro_economist agent and the macro critic. Drives sector_modifiers downstream and the executor's -graduated drawdown gate.""" +graduated drawdown gate. + +3-class Ang-Bekaert taxonomy. The legacy 4th value ``"caution"`` was +retired in v0.42.0 (plan: caution-regime-retirement-260528.md): the +rule-based caution override at the macro-agent layer double-counted +signals already weighted into the continuous ``regime_intensity_z`` +META_FEATURE. Portfolio-protective drawdown state is now a separate +axis (``drawdown_tier: Literal["risk_on","caution","risk_off"]``) +emitted by the predictor's drawdown leg; consumers compose the two +axes via most-protective override at decision time.""" CIORawDecisionLiteral = Literal["ADVANCE", "REJECT", "NO_ADVANCE_DEADLOCK"] diff --git a/tests/test_agent_schemas.py b/tests/test_agent_schemas.py index 4be4635..fe5a13a 100644 --- a/tests/test_agent_schemas.py +++ b/tests/test_agent_schemas.py @@ -325,6 +325,33 @@ def test_regime_literal_enforced(self): with pytest.raises(ValidationError): MacroEconomistRawOutput(market_regime="exuberant") + def test_regime_literal_is_3class_caution_rejected(self): + # v0.42.0 retired "caution" from the macro market_regime taxonomy + # per caution-regime-retirement-260528.md. The 3-class Ang-Bekaert + # taxonomy (bull/neutral/bear) is the institutional baseline; the + # rule-based caution override at the macro-agent layer was double- + # counted by the continuous regime_intensity_z META_FEATURE. + # Portfolio-protective hysteresis (risk_on/caution/risk_off) is a + # separate axis emitted by the predictor drawdown leg. + from alpha_engine_lib.agent_schemas import ( + MacroCriticOutput, + MacroEconomistRawOutput, + ) + + with pytest.raises(ValidationError): + MacroEconomistRawOutput(market_regime="caution") + with pytest.raises(ValidationError): + MacroCriticOutput( + action="revise", critique="elevated stress", suggested_regime="caution", + ) + + def test_regime_literal_accepts_all_3_classes(self): + from alpha_engine_lib.agent_schemas import MacroEconomistRawOutput + + for regime in ("bull", "neutral", "bear"): + out = MacroEconomistRawOutput(market_regime=regime) + assert out.market_regime == regime + class TestMacroCriticOutput: def test_accept_action(self):