1780 lines
114 KiB
Python
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()
|