902 lines
60 KiB
Python
902 lines
60 KiB
Python
import os
|
|
import unittest
|
|
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
|
|
|
|
|
|
class OperatorWorkspaceServiceTests(unittest.TestCase):
|
|
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(),
|
|
decision_assistant_repository=_DecisionAssistantRepo(),
|
|
)
|
|
|
|
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",
|
|
"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"], "decision_packet")
|
|
self.assertEqual(summary["top_opportunities"][0]["kind"], "decision_packet")
|
|
self.assertEqual(summary["candidate_watchlist_tiers"]["review_now"][0]["candidate_id"], 18)
|
|
self.assertEqual(summary["candidate_watchlist_tiers"]["strong_watch"][0]["candidate_id"], 21)
|
|
self.assertEqual(summary["candidate_watchlist_tiers"]["soft_watch"][0]["candidate_id"], 22)
|
|
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("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("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("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.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"]["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("profile_reason", summary["digest_surface"]["digest_preference_profile"])
|
|
self.assertIn("digest_history_archive", summary["digest_surface"])
|
|
self.assertIn("digest_history_archive_count", summary["digest_surface"])
|
|
self.assertIn("digest_persistence_status", summary["digest_surface"])
|
|
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.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"], "market_session_fallback")
|
|
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("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_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"], "optional_reference")
|
|
self.assertEqual(summary["external_context_summary"]["chart_primary_mode"], "internal_review_packet_html")
|
|
self.assertIn("optional", summary["external_context_summary"]["chart_context_prompt"].lower())
|
|
self.assertIn("CPI", summary["external_context_summary"]["headline"])
|
|
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"])
|
|
|
|
|
|
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"):
|
|
return {
|
|
"instrument_id": instrument_id,
|
|
"timeframe": timeframe,
|
|
"analysis_only_ready": True,
|
|
"decision_support_ready": True,
|
|
"blocking_reasons": [],
|
|
"warning_reasons": ["trust_observe_mode"],
|
|
"next_actions": ["review_priority_setup"],
|
|
"product_operating_mode": "decision_support_only",
|
|
"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 _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,
|
|
"session_code": "LONDON",
|
|
"status": "candidate",
|
|
"quality_score": 81.0,
|
|
"model_code": "SWEEP_MSS_FVG",
|
|
"ai_explanation": "Current focus candidate.",
|
|
"missing_confirmations": ["trust_soft_block"],
|
|
},
|
|
{
|
|
"id": 21,
|
|
"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,
|
|
"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,
|
|
"session_code": "OFF_HOURS",
|
|
"status": "candidate",
|
|
"quality_score": 12.0,
|
|
"model_code": "SWEEP_MSS_FVG",
|
|
"ai_explanation": "Low-priority background candidate.",
|
|
"missing_confirmations": [],
|
|
},
|
|
]
|
|
|
|
|
|
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 _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"},
|
|
"updated_ts": "2026-05-12T02:05:00+00:00",
|
|
}
|
|
|
|
def fetch_recent_digest_history(self, **kwargs):
|
|
return [
|
|
{
|
|
"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",
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|