Files
ai-exchange/tests/unit/test_operator_workspace_service.py
T
2026-05-22 11:17:04 +08:00

1780 lines
114 KiB
Python

import os
import unittest
from datetime import datetime, timezone
from unittest.mock import patch
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://user:pass@localhost:5432/ai_ict_test")
from src.services.operator import DecisionAssistantSummaryService, OperatorNoteService, ReviewPacketService, SessionFocusService
from src.services.operator.operator_workspace_service import (
_build_trader_mode_live_readout,
_build_trader_mode_r2_evidence_window,
_resolve_trader_mode_focus_candidate,
)
class OperatorWorkspaceServiceTests(unittest.TestCase):
def test_trader_mode_r2_evidence_window_follows_validation_failed_criteria(self) -> None:
window = _build_trader_mode_r2_evidence_window(
shadow_paper_validation_summary={
"paper_ticket_count": 1,
"paper_execution_count": 1,
"operator_feedback_count": 3,
"weekly_review_entry_count": 1,
"failed_criteria": ["operator_feedback_sample_gate"],
},
decision_support_ready=False,
)
self.assertIn("operator_feedback_count", window["missing_evidence"])
aligned_window = _build_trader_mode_r2_evidence_window(
shadow_paper_validation_summary={
"paper_ticket_count": 1,
"paper_execution_count": 1,
"operator_feedback_count": 3,
"weekly_review_entry_count": 1,
"failed_criteria": ["paper_ticket_sample_gate"],
},
decision_support_ready=False,
)
self.assertNotIn("operator_feedback_count", aligned_window["missing_evidence"])
def test_trader_mode_focus_preserves_doctrine_research_only_state(self) -> None:
focus = _resolve_trader_mode_focus_candidate(
candidate_watchlist_tiers={
"review_now": [],
"strong_watch": [
{
"candidate_id": 4693,
"title": "Candidate #4693",
"tier": "strong_watch",
"session_code": "LONDON",
"model_code": "OSOK_DAILY_RANGE_SWEEP_MSS_FVG_RETRACE",
"missing_confirmations": ["fvg_retrace_confirmed"],
"mainline_gate_blockers": ["missing_doctrine_setup_playbook"],
"doctrine_research_status": {
"status": "research_only",
"research_only": True,
"research_only_reason": "missing_doctrine_setup_playbook",
},
"research_only": True,
"research_only_reason": "missing_doctrine_setup_playbook",
}
],
"soft_watch": [],
},
candidate_watchlist_summary={},
top_opportunities=[],
decision_packet={},
)
readout = _build_trader_mode_live_readout(
status_label="Observe Only",
status_tone="observe",
data_confidence="fresh",
focus=focus,
mainline_focus_gate={"review_now_allowed": False, "blockers": []},
visible_reasons=["missing_doctrine_setup_playbook"],
plain_language_reasons=["missing_doctrine_setup_playbook"],
next_action="wait_for_doctrine_playbook",
freshness_summary={"is_stale": False},
current_session="LONDON",
market_posture="monitor",
readonly_live_health_summary={},
)
self.assertTrue(focus["research_only"])
self.assertEqual(focus["research_only_reason"], "missing_doctrine_setup_playbook")
self.assertIn("Research only", readout["focus_reason"])
self.assertIn("Doctrine Research only", readout["ict_chain_sentence"])
self.assertIn("full Doctrine playbook", readout["wait"])
def test_create_note_returns_payload(self) -> None:
service = OperatorNoteService(repository=_NoteRepo())
payload = service.create_note(
instrument_id=1,
note_scope="session",
author="alice",
body="Watch NY_AM sweep quality.",
session_code="NY_AM",
tags=["handoff"],
)
self.assertEqual(payload["author"], "alice")
self.assertEqual(payload["session_code"], "NY_AM")
def test_session_focus_aggregates_candidates_executions_and_alerts(self) -> None:
service = SessionFocusService(
candidate_repository=_CandidateRepo(),
execution_service=_ExecutionService(),
decision_repository=_DecisionRepo(),
session_repository=_SessionRepo(),
trust_service=_TrustService(),
)
summary = service.build_summary(instrument_id=1, limit=2)
self.assertEqual(summary["items"][0]["candidate_count"], 1)
self.assertEqual(summary["items"][0]["open_execution_count"], 1)
self.assertEqual(summary["items"][0]["alert_count"], 1)
self.assertEqual(summary["items"][0]["action_hint"], "review_alerts")
def test_review_packet_collects_core_sections(self) -> None:
service = ReviewPacketService(
note_service=_NoteService(),
decision_service=_DecisionService(),
execution_service=_ExecutionService(),
trust_service=_TrustService(),
weekly_review_service=_WeeklyReviewService(),
decision_repository=_DecisionRepo(),
alert_quality_summary_service=_AlertQualitySummaryService(),
research_council_service=_ResearchCouncilService(),
optimization_review_service=_OptimizationReviewService(),
decision_assistant_service=_DecisionAssistantService(),
)
packet = service.build_packet(instrument_id=1)
self.assertIn("decision_packet", packet)
self.assertIn("recent_notes", packet)
self.assertIn("weekly_review", packet)
self.assertIn("alert_quality_summary", packet)
self.assertIn("trust_score", packet)
self.assertIn("research_council", packet)
self.assertIn("research_council_optimization_review", packet)
self.assertIn("decision_assistant_summary", packet)
self.assertEqual(packet["research_council"]["id"], 31)
self.assertEqual(packet["research_council_optimization_review"]["health_state"], "healthy")
def test_review_packet_allows_absent_research_council_without_regression(self) -> None:
service = ReviewPacketService(
note_service=_NoteService(),
decision_service=_DecisionService(),
execution_service=_ExecutionService(),
trust_service=_TrustService(),
weekly_review_service=_WeeklyReviewService(),
decision_repository=_DecisionRepo(),
alert_quality_summary_service=_AlertQualitySummaryService(),
research_council_service=_EmptyResearchCouncilService(),
optimization_review_service=_OptimizationReviewService(),
decision_assistant_service=_DecisionAssistantService(),
)
packet = service.build_packet(instrument_id=1)
self.assertIn("decision_packet", packet)
self.assertIn("recent_notes", packet)
self.assertIn("weekly_review", packet)
self.assertIn("trust_score", packet)
self.assertIsNone(packet["research_council"])
def test_decision_assistant_summary_prioritizes_current_setup(self) -> None:
service = DecisionAssistantSummaryService(
launch_readiness_service=_LaunchReadinessService(),
session_focus_service=_SessionFocusSummaryService(),
operator_digest_service=_OperatorDigestSummaryService(),
decision_repository=_PreviousPacketRepo(),
candidate_repository=_CandidateWatchlistRepo(),
research_council_service=_ResearchCouncilSummaryService(),
exchange_status_provider=_ExchangeStatusProvider(),
announcements_provider=_AnnouncementsProvider(),
external_chart_provider=_ExternalChartProvider(),
decision_assistant_repository=_DecisionAssistantRepo(),
market_state_repository=_BacktestQualityGateRepo(passed=True),
)
summary = service.build_summary(
instrument_id=1,
timeframe="5m",
decision_packet={
"status": "actionable",
"action_recommendation": "confirm_ticket",
"candidate_id": 18,
"packet_ts": "2026-05-12T02:00:00+00:00",
"ai_explanation": "London sweep is confirmed and risk stays bounded.",
"narrative": "London draw into discount then MSS reclaim.",
"bias": "bullish",
"setup_type": "SWEEP_MSS_FVG",
"why_allowed": ["candidate_confirmed", "risk_governor_passed"],
"why_blocked": ["trust_soft_block"],
"trust_classification": "observe",
"operator_mode": "decision_support_only",
"research_council": {
"follow_up_checks": ["watch_london_open"],
"recommended_watch_items": ["news_window_review"],
"operator_questions": ["Should London wait for CPI to clear?"],
"next_confirmation_targets": ["wait_for_mss_reclaim"],
},
},
weekly_review={
"next_week_actions": ["review_london_session_playbook"],
"session_performance": {
"LONDON": {
"count": 5,
"framework_execution_rate": 0.8,
"model_outside_rate": 0.1,
"risk_violation_count": 1,
"avg_r_multiple": 1.4,
},
"NY_AM": {
"count": 4,
"framework_execution_rate": 0.5,
"model_outside_rate": 0.3,
"risk_violation_count": 2,
"avg_r_multiple": 0.4,
},
},
"high_quality_session": {"session_code": "LONDON", "framework_execution_rate": 0.8, "avg_r_multiple": 1.4},
"low_quality_session": {"session_code": "NY_AM", "framework_execution_rate": 0.5, "avg_r_multiple": 0.4},
},
recent_alerts=[{"created_ts": "2026-05-12T01:59:00+00:00", "alert_type": "candidate_actionable"}],
recent_notes=[
{
"created_ts": "2026-05-12T01:58:00+00:00",
"author": "assistant_template",
"body": "[top-opportunity] Candidate #18 | prior handoff",
"candidate_id": 18,
"session_code": "LONDON",
"tags": ["assistant-template", "top-opportunity", "outcome:useful", "actionable"],
}
],
recent_executions=[{"updated_ts": "2026-05-12T01:57:00+00:00", "execution_status": "open"}],
)
self.assertEqual(summary["focus_mode"], "review_top_setup")
self.assertIn("Candidate #18", summary["headline"])
self.assertEqual(summary["top_priority_items"][0]["kind"], "focus_candidate")
self.assertEqual(summary["top_opportunities"][0]["kind"], "focus_candidate")
self.assertTrue(any(item["kind"] == "decision_packet" for item in summary["top_priority_items"]))
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"][0]["candidate_id"], 18)
self.assertTrue(summary["candidate_watchlist_tiers"]["review_now"][0]["review_now_allowed"])
self.assertTrue(summary["candidate_watchlist_tiers"]["review_now"][0]["trader_product_promotion_gate"]["allowed_in_trader_cockpit_focus"])
self.assertFalse(summary["candidate_watchlist_tiers"]["review_now"][0]["research_only"])
self.assertEqual(summary["mainline_focus_gate"]["status"], "passed")
self.assertTrue(summary["mainline_focus_gate"]["review_now_allowed"])
self.assertEqual(summary["candidate_watchlist_tiers"]["archive_only"][0]["candidate_id"], 21)
self.assertTrue(summary["candidate_watchlist_tiers"]["archive_only"][0]["research_only"])
self.assertEqual(
summary["candidate_watchlist_tiers"]["archive_only"][0]["research_only_reason"],
"missing_doctrine_setup_playbook",
)
self.assertEqual(summary["candidate_watchlist_tiers"]["archive_only"][1]["candidate_id"], 22)
self.assertEqual(summary["candidate_watchlist_summary"]["trader_product_gate_summary"]["allowed_focus_count"], 1)
self.assertGreaterEqual(summary["candidate_watchlist_summary"]["trader_product_gate_summary"]["study_only_count"], 1)
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"][0]["action_method"], "POST")
self.assertIn("/trading/candidates/18/confirm-check", summary["candidate_watchlist_tiers"]["review_now"][0]["action_path"])
self.assertIn("trader_focus_reason", summary["candidate_watchlist_tiers"]["review_now"][0])
self.assertIn("trader_no_trade_reason", summary["candidate_watchlist_tiers"]["review_now"][0])
self.assertIn("trader_watch_next", summary["candidate_watchlist_tiers"]["review_now"][0])
trader_mode_summary = summary["trader_mode_summary"]
self.assertEqual(trader_mode_summary["mode"], "trader_mode_live_brief")
self.assertEqual(trader_mode_summary["status"], "review_now")
self.assertEqual(trader_mode_summary["status_label"], "Review Now")
self.assertEqual(trader_mode_summary["status_tone"], "review")
self.assertEqual(trader_mode_summary["status_color"], "review")
self.assertEqual(trader_mode_summary["primary_opportunity"]["candidate_id"], 18)
self.assertEqual(trader_mode_summary["primary_question_can_watch_now"], "yes_readonly")
self.assertEqual(trader_mode_summary["primary_question_trade_action"], "not_a_buy_sell_instruction")
self.assertEqual(trader_mode_summary["freshness_summary"]["status"], "fresh")
self.assertFalse(trader_mode_summary["freshness_summary"]["is_stale"])
self.assertIn("stale_seconds", trader_mode_summary["freshness_summary"])
self.assertEqual(trader_mode_summary["r2_evidence_window"]["status"], "collecting_evidence")
self.assertIn("paper_ticket_count", trader_mode_summary["r2_evidence_window"]["missing_evidence"])
self.assertNotIn("operator_feedback_count", trader_mode_summary["r2_evidence_window"]["missing_evidence"])
self.assertLessEqual(len(trader_mode_summary["key_reasons"]), 3)
self.assertIn("focus_quality_explanation", trader_mode_summary)
self.assertIn("plain_language_reasons", trader_mode_summary)
self.assertEqual(trader_mode_summary["trader_product_gate_summary"]["allowed_focus_count"], 1)
self.assertLessEqual(len(trader_mode_summary["plain_language_reasons"]), 3)
self.assertEqual(trader_mode_summary["trader_readout"]["mode"], "trader_one_screen_readout")
self.assertIn("now", trader_mode_summary["trader_readout"])
self.assertIn("reason", trader_mode_summary["trader_readout"])
self.assertIn("wait", trader_mode_summary["trader_readout"])
self.assertIn("risk", trader_mode_summary["trader_readout"])
self.assertIn("historical_edge", trader_mode_summary["trader_readout"])
self.assertIn("boundary", trader_mode_summary["trader_readout"])
self.assertIn("manual check only", trader_mode_summary["trader_readout"]["now"])
self.assertIn("Review the focus manually", trader_mode_summary["trader_readout"]["wait"])
self.assertIn("trade signal", trader_mode_summary["trader_readout"]["risk"])
self.assertIn("Read-only", trader_mode_summary["trader_readout"]["boundary"])
self.assertIn("No trade", trader_mode_summary["trader_readout"]["why_no_trade"])
self.assertIn("Watch", trader_mode_summary["trader_readout"]["watch_next"])
self.assertIn("Current chain:", trader_mode_summary["trader_readout"]["focus_reason"])
self.assertIn("ict_chain_sentence", trader_mode_summary["trader_readout"])
self.assertIn("backtest", trader_mode_summary["trader_readout"]["ict_chain_steps"])
self.assertIn("market", trader_mode_summary["trader_readout"]["data_state"])
self.assertEqual(
[item["label"] for item in trader_mode_summary["trader_readout"]["one_screen_cards"]],
["Now", "Reason", "Wait", "Risk", "Historical Edge", "Boundary"],
)
self.assertTrue(
all(len(str(item["value"])) <= 140 for item in trader_mode_summary["trader_readout"]["one_screen_cards"])
)
self.assertEqual(trader_mode_summary["ict_rulebook_mapping"]["surface"], "trader_cockpit")
self.assertIn("backtest_evidence", trader_mode_summary["ict_rulebook_mapping"]["required_concepts"])
self.assertIn("no_trade_conditions", trader_mode_summary["ict_rulebook_mapping"]["present_concepts"])
self.assertIn("recovery_actions", trader_mode_summary)
self.assertIn("keyboard_shortcuts", trader_mode_summary)
self.assertIn({"key": "Space", "action": "pause_or_resume_auto_refresh"}, trader_mode_summary["keyboard_shortcuts"])
self.assertEqual(trader_mode_summary["profile_template"]["default_timeframe"], "1m")
self.assertEqual(trader_mode_summary["profile_template"]["default_queue_instruments"][0]["symbol"], "BTC")
self.assertEqual(trader_mode_summary["multi_instrument_queue_template"]["symbols"], ["BTC", "ETH", "SOL"])
self.assertEqual(trader_mode_summary["chart_context_bridge"]["exact_candle_source"], "internal_candles")
self.assertEqual(trader_mode_summary["chart_context_bridge"]["external_chart_bridge_required"], False)
self.assertEqual(trader_mode_summary["post_market_review_draft"]["mode"], "auto_post_market_review_draft")
self.assertTrue(trader_mode_summary["post_market_review_draft"]["draft_sections"])
self.assertEqual(
trader_mode_summary["post_market_review_draft"]["review_type_counts"]["observation_review"],
2,
)
self.assertIn("feedback_loop_summary", trader_mode_summary["post_market_review_draft"])
self.assertEqual(
trader_mode_summary["post_market_review_draft"]["study_note_draft"]["status"],
"draft_ready",
)
self.assertIn("usable_today", trader_mode_summary["ops_duty_panel"])
self.assertEqual(trader_mode_summary["ops_duty_panel"]["trader_safe_health"]["label"], "Use Today")
self.assertTrue(trader_mode_summary["ops_duty_panel"]["trader_safe_health"]["can_use_for_readonly_analysis"])
self.assertTrue(trader_mode_summary["ops_duty_panel"]["system_ops_readonly_checks"])
self.assertTrue(trader_mode_summary["must_not_trade"])
self.assertFalse(trader_mode_summary["execution_allowed"])
self.assertFalse(trader_mode_summary["real_live_ready"])
self.assertIn("Read-only Live Analysis", trader_mode_summary["drilldown_sections"])
self.assertEqual(summary["evidence_summary"]["trader_mode_status"], "review_now")
self.assertEqual(summary["evidence_summary"]["trader_mode_color"], "review")
self.assertEqual(summary["evidence_summary"]["mainline_focus_gate_status"], "passed")
self.assertTrue(summary["evidence_summary"]["review_now_allowed_by_mainline"])
self.assertIn("focus_matrix", summary["candidate_watchlist_summary"])
self.assertIn("next_watchlist_focus", summary["candidate_watchlist_summary"])
self.assertIn("next_watchlist_focus_prompt", summary["candidate_watchlist_summary"])
self.assertEqual(summary["workflow_digest"][0]["phase"], "pre_market")
self.assertEqual(summary["workflow_digest"][1]["phase"], "intraday")
self.assertEqual(summary["workflow_digest"][2]["phase"], "post_trade")
self.assertIn("workflow_rhythm_summary", summary)
self.assertEqual(summary["workflow_rhythm_summary"]["phase_count"], 3)
self.assertIn("pending_count", summary["workflow_rhythm_summary"])
self.assertIn("next_action", summary["workflow_rhythm_summary"])
self.assertIn("workflow_session_completion_summary", summary)
self.assertEqual(summary["workflow_session_completion_summary"]["task_count"], 3)
self.assertIn("next_task_label", summary["workflow_session_completion_summary"])
self.assertIn("completion_ratio", summary["workflow_session_completion_summary"])
self.assertIn("workflow_artifacts", summary)
self.assertIn("workflow_artifact_summary", summary)
self.assertTrue(summary["workflow_artifacts"])
self.assertIn("next_artifact_kind", summary["workflow_artifact_summary"])
self.assertIn("workflow_history_archive", summary["workflow_artifact_summary"])
self.assertIn("workflow_history_archive_count", summary["workflow_artifact_summary"])
self.assertIn("workflow_archive_compare", summary["workflow_artifact_summary"])
self.assertIn("workflow_archive_operations_summary", summary["workflow_artifact_summary"])
self.assertIn("workflow_archive_multi_day_drift_review", summary["workflow_artifact_summary"])
self.assertIn("workflow_archive_bridge_summary", summary["workflow_artifact_summary"])
self.assertIn("workflow_archive_bridge_persistence_summary", summary["workflow_artifact_summary"])
self.assertIn("workflow_persistence_status", summary["workflow_artifact_summary"])
self.assertEqual(summary["workflow_artifact_summary"]["workflow_archive_operations_summary"]["status"], "active")
self.assertEqual(
summary["workflow_artifact_summary"]["workflow_archive_multi_day_drift_review"]["status"],
"insufficient_history",
)
self.assertEqual(summary["workflow_artifact_summary"]["workflow_archive_bridge_summary"]["status"], "active")
self.assertEqual(summary["workflow_artifact_summary"]["workflow_archive_bridge_persistence_summary"]["status"], "persisted")
self.assertEqual(
summary["workflow_artifact_summary"]["workflow_archive_bridge_persistence_summary"]["compare_writeback_summary"]["note_title"],
"Saved compare note",
)
self.assertIn("long_range_trend_summary", summary["workflow_artifact_summary"]["workflow_history_archive"])
self.assertIn("baseline_pin_summary", summary["workflow_artifact_summary"]["workflow_history_archive"])
self.assertIn("ranked_aggregation_summary", summary["workflow_artifact_summary"]["workflow_history_archive"])
self.assertEqual(summary["workflow_artifact_summary"]["workflow_history_archive"]["long_range_trend_summary"]["status"], "active")
self.assertEqual(summary["workflow_artifact_summary"]["workflow_history_archive"]["baseline_pin_summary"]["status"], "insufficient_history")
self.assertEqual(summary["workflow_artifact_summary"]["workflow_history_archive"]["baseline_pin_summary"]["persisted_baseline_snapshot_id"], 10)
self.assertIn("baseline_delta_story", summary["workflow_artifact_summary"]["workflow_history_archive"]["baseline_pin_summary"])
self.assertEqual(summary["workflow_artifact_summary"]["workflow_history_archive"]["ranked_aggregation_summary"]["status"], "active")
self.assertIn("daily_groups", summary["workflow_artifact_summary"]["workflow_history_archive"])
self.assertIn("session_groups", summary["workflow_artifact_summary"]["workflow_history_archive"])
self.assertIn("bridge_summary", summary["workflow_artifact_summary"]["workflow_history_archive"])
self.assertIn("assistant_loop_queue", summary)
self.assertIn("assistant_loop_summary", summary)
self.assertTrue(summary["assistant_loop_queue"])
self.assertEqual(summary["assistant_loop_summary"]["status"], "active")
self.assertTrue(summary["assistant_loop_summary"]["next_target_section"])
self.assertTrue(any(item["queue_id"] == "replay_learning" for item in summary["assistant_loop_queue"]))
self.assertIn("focus_workspace_context", summary)
self.assertEqual(summary["focus_workspace_context"]["status"], "active")
self.assertTrue(summary["focus_workspace_context"]["workspace_sections"])
self.assertIn("focus_persistence_status", summary["focus_workspace_context"])
self.assertEqual(summary["focus_workspace_context"]["focus_persistence_status"], "persisted")
self.assertIn("continuous_workspace_rail", summary)
self.assertIn("continuous_workspace_status", summary)
self.assertIn("continuous_workspace_summary", summary)
self.assertIn("workspace_continuity_handoff", summary)
self.assertTrue(summary["continuous_workspace_rail"])
self.assertIn("rail_index", summary["continuous_workspace_rail"][0])
self.assertIn("attention_level", summary["continuous_workspace_rail"][0])
self.assertIn("handoff_prompt", summary["continuous_workspace_rail"][0])
self.assertEqual(summary["continuous_workspace_summary"]["rail_count"], len(summary["continuous_workspace_rail"]))
self.assertTrue(summary["continuous_workspace_summary"]["next_target_section"])
self.assertIn("daily_rhythm_slot", summary["workflow_digest"][0])
self.assertIn("rhythm_checklist", summary["workflow_digest"][0])
self.assertIn("rhythm_status", summary["workflow_digest"][0])
self.assertIn("next_rhythm_action", summary["workflow_digest"][0])
self.assertIn("session_task_label", summary["workflow_digest"][0])
self.assertIn("session_task_status", summary["workflow_digest"][0])
self.assertIn("session_task_prompt", summary["workflow_digest"][0])
self.assertEqual(summary["decision_timeline"][0]["title"], "Current Packet")
self.assertEqual(summary["evidence_chain"][0]["title"], "Decision Gate")
self.assertEqual(summary["evidence_chain"][1]["title"], "Launch Mode")
self.assertTrue(any(item["title"] == "Focus Candidate" for item in summary["evidence_chain"]))
focus_evidence = next(item for item in summary["evidence_chain"] if item["title"] == "Focus Candidate")
self.assertTrue(focus_evidence["focus_related"])
self.assertEqual(focus_evidence["focus_candidate_id"], 18)
self.assertEqual(summary["explanation_drilldown"][0]["title"], "Decision Logic")
self.assertEqual(summary["reason_citations"][0]["source_path"], "decision_packet.why_allowed[0]")
self.assertEqual(summary["learning_rollup"]["status"], "active")
self.assertEqual(summary["external_context_summary"]["status"], "active")
self.assertEqual(summary["review_entrypoints"]["status"], "active")
self.assertEqual(summary["review_entrypoints"]["focus_candidate_id"], 18)
self.assertGreaterEqual(summary["review_entrypoints"]["focus_note_count"], 1)
self.assertIn("Candidate #18", summary["review_entrypoints"]["focus_note_prefill"])
self.assertIn("focus-candidate", summary["review_entrypoints"]["focus_note_tags"])
self.assertIn("focus_note_prompt", summary["review_entrypoints"])
self.assertEqual(summary["review_entrypoints"]["actions"][0]["action_id"], "open_operator_notes")
self.assertTrue(summary["review_entrypoints"]["note_templates"])
self.assertIn("review_entry_action_queue", summary["review_entrypoints"])
self.assertIn("review_entry_handoff_summary", summary["review_entrypoints"])
self.assertTrue(summary["review_entrypoints"]["review_entry_action_queue"])
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["note_scope"], "workbench")
self.assertTrue(summary["review_entrypoints"]["note_templates"][0]["tags"])
self.assertEqual(summary["review_entrypoints"]["recommended_template_id"], "top_opportunity")
self.assertEqual(summary["review_entrypoints"]["template_summary"]["recommended_label"], "Top setup note")
self.assertEqual(summary["review_entrypoints"]["template_summary"]["recommended_reason"], "best_current_opportunity")
self.assertTrue(summary["review_entrypoints"]["template_summary"]["recommended_reuse_hint"])
self.assertEqual(summary["review_entrypoints"]["template_reuse_metrics"]["reuse_status"], "recommended_reused")
self.assertEqual(summary["review_entrypoints"]["template_reuse_metrics"]["recommended_reuse_count"], 1)
self.assertEqual(summary["review_entrypoints"]["template_reuse_metrics"]["recommended_outcome_status"], "useful_signal")
self.assertGreater(summary["review_entrypoints"]["template_reuse_metrics"]["recommended_usefulness_score"], 0)
self.assertEqual(summary["review_entrypoints"]["template_reuse_metrics"]["template_outcome_summary"]["best_template_id"], "top_opportunity")
self.assertIn("feedback_capture_needed_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_outcome_summary"])
self.assertIn("feedback_loop_summary", summary["review_entrypoints"]["template_reuse_metrics"])
self.assertIn("next_feedback_template_id", summary["review_entrypoints"]["template_reuse_metrics"]["feedback_loop_summary"])
self.assertIn("template_feedback_aggregation", summary["review_entrypoints"]["template_reuse_metrics"])
self.assertIn("feedback_tuned_template_id", summary["review_entrypoints"]["template_reuse_metrics"])
self.assertIn("recommendation_tuning_status", summary["review_entrypoints"]["template_reuse_metrics"])
self.assertGreaterEqual(summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"]["useful_feedback_count"], 1)
self.assertIn("feedback_trend_status", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("negative_feedback_guardrail_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("negative_feedback_guardrail_template_ids", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_resolution_queue", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_resolution_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("preferred_template_decay_status", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("preferred_template_decay_template_ids", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("preference_recovery_queue", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("preference_recovery_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("next_preference_recovery_template_id", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_trail", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_trail_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("preference_recovery_recommendations", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("preference_recovery_recommendation_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("next_preference_recovery_action", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_filter_counts", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_filtered_trail", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("guardrail_audit_filtered_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("template_audit_lane_groups", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("recovery_action_shortcuts", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("recovery_action_shortcut_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("next_recovery_action_shortcut", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("template_audit_lane_recommendations", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("template_audit_lane_recommendation_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("shortcut_outcome_telemetry", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("shortcut_outcome_telemetry_count", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("shortcut_outcome_status_counts", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertIn("next_shortcut_outcome_prompt", summary["review_entrypoints"]["template_reuse_metrics"]["template_feedback_aggregation"])
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["reuse_count"], 1)
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["reuse_status"], "saved_reuse")
self.assertEqual(summary["review_entrypoints"]["note_templates"][0]["outcome_status"], "useful_signal")
self.assertTrue(summary["review_entrypoints"]["note_templates"][0]["outcome_evidence"])
self.assertTrue(summary["review_entrypoints"]["note_templates"][0]["feedback_actions"])
self.assertIn("feedback_loop_status", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("feedback_trend_history", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("negative_feedback_guardrail", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("negative_feedback_guardrail_prompt", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_resolution_status", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_resolution_prompt", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("preferred_template_decay", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("preferred_template_decay_reason", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("preference_recovery_status", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("preference_recovery_prompt", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_trail", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_trail_count", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("preference_recovery_recommendation", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("recovery_action_shortcuts", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("audit_lane_group", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("audit_lane_rank", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("audit_lane_recommendation", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("shortcut_outcome_telemetry", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("shortcut_outcome_telemetry_count", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filter", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filter_reason", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filtered_trail", summary["review_entrypoints"]["note_templates"][0])
self.assertIn("guardrail_audit_filtered_count", summary["review_entrypoints"]["note_templates"][0])
self.assertTrue(any(item["template_id"] == "next_review" for item in summary["review_entrypoints"]["note_templates"]))
self.assertTrue(any(item["template_id"] == "queue_follow_up" for item in summary["review_entrypoints"]["note_templates"]))
self.assertTrue(any(item["reuse_hint"] for item in summary["review_entrypoints"]["note_templates"]))
self.assertEqual(summary["digest_surface"]["status"], "active")
self.assertIn("history_summary", summary["digest_surface"])
self.assertIn("recommended_kind", summary["digest_surface"]["history_summary"])
self.assertIn("stale_kind_count", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_needed_count", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_evidence_anchor_count", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_confirmation_count", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_confirmation_needed_count", summary["digest_surface"]["history_summary"])
self.assertIn("freshness_proof_count", summary["digest_surface"]["history_summary"])
self.assertIn("freshness_proof_gap_count", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_confirmation_trail_count", summary["digest_surface"]["history_summary"])
self.assertIn("refresh_confirmation_trail", summary["digest_surface"]["history_summary"])
self.assertIn("proof_aging_trend_counts", summary["digest_surface"]["history_summary"])
self.assertIn("stale_proof_escalation_count", summary["digest_surface"]["history_summary"])
self.assertIn("next_stale_proof_kind", summary["digest_surface"]["history_summary"])
self.assertIn("next_stale_proof_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("proof_follow_up_queue_count", summary["digest_surface"]["history_summary"])
self.assertIn("proof_follow_up_queue", summary["digest_surface"]["history_summary"])
self.assertIn("resolved_proof_count", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_state_counts", summary["digest_surface"]["history_summary"])
self.assertIn("next_proof_follow_up_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("stale_proof_follow_up_count", summary["digest_surface"]["history_summary"])
self.assertIn("proof_follow_up_age_counts", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_history", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_history_count", summary["digest_surface"]["history_summary"])
self.assertIn("oldest_proof_follow_up_kind", summary["digest_surface"]["history_summary"])
self.assertIn("oldest_proof_follow_up_age_seconds", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_sla_status_counts", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_sla_breach_count", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_sla_queue", summary["digest_surface"]["history_summary"])
self.assertIn("proof_resolution_sla_queue_count", summary["digest_surface"]["history_summary"])
self.assertIn("repeated_stale_proof_suppression_count", summary["digest_surface"]["history_summary"])
self.assertIn("next_proof_resolution_sla_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("proof_sla_backlog_routes", summary["digest_surface"]["history_summary"])
self.assertIn("proof_sla_backlog_queue", summary["digest_surface"]["history_summary"])
self.assertIn("proof_sla_backlog_count", summary["digest_surface"]["history_summary"])
self.assertIn("freshness_debt_summary", summary["digest_surface"]["history_summary"])
self.assertIn("next_freshness_debt_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("freshness_debt_trend_counts", summary["digest_surface"]["history_summary"])
self.assertIn("sla_recovery_recommendations", summary["digest_surface"]["history_summary"])
self.assertIn("sla_recovery_recommendation_count", summary["digest_surface"]["history_summary"])
self.assertIn("next_sla_recovery_recommendation", summary["digest_surface"]["history_summary"])
self.assertIn("next_sla_recovery_prompt", summary["digest_surface"]["history_summary"])
self.assertIn("high_value_reminders", summary["digest_surface"])
self.assertIn("high_value_reminder_count", summary["digest_surface"])
self.assertIn("important_change_filter", summary["digest_surface"])
self.assertIn("importance_templates", summary["digest_surface"])
self.assertIn("digest_preference_profile", summary["digest_surface"])
self.assertIn("default_mode", summary["digest_surface"]["digest_preference_profile"])
self.assertIn("available_modes", summary["digest_surface"]["digest_preference_profile"])
self.assertIn("available_profiles", summary["digest_surface"]["digest_preference_profile"])
self.assertIn("importance_templates", summary["digest_surface"]["digest_preference_profile"])
self.assertIn("profile_reason", summary["digest_surface"]["digest_preference_profile"])
self.assertIn("archive_baselines", summary["digest_surface"]["digest_preference_profile"])
self.assertEqual(summary["digest_surface"]["digest_preference_profile"]["archive_baselines"]["digest_snapshot_id"], 9)
self.assertIn("digest_history_archive", summary["digest_surface"])
self.assertIn("digest_history_archive_count", summary["digest_surface"])
self.assertIn("digest_archive_compare", summary["digest_surface"])
self.assertIn("archive_operator_overview", summary)
self.assertEqual(summary["archive_operator_overview"]["status"], "active")
self.assertIn("focus_related_reminders", summary["digest_surface"])
self.assertGreaterEqual(summary["digest_surface"]["focus_related_reminder_count"], 1)
self.assertIsNotNone(summary["digest_surface"]["next_focus_related_reminder"])
self.assertIn("digest_persistence_status", summary["digest_surface"])
self.assertIn("next_digest_history_archive_review", summary["digest_surface"])
self.assertIn("long_range_trend_summary", summary["digest_surface"]["digest_history_archive"])
self.assertIn("baseline_pin_summary", summary["digest_surface"]["digest_history_archive"])
self.assertIn("ranked_aggregation_summary", summary["digest_surface"]["digest_history_archive"])
self.assertEqual(summary["digest_surface"]["digest_history_archive"]["long_range_trend_summary"]["status"], "active")
self.assertEqual(summary["digest_surface"]["digest_history_archive"]["baseline_pin_summary"]["status"], "insufficient_history")
self.assertEqual(summary["digest_surface"]["digest_history_archive"]["baseline_pin_summary"]["persisted_baseline_snapshot_id"], 9)
self.assertIn("baseline_delta_story", summary["digest_surface"]["digest_history_archive"]["baseline_pin_summary"])
self.assertEqual(summary["digest_surface"]["digest_history_archive"]["ranked_aggregation_summary"]["status"], "active")
self.assertIn("digest_baseline_delta_story", summary["archive_operator_overview"])
self.assertIn("workflow_baseline_delta_story", summary["archive_operator_overview"])
self.assertIn("next_high_value_change", summary["digest_surface"])
self.assertIn("digest_review_back_actions", summary["digest_surface"])
self.assertIn("digest_review_back_action_count", summary["digest_surface"])
self.assertIn("next_digest_review_back_action", summary["digest_surface"])
self.assertIn("history_browser", summary["digest_surface"])
self.assertIn("history_browser_status", summary["digest_surface"])
self.assertIn("digest_history_browser", summary)
self.assertEqual(summary["digest_history_browser"]["status"], "active")
self.assertTrue(summary["digest_history_browser"]["filter_options"])
self.assertTrue(summary["digest_history_browser"]["review_sequence"])
self.assertIn("digest_action_queue", summary["digest_surface"])
self.assertIn("digest_action_queue_count", summary["digest_surface"])
self.assertIn("next_digest_action", summary["digest_surface"])
self.assertTrue(summary["digest_surface"]["digest_action_queue"])
self.assertIn("refresh_prompts", summary["digest_surface"]["history_summary"])
self.assertIn("digest_history_groups", summary)
self.assertIn("group_status", summary["digest_history_groups"][0])
self.assertIn("headline", summary["digest_history_groups"][0])
self.assertIn("action_label", summary["digest_history_groups"][0])
self.assertIn("age_band", summary["digest_history_groups"][0])
self.assertIn("staleness_hint", summary["digest_history_groups"][0])
self.assertIn("refresh_prompt", summary["digest_history_groups"][0])
self.assertIn("refresh_action_label", summary["digest_history_groups"][0])
self.assertIn("refresh_evidence_anchor", summary["digest_history_groups"][0])
self.assertIn("refresh_evidence_path", summary["digest_history_groups"][0])
self.assertIn("refresh_confirmation_status", summary["digest_history_groups"][0])
self.assertIn("refresh_confirmation_prompt", summary["digest_history_groups"][0])
self.assertIn("freshness_proof_status", summary["digest_history_groups"][0])
self.assertIn("freshness_proof_detail", summary["digest_history_groups"][0])
self.assertIn("proof_age_trend", summary["digest_history_groups"][0])
self.assertIn("proof_escalation_level", summary["digest_history_groups"][0])
self.assertIn("proof_escalation_prompt", summary["digest_history_groups"][0])
self.assertIn("proof_follow_up_state", summary["digest_history_groups"][0])
self.assertIn("proof_follow_up_action_label", summary["digest_history_groups"][0])
self.assertIn("proof_resolved_state", summary["digest_history_groups"][0])
self.assertIn("proof_follow_up_age_seconds", summary["digest_history_groups"][0])
self.assertIn("proof_follow_up_age_band", summary["digest_history_groups"][0])
self.assertIn("proof_follow_up_stale_queue", summary["digest_history_groups"][0])
self.assertIn("proof_resolution_history", summary["digest_history_groups"][0])
self.assertIn("proof_resolution_sla_seconds", summary["digest_history_groups"][0])
self.assertIn("proof_resolution_sla_status", summary["digest_history_groups"][0])
self.assertIn("proof_resolution_sla_prompt", summary["digest_history_groups"][0])
self.assertIn("proof_sla_backlog_route", summary["digest_history_groups"][0])
self.assertIn("proof_sla_backlog_priority", summary["digest_history_groups"][0])
self.assertIn("freshness_debt_score", summary["digest_history_groups"][0])
self.assertIn("freshness_debt_trend", summary["digest_history_groups"][0])
self.assertIn("sla_recovery_recommendation", summary["digest_history_groups"][0])
self.assertIn("repeated_stale_proof_suppression", summary["digest_history_groups"][0])
self.assertIn("refresh_confirmation_trail", summary["digest_history_groups"][0])
self.assertIn("latest_confirmation_trail_item", summary["digest_history_groups"][0])
self.assertIn("browser_rank", summary["digest_history_groups"][0])
self.assertIn("filter_label", summary["digest_history_groups"][0])
self.assertIn("browser_prompt", summary["digest_history_groups"][0])
self.assertIn("review_back_priority", summary["digest_history_groups"][0])
self.assertTrue(summary["digest_history_preview"])
self.assertEqual(summary["digest_history_preview"][0]["kind"], "reminder")
self.assertEqual(summary["personalized_reminders"][0]["severity"], "high")
self.assertTrue(any(item.get("focus_related") for item in summary["personalized_reminders"]))
self.assertIn("importance_score", summary["personalized_reminders"][0])
self.assertIn("importance_template", summary["personalized_reminders"][0])
self.assertIn("high_value", summary["personalized_reminders"][0])
self.assertEqual(summary["setup_learning_rollup"]["status"], "active")
self.assertEqual(summary["setup_learning_rollup"]["top_lessons"][0]["lesson_type"], "journal_pattern")
self.assertEqual(summary["setup_learning_rollup"]["session_code"], "LONDON")
self.assertEqual(summary["setup_learning_rollup"]["session_code_source"], "focus_candidate")
self.assertEqual(summary["setup_learning_rollup"]["focus_candidate_id"], 18)
self.assertEqual(summary["setup_learning_rollup"]["focus_replay_scope"], "candidate_scoped")
self.assertIn("focus_replay_prompt", summary["setup_learning_rollup"])
self.assertIn("replay_statistics", summary["setup_learning_rollup"])
self.assertIn("learning_attribution_summary", summary["setup_learning_rollup"])
self.assertIn("failure_reason_attribution", summary["setup_learning_rollup"])
self.assertIn("missed_opportunity_attribution", summary["setup_learning_rollup"])
self.assertIn("setup_session_market_state_stats", summary["setup_learning_rollup"])
self.assertIn("weekly_trend_breakdown", summary["setup_learning_rollup"])
self.assertIn("weekly_trend_summary", summary["setup_learning_rollup"])
self.assertIn("learning_focus_queue", summary["setup_learning_rollup"])
self.assertIn("learning_focus_queue_count", summary["setup_learning_rollup"])
self.assertIn("next_learning_focus", summary["setup_learning_rollup"])
self.assertIn("next_learning_focus_prompt", summary["setup_learning_rollup"])
self.assertIn("learning_focus_matrix", summary["setup_learning_rollup"])
self.assertTrue(summary["setup_learning_rollup"]["learning_focus_queue"])
self.assertGreaterEqual(summary["setup_learning_rollup"]["learning_attribution_summary"]["failure_reason_count"], 1)
self.assertIn("sample_count", summary["setup_learning_rollup"]["replay_statistics"])
self.assertIn("comparative_summary", summary["setup_learning_rollup"]["replay_statistics"])
self.assertIn("broader_baseline_summary", summary["setup_learning_rollup"]["replay_statistics"])
self.assertIn("notes", summary["setup_learning_rollup"]["replay_statistics"]["comparative_summary"])
self.assertIn("story", summary["setup_learning_rollup"]["replay_statistics"]["comparative_summary"])
self.assertIn("story", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("framework_percentile", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("composite_percentile", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("composite_band", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("composite_rank", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("risk_rank", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("risk_percentile", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("replay_verdict", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertIn("peer_summary", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertEqual(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family"], "LIQUIDITY_REVERSAL_FAMILY")
self.assertIn("setup_family_peer_summary", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_peer_sessions"])
self.assertEqual(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"]["status"], "active")
self.assertIn("trend_story", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("drift_alert", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("drift_severity", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("focus_recommendation", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"]["focus_reasons"])
self.assertIn("watchlist_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("watchlist_priority", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("watchlist_priority_score", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_ready", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_readiness_hint", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_blockers", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_queue_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_queue_rank", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_next_action", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_queue_item", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_decision_history", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_decision_history_count", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("review_lane_age_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("review_lane_age_score", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("review_lane_aging_hint", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("review_lane_sla_seconds", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("review_lane_sla_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_priority", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_rank", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_reason", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_item", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_queue", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_queue_count", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_trend_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_trend_handoff", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_aging_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_aging_prompt", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_evidence_aging_queue", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_comparison_status", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("promotion_backlog_comparison_summary", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("demotion_history", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("demotion_history_count", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("sample_threshold", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertIn("sample_gap", summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["setup_family_trend_delta"]["drift_watchlist_items"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["top_sessions"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["top_safe_sessions"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["top_composite_sessions"])
self.assertTrue(summary["setup_learning_rollup"]["replay_statistics"]["broader_baseline_summary"]["peer_sessions"])
self.assertTrue(summary["scan_summary_cards"])
self.assertEqual(summary["scan_summary_cards"][0]["action_label"], "Open Setup")
self.assertEqual(summary["scan_summary_cards"][0]["emphasis"], "primary")
self.assertTrue(summary["scan_summary_cards"][0]["context"])
self.assertTrue(summary["scan_summary_cards"][0]["transition_hint"])
self.assertIn("completion_state", summary["scan_summary_cards"][0])
self.assertIn("completion_hint", summary["scan_summary_cards"][0])
self.assertIn("scan_completion_summary", summary)
self.assertEqual(summary["scan_completion_summary"]["status"], "active")
self.assertTrue(summary["scan_completion_summary"]["next_target_section"])
self.assertIn("handoff_marker", summary["scan_summary_cards"][0])
self.assertIn("handoff_status", summary["scan_summary_cards"][0])
self.assertIn("handoff_stale_after_seconds", summary["scan_summary_cards"][0])
self.assertIn("handoff_stale_warning", summary["scan_summary_cards"][0])
self.assertIn("scan_handoff_summary", summary)
self.assertEqual(summary["scan_handoff_summary"]["status"], "active")
self.assertTrue(summary["scan_handoff_summary"]["next_marker"])
self.assertIn("default_stale_after_seconds", summary["scan_handoff_summary"])
self.assertIn("stale_warning_count", summary["scan_handoff_summary"])
self.assertIn("handoff_stale_policy", summary["scan_handoff_summary"])
self.assertIn("cleanup_candidate_count", summary["scan_handoff_summary"])
self.assertIn("overdue_cleanup_count", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_marker", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_prompt", summary["scan_handoff_summary"])
self.assertIn("handoff_lane_groups", summary["scan_handoff_summary"])
self.assertIn("handoff_lifecycle_summary", summary["scan_handoff_summary"])
self.assertIn("operator_resume_queue", summary["scan_handoff_summary"])
self.assertIn("resume_queue_count", summary["scan_handoff_summary"])
self.assertIn("next_resume_marker", summary["scan_handoff_summary"])
self.assertIn("next_resume_prompt", summary["scan_handoff_summary"])
self.assertIn("resume_queue_persistence_status", summary["scan_handoff_summary"])
self.assertIn("resume_queue_persistence_key", summary["scan_handoff_summary"])
self.assertIn("resolved_lane_state_counts", summary["scan_handoff_summary"])
self.assertIn("resolved_lane_count", summary["scan_handoff_summary"])
self.assertIn("unresolved_lane_count", summary["scan_handoff_summary"])
self.assertIn("next_resolved_lane_prompt", summary["scan_handoff_summary"])
self.assertIn("resume_history", summary["scan_handoff_summary"])
self.assertIn("resume_history_count", summary["scan_handoff_summary"])
self.assertIn("resume_history_latest_event", summary["scan_handoff_summary"])
self.assertIn("stale_persistence_cleanup_queue", summary["scan_handoff_summary"])
self.assertIn("stale_persistence_cleanup_count", summary["scan_handoff_summary"])
self.assertIn("next_stale_persistence_cleanup_key", summary["scan_handoff_summary"])
self.assertIn("next_stale_persistence_cleanup_prompt", summary["scan_handoff_summary"])
self.assertIn("resume_backlog_groups", summary["scan_handoff_summary"])
self.assertIn("cleanup_action_ready_count", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_action_label", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_action_prompt", summary["scan_handoff_summary"])
self.assertIn("cleanup_execution_log", summary["scan_handoff_summary"])
self.assertIn("cleanup_execution_log_count", summary["scan_handoff_summary"])
self.assertIn("grouped_resume_handoff_summary", summary["scan_handoff_summary"])
self.assertIn("next_cleanup_execution_prompt", summary["scan_handoff_summary"])
self.assertIn("handoff_cleanup_recommendation", summary["scan_handoff_summary"])
self.assertTrue(summary["scan_handoff_summary"]["snooze_options"])
self.assertIn("snooze_seconds", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("lane_group", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("overdue_cleanup_candidate", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_priority", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("lifecycle_state", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_queue_rank", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_action_label", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_persistence_key", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_persistence_status", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resolved_lane_state", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resolved_lane_prompt", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_history", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_history_count", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("stale_persistence_cleanup_status", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("stale_persistence_cleanup_prompt", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_action_readiness", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_action_readiness_reason", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_backlog_group", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("resume_backlog_priority", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_execution_log", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertIn("cleanup_execution_log_count", summary["scan_handoff_summary"]["handoff_markers"][0])
self.assertTrue(summary["external_context_summary"]["operator_questions"])
self.assertTrue(summary["external_context_summary"]["source_cards"])
self.assertEqual(summary["external_context_summary"]["exchange_status"]["status"], "active")
self.assertEqual(summary["external_context_summary"]["announcements_snapshot"]["status"], "active")
self.assertTrue(summary["external_context_summary"]["announcement_titles"])
self.assertIn("macro_news_calendar", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["macro_news_calendar_count"], 3)
self.assertIn("macro_news_provider_inventory", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["macro_news_provider_count"], 1)
self.assertIn("event_window_summary", summary["external_context_summary"])
self.assertIn("event_window_status", summary["external_context_summary"])
self.assertIn("event_window_prompt", summary["external_context_summary"])
self.assertTrue(summary["external_context_summary"]["high_volatility_window"])
self.assertTrue(summary["external_context_summary"]["exchange_anomaly_window"])
self.assertIn("window_type", summary["external_context_summary"]["macro_news_calendar"][0])
self.assertIn("chart_readonly_sources", summary["external_context_summary"])
self.assertIn("chart_provider_registry", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["chart_provider_registry_count"], 3)
self.assertIn("chart_stale_source_evidence", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["chart_readonly_source_count"], 1)
self.assertEqual(summary["external_context_summary"]["chart_stale_source_count"], 0)
self.assertEqual(summary["external_context_summary"]["chart_adapter_mode"], "live_reference")
self.assertEqual(summary["external_context_summary"]["chart_primary_mode"], "internal_review_packet_html")
self.assertIn("exchange_context_sources", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["exchange_context_source_count"], 3)
self.assertIn("provider_health_cards", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["provider_health_card_count"], 3)
self.assertIn("provider_health_summary", summary["external_context_summary"])
self.assertEqual(summary["external_context_summary"]["provider_health_summary"]["status"], "active")
self.assertIn("health_story", summary["external_context_summary"]["provider_health_summary"])
self.assertIn("visual_evidence_bridge_summary", summary["external_context_summary"])
self.assertEqual(summary["external_context_summary"]["visual_evidence_bridge_summary"]["status"], "active")
self.assertIn("bridge_story", summary["external_context_summary"]["visual_evidence_bridge_summary"])
self.assertIn("capture_surface_cards", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["capture_surface_card_count"], 3)
self.assertIn("capture_surface_summary", summary["external_context_summary"])
self.assertEqual(summary["external_context_summary"]["capture_surface_summary"]["status"], "active")
self.assertIn("capture_story", summary["external_context_summary"]["capture_surface_summary"])
self.assertIn("decision_support_boundary_summary", summary)
self.assertEqual(summary["decision_support_boundary_summary"]["label"], "Decision support available")
self.assertIn("operator_summary", summary["decision_support_boundary_summary"])
self.assertIn("live_analysis_assistant_summary", summary)
self.assertEqual(summary["live_analysis_assistant_summary"]["status"], "ready_for_readonly_live_analysis")
self.assertTrue(summary["evidence_summary"]["live_analysis_assistant_ready"])
self.assertTrue(summary["evidence_summary"]["must_not_trade"])
self.assertEqual(summary["readonly_live_health_summary"]["status"], "usable")
self.assertIn("decision_support_promotion_summary", summary)
self.assertEqual(
summary["decision_support_promotion_summary"]["status"],
"ready_for_decision_support_non_execution",
)
self.assertEqual(summary["evidence_summary"]["promotion_status"], "ready_for_decision_support_non_execution")
self.assertIn("shadow_paper_validation_summary", summary)
self.assertEqual(summary["shadow_paper_validation_summary"]["status"], "collecting_paper_samples")
self.assertEqual(summary["evidence_summary"]["shadow_paper_status"], "collecting_paper_samples")
self.assertEqual(summary["evidence_summary"]["next_shadow_paper_gate"], "paper_ticket_sample_gate")
self.assertIn("screenshot_readiness_summary", summary["external_context_summary"])
self.assertEqual(summary["external_context_summary"]["screenshot_readiness_summary"]["status"], "active")
self.assertIn("screenshot_story", summary["external_context_summary"]["screenshot_readiness_summary"])
self.assertIn("surface_selection_history_summary", summary["external_context_summary"])
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["status"], "active")
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["entry_count"], 1)
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["dominant_surface_title"], "binance_public_chart_reference")
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["dominant_source_group"], "visual_reference_card")
self.assertIn("recency_status", summary["external_context_summary"]["surface_selection_history_summary"])
self.assertIn("revisit_prompt", summary["external_context_summary"]["surface_selection_history_summary"])
self.assertIn("cadence_status", summary["external_context_summary"]["surface_selection_history_summary"])
self.assertIn("revisit_queue_count", summary["external_context_summary"]["surface_selection_history_summary"])
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["acknowledged_count"], 1)
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["resolution_history_count"], 1)
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["cleared_queue_status"], "active")
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"]["note_count"], 1)
self.assertEqual(summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"]["acknowledged_by_counts"]["local_operator"], 1)
self.assertIn("note_gap_queue_count", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("owner_spotlight", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("owner_cadence_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("note_gap_priority_lane_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("owner_trend_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("note_gap_priority_lane_story", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("owner_handoff_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("note_gap_priority_lane_carry_forward_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("owner_handoff_follow_up_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("note_gap_priority_lane_carry_forward_aging_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("resolved_owner_drift_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("carry_forward_follow_up_lane_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("resolved_owner_drift_review_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("resolved_owner_drift_review_queue_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("resolved_owner_drift_review_queue_aging_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("carry_forward_follow_up_lane_rollover_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("carry_forward_follow_up_lane_rollover_handoff_status", summary["external_context_summary"]["surface_selection_history_summary"]["resolution_filter_summary"])
self.assertIn("visual_reference_cards", summary["external_context_summary"])
self.assertGreaterEqual(summary["external_context_summary"]["visual_reference_card_count"], 3)
self.assertIsNotNone(summary["external_context_summary"]["visual_reference_next_prompt"])
self.assertIn("live", summary["external_context_summary"]["chart_context_prompt"].lower())
self.assertIn("CPI", summary["external_context_summary"]["headline"])
self.assertTrue(any(item["title"] == "External Visual Evidence" for item in summary["evidence_chain"]))
self.assertTrue(summary["no_trade_reason_items"])
self.assertIn("watch_london_open", summary["next_actions"])
self.assertIn("review_london_session_playbook", summary["next_actions"])
self.assertIn("action:observe_only->confirm_ticket", summary["changed_since_last"]["items"])
self.assertIn("candidate:none->18", summary["changed_since_last"]["items"])
def test_decision_assistant_blocks_review_now_without_active_plan(self) -> None:
service = DecisionAssistantSummaryService(
launch_readiness_service=_LaunchReadinessService(),
session_focus_service=_SessionFocusSummaryService(),
operator_digest_service=_OperatorDigestSummaryService(),
decision_repository=_PreviousPacketRepo(),
candidate_repository=_NoActivePlanCandidateWatchlistRepo(),
research_council_service=_ResearchCouncilSummaryService(),
exchange_status_provider=_ExchangeStatusProvider(),
announcements_provider=_AnnouncementsProvider(),
external_chart_provider=_ExternalChartProvider(),
decision_assistant_repository=_DecisionAssistantRepo(),
market_state_repository=_BacktestQualityGateRepo(passed=True),
)
summary = service.build_summary(
instrument_id=1,
timeframe="5m",
decision_packet={
"status": "actionable",
"action_recommendation": "confirm_ticket",
"candidate_id": 18,
"packet_ts": datetime.now(timezone.utc).isoformat(),
"setup_type": "SWEEP_MSS_FVG",
"why_allowed": ["candidate_confirmed"],
"why_blocked": [],
},
weekly_review={"next_week_actions": [], "session_performance": {}},
recent_alerts=[],
recent_notes=[],
recent_executions=[],
)
self.assertEqual(summary["mainline_focus_gate"]["status"], "blocked")
self.assertIn("missing_active_trading_plan", summary["mainline_focus_gate"]["blockers"])
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"], [])
self.assertEqual(summary["candidate_watchlist_tiers"]["archive_only"][0]["candidate_id"], 18)
self.assertIn("missing_active_trading_plan", summary["candidate_watchlist_tiers"]["archive_only"][0]["mainline_gate_blockers"])
self.assertEqual(summary["candidate_watchlist_summary"]["focus_matrix"]["status"], "study_only")
self.assertEqual(summary["trader_mode_summary"]["status"], "observe_only")
self.assertEqual(summary["trader_mode_summary"]["status_label"], "Observe Only")
def test_decision_assistant_blocks_review_now_when_backtest_quality_gate_fails(self) -> None:
service = DecisionAssistantSummaryService(
launch_readiness_service=_LaunchReadinessService(),
session_focus_service=_SessionFocusSummaryService(),
operator_digest_service=_OperatorDigestSummaryService(),
decision_repository=_PreviousPacketRepo(),
candidate_repository=_CandidateWatchlistRepo(),
research_council_service=_ResearchCouncilSummaryService(),
exchange_status_provider=_ExchangeStatusProvider(),
announcements_provider=_AnnouncementsProvider(),
external_chart_provider=_ExternalChartProvider(),
decision_assistant_repository=_DecisionAssistantRepo(),
market_state_repository=_BacktestQualityGateRepo(passed=False, reasons=["sample_size_too_small"]),
)
summary = service.build_summary(
instrument_id=1,
timeframe="5m",
decision_packet={
"status": "actionable",
"action_recommendation": "confirm_ticket",
"candidate_id": 18,
"packet_ts": datetime.now(timezone.utc).isoformat(),
"setup_type": "SWEEP_MSS_FVG",
"why_allowed": ["candidate_confirmed"],
"why_blocked": [],
},
weekly_review={"next_week_actions": [], "session_performance": {}},
recent_alerts=[],
recent_notes=[],
recent_executions=[],
)
self.assertEqual(summary["mainline_focus_gate"]["status"], "blocked")
self.assertIn("sample_size_too_small", summary["mainline_focus_gate"]["blockers"])
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"], [])
self.assertEqual(summary["trader_mode_summary"]["status"], "observe_only")
self.assertIn("sample_size_too_small", summary["trader_mode_summary"]["key_reasons"])
readout = summary["trader_mode_summary"]["trader_readout"]
self.assertIn("No trader-ready playbook", readout["now"])
self.assertIn("Strategy Backtest", readout["no_candidate_state"])
self.assertIn("more backtest samples", readout["wait"])
self.assertIn("Study only", readout["historical_edge"])
self.assertIn("no historical edge", readout["historical_edge"])
def test_decision_assistant_d7_keeps_study_only_candidates_out_of_trader_focus(self) -> None:
service = DecisionAssistantSummaryService(
launch_readiness_service=_LaunchReadinessService(),
session_focus_service=_SessionFocusSummaryService(),
operator_digest_service=_OperatorDigestSummaryService(),
decision_repository=_PreviousPacketRepo(),
candidate_repository=_StudyOnlyCandidateWatchlistRepo(),
research_council_service=_ResearchCouncilSummaryService(),
exchange_status_provider=_ExchangeStatusProvider(),
announcements_provider=_AnnouncementsProvider(),
external_chart_provider=_ExternalChartProvider(),
decision_assistant_repository=_DecisionAssistantRepo(),
market_state_repository=_BacktestQualityGateRepo(passed=True),
)
summary = service.build_summary(
instrument_id=1,
timeframe="5m",
decision_packet={
"status": "actionable",
"action_recommendation": "confirm_ticket",
"candidate_id": 77,
"packet_ts": datetime.now(timezone.utc).isoformat(),
"setup_type": "SWEEP_MSS_FVG",
"why_allowed": ["candidate_confirmed"],
"why_blocked": [],
},
weekly_review={"next_week_actions": [], "session_performance": {}},
recent_alerts=[],
recent_notes=[],
recent_executions=[],
)
product_summary = summary["candidate_watchlist_summary"]["trader_product_gate_summary"]
self.assertEqual(product_summary["allowed_focus_count"], 0)
self.assertEqual(product_summary["study_only_count"], 1)
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"], [])
self.assertEqual(summary["candidate_watchlist_summary"]["focus_matrix"]["status"], "study_only")
self.assertIsNone(summary["candidate_watchlist_summary"]["next_watchlist_focus"])
trader_mode_summary = summary["trader_mode_summary"]
self.assertEqual(trader_mode_summary["status_label"], "Observe Only")
self.assertEqual(trader_mode_summary["primary_opportunity"], {})
self.assertIn("Strategy Backtest", trader_mode_summary["trader_readout"]["no_candidate_state"])
self.assertIn("study-only", trader_mode_summary["trader_readout"]["no_candidate_state"])
self.assertTrue(any("Candidate is not linked" in reason for reason in trader_mode_summary["plain_language_reasons"]))
def test_decision_assistant_trader_mode_freezes_stale_payload(self) -> None:
service = DecisionAssistantSummaryService(
launch_readiness_service=_StaleLaunchReadinessService(),
session_focus_service=_SessionFocusSummaryService(),
operator_digest_service=_OperatorDigestSummaryService(),
decision_repository=_PreviousPacketRepo(),
candidate_repository=_CandidateWatchlistRepo(),
research_council_service=_ResearchCouncilSummaryService(),
exchange_status_provider=_ExchangeStatusProvider(),
announcements_provider=_AnnouncementsProvider(),
external_chart_provider=_ExternalChartProvider(),
decision_assistant_repository=_DecisionAssistantRepo(),
market_state_repository=_BacktestQualityGateRepo(passed=True),
)
summary = service.build_summary(
instrument_id=1,
timeframe="1m",
decision_packet={
"status": "actionable",
"action_recommendation": "confirm_ticket",
"candidate_id": 18,
"packet_ts": "2026-01-01T00:00:00+00:00",
"ai_explanation": "Would otherwise be a focus setup.",
"setup_type": "SWEEP_MSS_FVG",
"why_allowed": ["candidate_confirmed"],
"why_blocked": [],
},
weekly_review={"next_week_actions": [], "session_performance": {}},
recent_alerts=[],
recent_notes=[],
recent_executions=[],
)
trader_mode_summary = summary["trader_mode_summary"]
self.assertEqual(trader_mode_summary["status"], "do_not_use")
self.assertEqual(trader_mode_summary["status_label"], "Do Not Use")
self.assertEqual(trader_mode_summary["data_confidence"], "stale")
self.assertTrue(trader_mode_summary["freshness_summary"]["is_stale"])
self.assertTrue(trader_mode_summary["freshness_freeze"])
self.assertEqual(trader_mode_summary["primary_question_can_watch_now"], "no")
self.assertIn("Do Not Use", trader_mode_summary["trader_readout"]["headline"])
self.assertIn("restore", trader_mode_summary["trader_readout"]["why_no_trade"].lower())
def test_trader_mode_uses_readonly_rest_fallback_without_do_not_use(self) -> None:
service = DecisionAssistantSummaryService(
launch_readiness_service=_ReadonlyFallbackLaunchReadinessService(),
session_focus_service=_SessionFocusSummaryService(),
operator_digest_service=_OperatorDigestSummaryService(),
decision_repository=_PreviousPacketRepo(),
candidate_repository=_CandidateWatchlistRepo(),
research_council_service=_ResearchCouncilSummaryService(),
exchange_status_provider=_ExchangeStatusProvider(),
announcements_provider=_AnnouncementsProvider(),
external_chart_provider=_ExternalChartProvider(),
decision_assistant_repository=_DecisionAssistantRepo(),
market_state_repository=_BacktestQualityGateRepo(passed=True),
)
summary = service.build_summary(
instrument_id=1,
timeframe="1m",
decision_packet={
"status": "actionable",
"action_recommendation": "confirm_ticket",
"candidate_id": 18,
"packet_ts": datetime.now(timezone.utc).isoformat(),
"setup_type": "SWEEP_MSS_FVG",
"why_allowed": ["candidate_confirmed"],
"why_blocked": [],
},
weekly_review={"next_week_actions": [], "session_performance": {}},
recent_alerts=[],
recent_notes=[],
recent_executions=[],
)
trader_mode_summary = summary["trader_mode_summary"]
self.assertEqual(trader_mode_summary["status_label"], "Review Now")
self.assertEqual(trader_mode_summary["data_confidence"], "fresh")
self.assertFalse(trader_mode_summary["freshness_summary"]["is_stale"])
self.assertEqual(trader_mode_summary["freshness_summary"]["stream_warning"], "market_stream_connecting")
self.assertIn("stream connecting", trader_mode_summary["trader_readout"]["data_state"])
self.assertIn("No trade", trader_mode_summary["trader_readout"]["why_no_trade"])
self.assertEqual(trader_mode_summary["ops_duty_panel"]["trader_safe_health"]["label"], "Use With REST Fallback")
self.assertTrue(trader_mode_summary["ops_duty_panel"]["trader_safe_health"]["can_use_for_readonly_analysis"])
class _NoteRepo:
def insert_note(self, **kwargs):
return 11
def fetch_recent_notes(self, **kwargs):
return [{"id": 11, "author": "alice"}]
class _CandidateRepo:
def fetch_recent_candidates(self, **kwargs):
return [{"session_code": "LONDON", "status": "executable", "executable": True}]
class _ExecutionService:
def build_execution_dashboard_items(self, **kwargs):
return [{"session_code": "LONDON", "execution_status": "open"}]
class _DecisionRepo:
def fetch_recent_decision_alerts(self, **kwargs):
return [{"payload": {"session_code": "LONDON"}, "alert_type": "candidate_actionable"}]
class _TrustService:
def build_summary(self, **kwargs):
return {
"models": [
{
"session_recommendations": [
{"session_code": "LONDON", "score": 80.0, "classification": "prefer"},
{"session_code": "NY_AM", "score": 30.0, "classification": "avoid"},
]
}
]
}
class _NoteService:
def get_recent_notes(self, **kwargs):
return [{"id": 1, "author": "alice"}]
class _DecisionService:
def build_preview(self, **kwargs):
return {"status": "actionable", "action_recommendation": "confirm_ticket"}
class _WeeklyReviewService:
def build_summary(self, **kwargs):
return {"entry_count": 2}
class _AlertQualitySummaryService:
def build_summary(self, **kwargs):
return {"recent_alert_count": 1, "operator_summary": "delivery success=100.0%"}
class _ResearchCouncilService:
def get_latest_run(self, **kwargs):
return {"id": 31, "payload": {"decision": {"overall_stance": "observe_only"}}}
class _EmptyResearchCouncilService:
def get_latest_run(self, **kwargs):
return None
class _OptimizationReviewService:
def build_review(self, **kwargs):
return {
"present": True,
"health_state": "healthy",
"recommended_actions": ["continue_periodic_optimization_cycle"],
}
class _DecisionAssistantService:
def build_summary(self, **kwargs):
return {
"status": "completed",
"focus_mode": "monitor_and_wait",
"headline": "Monitor session flow.",
"top_priority_items": [],
"next_actions": [],
}
class _SessionRepo:
def fetch_recent_sessions(self, **kwargs):
return [
{"session_code": "LONDON", "status": "active", "start_ts": None, "end_ts": None},
{"session_code": "NY_AM", "status": "closed", "start_ts": None, "end_ts": None},
]
class _LaunchReadinessService:
def build_summary(self, *, instrument_id: int, timeframe: str = "1m"):
now = datetime.now(timezone.utc).isoformat()
return {
"instrument_id": instrument_id,
"timeframe": timeframe,
"analysis_only_ready": True,
"live_analysis_assistant_ready": True,
"decision_support_ready": True,
"blocking_reasons": [],
"warning_reasons": ["trust_observe_mode"],
"next_actions": ["review_priority_setup"],
"product_operating_mode": "decision_support_only",
"readonly_account_context_status": "available",
"readonly_live_health_summary": {
"status": "usable",
"api_health": "available",
"runtime_health": "available",
"market_freshness": "market_data_fresh",
"market_stream": "market_stream_connected",
"broker_account_context": "available",
"latest_candidate_timestamp": now,
"latest_decision_packet_timestamp": now,
"latest_review_packet_timestamp": now,
"current_candidate_count": 3,
"current_blocker": "",
"recommendation": "can_use_for_live_market_analysis",
},
"live_analysis_assistant_summary": {
"status": "ready_for_readonly_live_analysis",
"ready_for_readonly_live_analysis": True,
"must_not_trade": True,
"execution_allowed": False,
"launch_boundary": {
"mode_label": "Read-only Live Analysis",
"not_buy_sell_instruction": True,
"no_order_submission": True,
"execution_remains_disabled": True,
},
"health_summary": {
"status": "usable",
"market_freshness": "market_data_fresh",
"broker_account_context": "available",
},
"allowed_actions": ["review_market_context", "record_shadow_observation_or_operator_note"],
"blocked_actions": ["submit_live_orders", "live_execute"],
},
"decision_support_boundary_summary": {
"status": "decision_support_available",
"label": "Decision support available",
"operator_summary": "Decision support is available for human review, but execution assist and live submit remain gated.",
"current_model_boundary": "decision_support_candidate_available",
"current_model_reason": "decision_support_list_present",
"live_execution_boundary": "live_submit_blocked",
"live_execution_reason": "adapter_real_live_not_ready",
"allowed_operator_actions": ["review_market_context", "use_decision_support_for_manual_trade_review"],
"blocked_operator_actions": ["submit_live_orders", "enable_default_auto_trading"],
"next_boundary_gate": "execution_assist_trust_gate",
"next_boundary_gate_reason": "execution_candidate_list_missing",
},
"decision_support_promotion_summary": {
"status": "ready_for_decision_support_non_execution",
"strict_decision_support_ready": True,
"current_decision_support_ready": True,
"execution_assist_ready": False,
"execution_boundary_after_promotion": "execution_still_blocked",
"real_live_boundary_after_promotion": "real_live_still_blocked",
"candidate_model_code": "OSOK",
"candidate_classification": "observe",
"operator_review_sample_count": 5,
"passed_criteria_count": 8,
"required_criteria_count": 8,
"failed_criteria": [],
"next_promotion_gate": "decision_support_ready_can_be_true_without_execution",
"operator_summary": "Decision support can be marked ready for human review; execution assist and live submit remain blocked.",
},
"shadow_paper_validation_summary": {
"status": "collecting_paper_samples",
"observed_shadow_day_count": 3,
"window_days": 14,
"candidate_count": 6,
"paper_ticket_count": 1,
"paper_execution_count": 1,
"operator_feedback_count": 2,
"journal_entry_count": 1,
"risk_violation_count": 0,
"passed_criteria_count": 4,
"required_criteria_count": 8,
"failed_criteria": ["paper_ticket_sample_gate"],
"next_validation_gate": "paper_ticket_sample_gate",
"execution_boundary": "real_live_still_blocked",
"operator_summary": "Shadow/paper validation is collecting paper samples.",
"daily_coverage": [],
"recent_samples": {
"shadow_observations": [
{
"operator_agreement": "no_trade",
"observation_status": "operator_feedback_recorded",
"system_recommendation": "stand_down_blocked_candidate",
"reason_codes": ["wait_for_mss"],
"paper_ticket_id": None,
"paper_execution_id": None,
},
{
"operator_agreement": "need_more_evidence",
"observation_status": "operator_feedback_recorded",
"system_recommendation": "observe_only",
"reason_codes": ["backtest_quality_gate_failed"],
"paper_ticket_id": None,
"paper_execution_id": None,
},
],
},
},
"market_state": {
"session": {"session_code": "LONDON", "status": "active"},
"freshness": {"code": "market_data_fresh"},
"stream_state": {"code": "market_stream_connected"},
"data_gap_state": {"code": "market_data_gap_clear"},
"news_window": {
"code": "news_window_active",
"provider_name": "tradingeconomics_snapshot",
"freshness_code": "news_provider_fresh",
"event_count": 1,
"active_events": [{"title": "CPI"}],
},
},
"external_waiting_items": ["real_trade_sample_verified"],
}
class _StaleLaunchReadinessService(_LaunchReadinessService):
def build_summary(self, *, instrument_id: int, timeframe: str = "1m"):
payload = super().build_summary(instrument_id=instrument_id, timeframe=timeframe)
stale_ts = "2026-01-01T00:00:00+00:00"
payload["readonly_live_health_summary"]["latest_candidate_timestamp"] = stale_ts
payload["readonly_live_health_summary"]["latest_decision_packet_timestamp"] = stale_ts
payload["readonly_live_health_summary"]["latest_review_packet_timestamp"] = stale_ts
return payload
class _ReadonlyFallbackLaunchReadinessService(_LaunchReadinessService):
def build_summary(self, *, instrument_id: int, timeframe: str = "1m"):
payload = super().build_summary(instrument_id=instrument_id, timeframe=timeframe)
payload["decision_support_ready"] = False
payload["blocking_reasons"] = ["market_truth_ready", "broker_state_ready"]
payload["readonly_live_health_summary"]["latest_candidate_timestamp"] = "2026-01-01T00:00:00+00:00"
payload["readonly_live_health_summary"]["market_stream"] = "market_stream_connecting"
payload["readonly_live_health_summary"]["runtime_health"] = "blocked"
payload["readonly_live_health_summary"]["current_blocker"] = ""
payload["readonly_live_health_summary"]["broker_account_context"] = "unavailable"
payload["live_analysis_assistant_summary"]["ready_for_readonly_live_analysis"] = True
payload["live_analysis_assistant_summary"]["blockers"] = []
payload["live_analysis_assistant_summary"]["warnings"] = [
"market_stream_not_connected_rest_fallback",
"account_context_unavailable",
]
return payload
class _SessionFocusSummaryService:
def build_summary(self, *, instrument_id: int, limit: int = 6):
return {
"instrument_id": instrument_id,
"items": [
{
"session_code": "LONDON",
"status": "active",
"alert_count": 1,
"executable_candidate_count": 1,
"open_execution_count": 0,
"action_hint": "focus_now",
"session_recommendation": {"classification": "prefer", "score": 82.0},
}
],
}
class _OperatorDigestSummaryService:
def build_digest(self, *, instrument_id: int, top_limit: int = 5):
return {
"instrument_id": instrument_id,
"status": "completed",
"summary_counts": {"high_value_alert_count": 1},
"quality_flags": ["visual_mismatch_present"],
"quality_summary": {"status": "completed"},
"operator_summary": "pending feedback=2",
"review_queue": [
{
"queue_type": "high_value_alert",
"priority": "medium",
"alert_type": "candidate_actionable",
"reason": "needs_operator_review",
}
],
}
class _CandidateWatchlistRepo:
def fetch_recent_candidates(self, *, instrument_id: int, limit: int = 20):
return [
{
"id": 18,
"trading_plan_id": 101,
"session_code": "LONDON",
"status": "candidate",
"quality_score": 81.0,
"model_code": "SWEEP_MSS_FVG",
"setup_playbook_id": "ICT-CH1-6-LONDON-DISCOUNT-REVERSAL",
"range_id": "london-range-1",
"range_high": 105.0,
"range_low": 95.0,
"range_timeframe": "1h",
"equilibrium": 100.0,
"primary_draw": "sell_side_liquidity",
"draw_side": "sell_side",
"objective_price": 96.0,
"premium_discount_zone": "discount",
"location_advantage": "discount_reclaim",
"context": "daily range expansion",
"framework": "London killzone reversal",
"reference_point": "range_low_sweep",
"setup_type": "range_sweep_mss_fvg_retrace",
"allowed_sessions": ["LONDON"],
"path_context": "sweep_then_displacement_then_retrace",
"sequence_validity": "sweep_mss_fvg_retrace",
"liquidity_target_side": "sell_side",
"liquidity_pool_id": "london_ssl_1",
"liquidity_pool_rank": "primary",
"liquidity_event": "sweep",
"swept_level": 94.8,
"sweep_confirmed": True,
"raid_stop_run_kind": "sell_side_sweep",
"manipulation_window_state": "complete",
"post_take_reaction": "displacement",
"pd_array_type": "fvg",
"pd_array_id": "fvg_london_1",
"pd_array_role": "entry_retrace",
"pd_array_rank": "primary",
"array_location": "discount",
"array_timeframe": "5m",
"array_freshness": "fresh",
"formation_event": "post_sweep_displacement",
"displacement_origin": "mss_london_1",
"fvg_role": "entry_retrace",
"fvg_retrace_confirmed": True,
"reaction_quality": "clean_rejection",
"session_window_state": "verified",
"killzone_name": "London Open",
"killzone_state": "active",
"judas_window_state": "complete",
"opening_range_state": "resolved",
"weekly_profile": "weekly_draw_to_external_liquidity",
"news_window_state": "clear",
"session_invalidation": "range_low_lost",
"invalidations": ["range_low_reclaim_failed"],
"invalidation": "range_low_lost",
"stop_loss": 94.5,
"invalid_level": 94.5,
"target_plan": {"tp1": 100.0, "tp2": 104.0, "irl": "near_fvg", "erl": "range_high"},
"ai_explanation": "Current focus candidate.",
"missing_confirmations": ["trust_soft_block"],
},
{
"id": 21,
"trading_plan_id": 101,
"session_code": "LONDON",
"status": "candidate",
"quality_score": 72.0,
"model_code": "SWEEP_MSS_FVG",
"ai_explanation": "Strong session-aligned watch.",
"missing_confirmations": ["mss"],
},
{
"id": 22,
"trading_plan_id": 101,
"session_code": "NY_AM",
"status": "candidate",
"quality_score": 44.0,
"model_code": "SWEEP_MSS_FVG",
"ai_explanation": "Needs more confirmation.",
"missing_confirmations": ["fvg_retrace"],
},
{
"id": 23,
"trading_plan_id": 101,
"session_code": "OFF_HOURS",
"status": "candidate",
"quality_score": 12.0,
"model_code": "SWEEP_MSS_FVG",
"ai_explanation": "Low-priority background candidate.",
"missing_confirmations": [],
},
]
def fetch_candidate_by_id(self, candidate_id: int):
for item in self.fetch_recent_candidates(instrument_id=1):
if int(item.get("id") or 0) == int(candidate_id):
return {"instrument_id": 1, **item}
return None
def fetch_active_trading_plan(self, *, instrument_id: int, plan_date=None):
return {
"id": 101,
"instrument_id": instrument_id,
"plan_date": "2026-05-12",
"status": "active",
"setup_playbook_id": "ICT-CH1-6-LONDON-DISCOUNT-REVERSAL",
"primary_draw": {"side": "buy_side", "source": "liquidity_pool"},
"invalidations": [{"code": "range_low_lost"}],
"no_trade_conditions": [],
"bias": "bullish",
}
class _NoActivePlanCandidateWatchlistRepo(_CandidateWatchlistRepo):
def fetch_active_trading_plan(self, *, instrument_id: int, plan_date=None):
return None
class _StudyOnlyCandidateWatchlistRepo(_CandidateWatchlistRepo):
def fetch_recent_candidates(self, *, instrument_id: int, limit: int = 20):
return [
{
"id": 77,
"trading_plan_id": 101,
"session_code": "LONDON",
"status": "executable",
"executable": True,
"quality_score": 91.0,
"model_code": "SWEEP_MSS_FVG",
"ai_explanation": "Raw surface-shape candidate without Doctrine playbook mapping.",
"missing_confirmations": [],
}
]
class _BacktestQualityGateRepo:
def __init__(self, *, passed: bool, reasons=None) -> None:
self.passed = passed
self.reasons = list(reasons or [])
def fetch_latest_backtest_quality_gate(self, *, instrument_id: int):
return {
"passed": self.passed,
"reasons": self.reasons,
"backtest_run_id": 489,
"observed_ts": "2026-05-12T01:00:00+00:00",
}
class _PreviousPacketRepo:
def fetch_latest_decision_packet(self, *, instrument_id: int):
return {
"id": 7,
"instrument_id": instrument_id,
"packet_ts": "2026-05-12T01:55:00+00:00",
"action_recommendation": "observe_only",
"status": "observe_only",
"payload": {
"status": "observe_only",
"action_recommendation": "observe_only",
"candidate_id": None,
"bias": "neutral",
"trust_classification": "observe",
"why_blocked": ["candidate_not_confirmed"],
},
}
class _ResearchCouncilSummaryService:
def get_compact_lessons(
self,
*,
instrument_id: int,
session_code=None,
setup_type=None,
model_code=None,
limit: int = 5,
):
return [
{
"title": "London setup lesson",
"lesson_key": "lesson:london",
"lesson_summary": "Confirmed London sweep setups perform better after MSS reclaim.",
"lesson_type": "journal_pattern",
}
]
class _ExchangeStatusProvider:
def fetch_snapshot(self):
return {
"provider_name": "okx_system_status",
"status": "active",
"active_events": [{"title": "API maintenance", "service_type": "api"}],
}
class _AnnouncementsProvider:
def fetch_snapshot(self):
return {
"provider_name": "okx_announcements",
"status": "active",
"announcements": [{"title": "Listing schedule update", "ann_type": "listing"}],
}
class _ExternalChartProvider:
def fetch_snapshot(self, **kwargs):
return {
"provider_name": "binance_public_chart_reference",
"provider_key": "binance_public_chart_reference:BTCUSDT:5m",
"status": "active",
"freshness_code": "provider_fresh",
"cache_state": "fresh",
"symbol": "BTC-USDT",
"api_symbol": "BTCUSDT",
"interval": "5m",
"chart_url": "https://www.binance.com/en/trade/BTC_USDT?type=spot",
"candle_count": 2,
"reference_price": 111.0,
"reference_ts": "2026-05-12T02:00:00+00:00",
"error": None,
"warning": None,
"raw_payload": {"data": []},
}
class _DecisionAssistantRepo:
def fetch_preference(self, **kwargs):
return {
"profile_key": "local_operator",
"instrument_id": kwargs["instrument_id"],
"digest_mode": "high_value_only",
"preferred_history_kind": "reminder",
"importance_threshold": 75,
"payload": {
"source": "unit",
"profile_label": "Desk A",
"focus_candidate_id": 18,
"focus_session_code": "LONDON",
"focus_model_code": "SWEEP_MSS_FVG",
"importance_templates": {"risk_first_change": 80},
"archive_baselines": {
"digest_snapshot_id": 9,
"workflow_snapshot_id": 10,
"updated_ts": "2026-05-12T02:04:00+00:00",
},
"workflow_bridge_state": {
"handoff_packet_starter": {"headline": "Saved handoff starter"},
"replay_note_bridge_summary": {"note_title": "Saved replay note"},
"artifact_bundle_summary": {"starter_export_prompt": "Saved artifact bundle"},
"compare_writeback_summary": {"note_title": "Saved compare note", "status": "persisted"},
"query_context": {"profile_scope": "selected_profile", "query_preset": "intraday_recent"},
"updated_ts": "2026-05-12T02:04:30+00:00",
},
"external_context_surface_history": [
{
"surface_id": "binance_public_chart_reference",
"title": "binance_public_chart_reference",
"source_group": "visual_reference_card",
"selection_kind": "target_url",
"target_url": "https://www.binance.com/en/trade/BTC_USDT?type=spot",
"selected_ts": "2026-05-12T02:05:30+00:00",
"revisit_acknowledged": True,
"revisit_acknowledged_ts": "2026-05-12T02:15:30+00:00",
"revisit_acknowledged_by": "local_operator",
"revisit_ack_note": "checked and cleared",
}
],
},
"updated_ts": "2026-05-12T02:05:00+00:00",
}
def fetch_preferences(self, **kwargs):
return [self.fetch_preference(**kwargs)]
def fetch_recent_digest_history(self, **kwargs):
return [
{
"id": 9,
"profile_key": "local_operator",
"instrument_id": kwargs["instrument_id"],
"digest_mode": "high_value_only",
"preferred_history_kind": "reminder",
"top_summary": "Review Candidate #18",
"snapshot_signature": "unit-history-signature",
"payload": {
"focus_mode": "review_top_setup",
"top_opportunity_title": "Candidate #18",
"next_digest_action_label": "Review reminder",
"high_value_reminder_count": 1,
"reminder_count": 2,
},
"created_ts": "2026-05-12T02:06:00+00:00",
}
]
def fetch_latest_digest_history(self, **kwargs):
return self.fetch_recent_digest_history(**kwargs)[0]
def insert_digest_history(self, **kwargs):
return {
"id": 9,
"instrument_id": kwargs["instrument_id"],
"snapshot_signature": kwargs["snapshot_signature"],
"created_ts": "2026-05-12T02:07:00+00:00",
}
def fetch_recent_workflow_history(self, **kwargs):
return [
{
"id": 10,
"profile_key": "local_operator",
"instrument_id": kwargs["instrument_id"],
"current_session": "LONDON",
"next_phase": "intraday",
"next_artifact_kind": "intraday_monitor_digest",
"snapshot_signature": "unit-workflow-signature",
"payload": {
"focus_mode": "review_top_setup",
"artifact_count": 3,
"ready_for_handoff_count": 1,
"needs_update_count": 1,
"pending_capture_count": 1,
},
"created_ts": "2026-05-12T02:08:00+00:00",
}
]
def fetch_latest_workflow_history(self, **kwargs):
return self.fetch_recent_workflow_history(**kwargs)[0]
def insert_workflow_history(self, **kwargs):
return {
"id": 10,
"instrument_id": kwargs["instrument_id"],
"snapshot_signature": kwargs["snapshot_signature"],
"created_ts": "2026-05-12T02:09:00+00:00",
}
if __name__ == "__main__":
unittest.main()