agent_panel: alerts use the macro persona's verdict; all 4 takes still logged

agent_panel.verdict_source defaults to "macro": the macro take's stance is the
verdict, the synthesizer call is skipped (4 LLM calls per panel instead of 5);
"chair" restores the four-voice consensus + OPPOSE guard. Verdict source is
cached per mode and logged (panel_verdict_source). 406 tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC4s6VCURFte4dub4xKxYL
This commit is contained in:
Harwinder Sekhon
2026-09-03 21:16:45 -04:00
parent d681027b20
commit b8f903ad0e
9 changed files with 175 additions and 26 deletions
+105 -6
View File
@@ -6319,6 +6319,22 @@ def _():
pass
@test("app_config: agent_panel.verdict_source validated")
def _():
assert load_app_config(_write_json(
{"agent_panel": {"verdict_source": "macro"}}
)).agent_panel_settings["verdict_source"] == "macro"
assert load_app_config(_write_json(
{"agent_panel": {"verdict_source": "chair"}}
)).agent_panel_settings["verdict_source"] == "chair"
try:
load_app_config(_write_json(
{"agent_panel": {"verdict_source": "technical"}}))
assert False, "should have raised"
except AppConfigError:
pass
import agent_panel as ap
from signal_aggregator import SignalReport, TradeSetup
@@ -6361,7 +6377,7 @@ _ALL_OK = {ap._MACRO_SYSTEM: _TAKE_OK, ap._TECH_SYSTEM: _TAKE_OK,
ap._SYNTH_SYSTEM: _SYNTH_OK}
_PANEL_ON = {"enabled": True, "telegram": True, "dashboard": False,
"min_quality": "B", "max_panels_per_day": 20,
"reuse_ttl_seconds": 3600}
"reuse_ttl_seconds": 3600, "verdict_source": "chair"}
def _panel_report(quality="A", signal=SignalStrength.BUY):
@@ -6490,7 +6506,8 @@ def _():
for synth in (None, {"verdict": "MAYBE", "confidence": 50},
{"verdict": "AGREE", "confidence": "hot"}):
lc._client = _PanelStub({**_ALL_OK, ap._SYNTH_SYSTEM: synth})
assert ap.run_panel(_panel_report()) is None, f"synth={synth}"
assert ap.run_panel(_panel_report(), verdict_source="chair") is None, \
f"synth={synth}"
finally:
lc._client = None
ap.reset_panel_state()
@@ -6508,8 +6525,9 @@ def _():
stub.replies[ap._MACRO_SYSTEM] = {
**stub.replies[ap._MACRO_SYSTEM], "stance": "oppose"}
lc._client = stub
v = ap.run_panel(_panel_report())
v = ap.run_panel(_panel_report(), verdict_source="chair")
assert v.verdict == "OPPOSE" and v.confidence == 72.4
assert v.source == "chair"
assert v.payload_hash.startswith("sha256:")
assert len(v.rationale) == 240, "rationale truncated"
assert [t.persona for t in v.takes] == \
@@ -6522,6 +6540,57 @@ def _():
ap.reset_panel_state()
@test("agent panel: macro-only mode uses the macro take as the verdict and skips the chair")
def _():
try:
stub = _PanelStub(dict(_ALL_OK))
lc._client = stub
v = ap.run_panel(_panel_report())
assert v is not None
assert ap._SYNTH_SYSTEM not in [s for s, _ in stub.calls], \
"macro-only mode must not spend the synthesizer"
assert len(stub.calls) == 4
assert v.verdict == "AGREE", "macro take's stance is support"
assert v.confidence == _TAKE_OK["confidence"]
assert v.rationale == _TAKE_OK["note"]
assert v.source == "macro"
assert [t.persona for t in v.takes] == \
["macro", "technical", "risk", "contrarian"]
finally:
lc._client = None
ap.reset_panel_state()
try:
stub2 = _PanelStub(dict(_ALL_OK))
stub2.replies[ap._MACRO_SYSTEM] = {**_TAKE_OK, "stance": "oppose"}
lc._client = stub2
v2 = ap.run_panel(_panel_report())
assert v2.verdict == "OPPOSE", \
"macro-only verdict follows macro even when others support"
assert ap._SYNTH_SYSTEM not in [s for s, _ in stub2.calls]
finally:
lc._client = None
ap.reset_panel_state()
@test("agent panel: macro take missing falls back to the chair")
def _():
try:
stub = _PanelStub(dict(_ALL_OK))
stub.replies[ap._MACRO_SYSTEM] = None # _validate_take rejects it
lc._client = stub
v = ap.run_panel(_panel_report())
assert v is not None
assert ap._SYNTH_SYSTEM in [s for s, _ in stub.calls], \
"a missing macro take must fall back to the synthesizer"
assert v.source == "chair"
assert v.verdict == _SYNTH_OK["verdict"]
assert [t.persona for t in v.takes] == ["technical", "risk", "contrarian"]
finally:
lc._client = None
ap.reset_panel_state()
@test("agent_panel: NEUTRAL signal → no panel even forced")
def _():
try:
@@ -6590,6 +6659,32 @@ def _():
ap.reset_panel_state()
@test("agent panel: attach_panel honours verdict_source and caches per source")
def _():
try:
stub = _PanelStub(dict(_ALL_OK))
lc._client = stub
ap._dp = _NullDP()
report = _panel_report()
assert ap.attach_panel(report, {**_PANEL_ON, "verdict_source": "macro"},
context=FundamentalContext()) is True
assert report.panel_verdict_source == "macro"
assert report.panel_cache_status == "MISS"
assert len(stub.calls) == 4
report2 = _panel_report()
assert ap.attach_panel(report2, {**_PANEL_ON, "verdict_source": "chair"},
context=FundamentalContext()) is True
assert report2.panel_verdict_source == "chair"
assert report2.panel_cache_status == "MISS", \
"a chair-mode attach must not reuse a macro-mode cached verdict"
assert len(stub.calls) == 9
finally:
lc._client = None
ap._dp = None
ap.reset_panel_state()
@test("agent_panel: daily budget exhausts and re-arms on UTC roll")
def _():
try:
@@ -6972,7 +7067,8 @@ def _():
lc._client = stub
try:
ap.reset_panel_state_for_tests() if hasattr(ap, "reset_panel_state_for_tests") else None
verdict = ap.run_panel(_panel_report(), FundamentalContext())
verdict = ap.run_panel(_panel_report(), FundamentalContext(),
verdict_source="chair")
assert verdict is not None
assert verdict.verdict == "NEUTRAL", verdict
assert verdict.confidence <= 49.0
@@ -7190,13 +7286,15 @@ def _():
slog._dumps(report.panel_takes),
created_at=report.panel_created_at,
cache_status=report.panel_cache_status,
payload_hash=report.panel_payload_hash)
payload_hash=report.panel_payload_hash,
verdict_source="macro")
lg.close()
conn = sqlite3.connect(path)
row = conn.execute(
"SELECT panel_verdict, panel_confidence, panel_rationale,"
" panel_takes_json, panel_created_at, panel_cache_status,"
" panel_payload_hash, advisory_outcome_status"
" panel_payload_hash, advisory_outcome_status,"
" panel_verdict_source"
" FROM signals WHERE id=?",
(rowid,)).fetchone()
conn.close()
@@ -7206,6 +7304,7 @@ def _():
assert row[4] == report.panel_created_at and row[5] == "MISS"
assert row[6] == report.panel_payload_hash
assert row[7] == "PENDING"
assert row[8] == "macro"
finally:
slog.reset_loggers_for_tests()
os.remove(path)