Add chart inventory and continuous assistant rail
This commit is contained in:
+17
-3
@@ -108,6 +108,10 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_workflow_rhythm_slice: pre_intraday_post_rhythm_checklist
|
||||
- latest_decision_assistant_learning_attribution_slice: failure_missed_opportunity_setup_session_stats
|
||||
- latest_decision_assistant_digest_high_value_slice: importance_templates_high_value_filter
|
||||
- latest_decision_assistant_chart_source_slice: chart_readonly_source_inventory_stale_evidence
|
||||
- latest_decision_assistant_continuous_workspace_slice: continuous_workspace_rail
|
||||
- latest_decision_assistant_weekly_trend_slice: weekly_trend_breakdown
|
||||
- latest_decision_assistant_digest_review_back_slice: digest_history_review_back_actions
|
||||
- latest_decision_assistant_summary_status: implemented
|
||||
- latest_decision_assistant_change_summary_status: implemented
|
||||
- latest_decision_assistant_evidence_status: implemented
|
||||
@@ -200,10 +204,14 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_workflow_rhythm_status: implemented
|
||||
- latest_decision_assistant_learning_attribution_status: implemented
|
||||
- latest_decision_assistant_digest_high_value_status: implemented
|
||||
- latest_decision_assistant_chart_source_status: implemented_inventory_only_external_url_missing
|
||||
- latest_decision_assistant_continuous_workspace_status: implemented
|
||||
- latest_decision_assistant_weekly_trend_status: implemented
|
||||
- latest_decision_assistant_digest_review_back_status: implemented
|
||||
- latest_decision_assistant_workbench_status: visible_local_smoke
|
||||
- latest_remote_decision_assistant_deploy_status: deployed_current_head
|
||||
- latest_remote_decision_assistant_smoke_status: passed
|
||||
- latest_remote_decision_assistant_review_packet_status: external_calendar_workflow_rhythm_learning_attribution_high_value_digest_visible
|
||||
- latest_remote_decision_assistant_review_packet_status: chart_inventory_continuous_workspace_weekly_trend_digest_review_back_visible
|
||||
- latest_p7_selector_choice: continue_periodic_analysis_decision_monitoring_with_maturity_rollup
|
||||
- latest_p14_slice: webhook_transport_retry
|
||||
- latest_p15_pxx_tasklist_status: ready
|
||||
@@ -294,6 +302,11 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- Added personalized digest high-value filtering fields (`importance_score`, `importance_template`, `high_value_reminders`, `important_change_filter`, and `importance_templates`) plus cockpit visibility.
|
||||
- Passed focused validation: `unittest tests.unit.test_operator_workspace_service`, `node --check src/web/app.js`, `unittest tests.unit.test_operator_workspace_service tests.unit.test_api_static_server tests.unit.test_api_server`, full `unittest discover -s tests/unit` (713 tests), and `scripts/run_script_static_checks.py`.
|
||||
- Deployed the current working tree to `http://192.168.3.90:8000`; deploy smoke passed 11 endpoints, `run_remote_ops_smoke.py` passed 9 checks, and remote field smoke confirmed macro/news calendar, workflow rhythm, learning attribution, and high-value digest fields.
|
||||
- Added external chart read-only source inventory / stale-source evidence fields (`chart_readonly_sources`, `chart_stale_source_evidence`, and `chart_context_prompt`) plus cockpit visibility.
|
||||
- Added continuous workspace rail fields (`continuous_workspace_rail` and `continuous_workspace_status`) linking Watchlist, Evidence, Notes, Digest, and Replay.
|
||||
- Added weekly trend breakdown fields (`weekly_trend_breakdown`) and digest history review-back actions (`digest_review_back_actions`, `next_digest_review_back_action`) plus cockpit visibility.
|
||||
- Passed focused validation: `unittest tests.unit.test_operator_workspace_service`, `node --check src/web/app.js`, `unittest tests.unit.test_operator_workspace_service tests.unit.test_api_static_server tests.unit.test_api_server`, full `unittest discover -s tests/unit` (713 tests), and `scripts/run_script_static_checks.py`.
|
||||
- Deployed the current working tree to `http://192.168.3.90:8000`; deploy smoke passed 11 endpoints, `run_remote_ops_smoke.py` passed 9 checks, and remote field smoke confirmed chart inventory, continuous workspace rail, weekly trend breakdown, and digest review-back fields.
|
||||
- Added handoff cleanup-action readiness / resume backlog grouping fields (`cleanup_action_readiness`, `cleanup_action_ready_count`, `resume_backlog_group`, `resume_backlog_groups`, and `next_cleanup_action_prompt`) plus cockpit visibility.
|
||||
- Added digest proof-SLA backlog routing / freshness-debt summary fields (`proof_sla_backlog_route`, `proof_sla_backlog_priority`, `freshness_debt_score`, `proof_sla_backlog_queue`, and `freshness_debt_summary`) plus cockpit visibility.
|
||||
- Added template recovery shortcuts / audit lane grouping fields (`recovery_action_shortcuts`, `audit_lane_group`, `audit_lane_rank`, `template_audit_lane_groups`, and `next_recovery_action_shortcut`) plus cockpit visibility.
|
||||
@@ -555,7 +568,7 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
|
||||
## Current Risks Or Open Questions
|
||||
|
||||
- Remote deploy is no longer blocked, but the current assistant still lacks richer external source coverage beyond the existing news/provider chain and council watch items.
|
||||
- Remote deploy is no longer blocked. The remaining true external blocker is a real read-only external chart source URL/provider/access pattern; current E6 exposes inventory and stale-source evidence but cannot fetch an external chart without that input.
|
||||
- Browser screenshot capture adapter is a future enhancement, not a P8 closure blocker. The current adapter is an advisory review-packet HTML capture, and the review loop can still use an existing snapshot, an explicit `image_path`, or write an `unavailable` structured review.
|
||||
- Visual review remains advisory and must not change execution permission, live readiness, trust bucket, risk governor, or operator confirmation.
|
||||
- Alert-quality statistics remain advisory and must not change execution permission, live readiness, trust bucket, risk governor, or operator confirmation.
|
||||
@@ -606,11 +619,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- Latest H17/F18/G17/D19 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke returned `status=passed`.
|
||||
- Latest H19/F20/G19/D21 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke confirmed cleanup execution, debt trend, shortcut telemetry, and evidence aging fields.
|
||||
- Latest E5/W1/D22/F21 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke confirmed external calendar, workflow rhythm, learning attribution, and high-value digest fields.
|
||||
- Latest E6/H20/D23/F22 validation passed: 713 unit tests, 73 script static checks, `node --check src/web/app.js`, deploy smoke passed 11 endpoints, remote ops smoke passed 9 checks, and remote field smoke confirmed chart inventory, continuous workspace, weekly trend, and digest review-back fields.
|
||||
- Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter.
|
||||
|
||||
## Exact Next Step
|
||||
|
||||
Continue the Decision Assistant dense operator workspace with the next bounded non-trading slice: E6 external chart read-only source inventory / stale-source evidence, H20 continuous workspace rail linking Watchlist / Evidence / Notes / Digest / Replay, D23 weekly trend learning summary deepening, and F22 digest history review-back ergonomics. PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
|
||||
Blocked on real external input for the next external-chart step: provide a read-only chart source URL/provider/access pattern if the inventory should become an actual external chart adapter. Until that is provided, continue only internal non-trading polishing such as H21 cockpit layout refinement or F23 digest history browsing. PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
|
||||
|
||||
## Validation Commands Used Recently
|
||||
|
||||
|
||||
@@ -139,10 +139,11 @@
|
||||
- [x] W1 pre/intraday/post workflow rhythm checklist
|
||||
- [x] D22 replay learning failure/missed-opportunity attribution rollup
|
||||
- [x] F21 personalized digest high-value filter / importance template
|
||||
- [ ] E6 external chart read-only source inventory / stale-source evidence
|
||||
- [ ] H20 continuous workspace rail linking Watchlist / Evidence / Notes / Digest / Replay
|
||||
- [ ] D23 weekly trend learning summary deepening
|
||||
- [ ] F22 digest history review-back ergonomics
|
||||
- [x] E6 external chart read-only source inventory / stale-source evidence
|
||||
- [x] H20 continuous workspace rail linking Watchlist / Evidence / Notes / Digest / Replay
|
||||
- [x] D23 weekly trend learning summary deepening
|
||||
- [x] F22 digest history review-back ergonomics
|
||||
- [ ] Blocked external input: provide read-only external chart source URL/provider/access pattern for real external chart adapter
|
||||
- [x] P8 local closure complete
|
||||
- [x] P9 local closure complete
|
||||
- [x] P10 local closure complete
|
||||
@@ -339,10 +340,14 @@
|
||||
- [x] latest_decision_assistant_workflow_rhythm_status: implemented
|
||||
- [x] latest_decision_assistant_learning_attribution_status: implemented
|
||||
- [x] latest_decision_assistant_digest_high_value_status: implemented
|
||||
- [x] latest_decision_assistant_chart_source_status: implemented_inventory_only_external_url_missing
|
||||
- [x] latest_decision_assistant_continuous_workspace_status: implemented
|
||||
- [x] latest_decision_assistant_weekly_trend_status: implemented
|
||||
- [x] latest_decision_assistant_digest_review_back_status: implemented
|
||||
- [x] latest_decision_assistant_workbench_status: visible_local_smoke
|
||||
- [x] latest_remote_decision_assistant_deploy_status: deployed_current_head
|
||||
- [x] latest_remote_decision_assistant_smoke_status: passed
|
||||
- [x] latest_remote_decision_assistant_review_packet_status: external_calendar_workflow_rhythm_learning_attribution_high_value_digest_visible
|
||||
- [x] latest_remote_decision_assistant_review_packet_status: chart_inventory_continuous_workspace_weekly_trend_digest_review_back_visible
|
||||
- [x] latest_p7_selector_choice: continue_periodic_analysis_decision_monitoring_with_maturity_rollup
|
||||
- [x] latest_p14_slice: webhook_transport_retry
|
||||
- [x] latest_p15_pxx_tasklist_status: ready
|
||||
@@ -643,7 +648,7 @@
|
||||
|
||||
## Blocked
|
||||
|
||||
- [x] none
|
||||
- [ ] External chart adapter is blocked until a real read-only chart source URL/provider/access pattern is provided.
|
||||
|
||||
## Standing Guardrails
|
||||
|
||||
|
||||
@@ -443,6 +443,13 @@ def build_decision_assistant_summary(
|
||||
)
|
||||
scan_completion_summary = _build_decision_assistant_scan_completion_summary(scan_summary_cards)
|
||||
scan_handoff_summary = _build_decision_assistant_scan_handoff_summary(scan_summary_cards)
|
||||
continuous_workspace_rail = _build_decision_assistant_continuous_workspace_rail(
|
||||
candidate_watchlist_summary=candidate_watchlist_summary,
|
||||
evidence_chain=evidence_chain,
|
||||
review_entrypoints=review_entrypoints,
|
||||
digest_surface=digest_surface,
|
||||
setup_learning_rollup=setup_learning_rollup,
|
||||
)
|
||||
return {
|
||||
"instrument_id": instrument_id,
|
||||
"timeframe": timeframe,
|
||||
@@ -477,6 +484,8 @@ def build_decision_assistant_summary(
|
||||
"scan_summary_cards": scan_summary_cards,
|
||||
"scan_completion_summary": scan_completion_summary,
|
||||
"scan_handoff_summary": scan_handoff_summary,
|
||||
"continuous_workspace_rail": continuous_workspace_rail,
|
||||
"continuous_workspace_status": _continuous_workspace_status(continuous_workspace_rail),
|
||||
"no_trade_reason_items": no_trade_reason_items,
|
||||
"no_trade_reasons": no_trade_reasons,
|
||||
"next_actions": next_actions,
|
||||
@@ -495,6 +504,63 @@ def build_decision_assistant_summary(
|
||||
}
|
||||
|
||||
|
||||
def _build_decision_assistant_continuous_workspace_rail(
|
||||
*,
|
||||
candidate_watchlist_summary: dict,
|
||||
evidence_chain: list[dict],
|
||||
review_entrypoints: dict,
|
||||
digest_surface: dict,
|
||||
setup_learning_rollup: dict,
|
||||
) -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"section": "Watchlist",
|
||||
"target_section": "watchlist",
|
||||
"status": "active" if int(candidate_watchlist_summary.get("review_now_count") or 0) else "watch",
|
||||
"summary": f"Review now {candidate_watchlist_summary.get('review_now_count') or 0}; strong watch {candidate_watchlist_summary.get('strong_watch_count') or 0}.",
|
||||
"next_action": "Open Watchlist",
|
||||
},
|
||||
{
|
||||
"section": "Evidence",
|
||||
"target_section": "evidence",
|
||||
"status": "active" if evidence_chain else "idle",
|
||||
"summary": f"{len(evidence_chain)} evidence block(s) linked to the current decision context.",
|
||||
"next_action": "Open Evidence",
|
||||
},
|
||||
{
|
||||
"section": "Notes",
|
||||
"target_section": "notes",
|
||||
"status": review_entrypoints.get("status") or "idle",
|
||||
"summary": f"{review_entrypoints.get('note_count') or 0} note(s), {review_entrypoints.get('journal_entry_count') or 0} journal entry(s).",
|
||||
"next_action": "Open Notes",
|
||||
},
|
||||
{
|
||||
"section": "Digest",
|
||||
"target_section": "digest",
|
||||
"status": digest_surface.get("status") or "idle",
|
||||
"summary": f"{digest_surface.get('reminder_count') or 0} reminder(s), {digest_surface.get('high_value_reminder_count') or 0} high-value.",
|
||||
"next_action": "Open Digest",
|
||||
},
|
||||
{
|
||||
"section": "Replay",
|
||||
"target_section": "replay",
|
||||
"status": setup_learning_rollup.get("status") or "idle",
|
||||
"summary": setup_learning_rollup.get("weekly_trend_summary") or "Replay learning summary unavailable.",
|
||||
"next_action": "Open Replay",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _continuous_workspace_status(continuous_workspace_rail: list[dict]) -> str:
|
||||
if not continuous_workspace_rail:
|
||||
return "idle"
|
||||
if all(str(item.get("status") or "idle") in {"active", "complete"} for item in continuous_workspace_rail):
|
||||
return "continuous"
|
||||
if any(str(item.get("status") or "idle") == "active" for item in continuous_workspace_rail):
|
||||
return "partial"
|
||||
return "watch"
|
||||
|
||||
|
||||
def _build_market_posture(
|
||||
*,
|
||||
current_session: str,
|
||||
@@ -1060,6 +1126,12 @@ def _build_decision_assistant_external_context_summary(
|
||||
calendar_events=macro_news_calendar,
|
||||
news_window=news_window,
|
||||
)
|
||||
chart_readonly_sources = _build_external_context_chart_readonly_sources(
|
||||
news_window=news_window,
|
||||
exchange_status=exchange_status,
|
||||
announcements_snapshot=announcements_snapshot,
|
||||
)
|
||||
chart_stale_source_evidence = _build_external_context_chart_stale_source_evidence(chart_readonly_sources)
|
||||
return {
|
||||
"status": "active" if active_events or external_waiting_items or council_watch_items or operator_questions or next_confirmation_targets or exchange_events or announcements else "idle",
|
||||
"news_window_code": news_window.get("code") or "unknown",
|
||||
@@ -1083,6 +1155,11 @@ def _build_decision_assistant_external_context_summary(
|
||||
"high_volatility_window": bool(event_window_summary.get("high_volatility_window")),
|
||||
"low_liquidity_window": bool(event_window_summary.get("low_liquidity_window")),
|
||||
"exchange_anomaly_window": bool(event_window_summary.get("exchange_anomaly_window")),
|
||||
"chart_readonly_sources": chart_readonly_sources,
|
||||
"chart_readonly_source_count": len(chart_readonly_sources),
|
||||
"chart_stale_source_evidence": chart_stale_source_evidence,
|
||||
"chart_stale_source_count": len(chart_stale_source_evidence),
|
||||
"chart_context_prompt": _external_context_chart_context_prompt(chart_readonly_sources),
|
||||
"source_cards": [
|
||||
{
|
||||
"source_type": "news_provider",
|
||||
@@ -1104,6 +1181,11 @@ def _build_decision_assistant_external_context_summary(
|
||||
"title": "macro_news_calendar",
|
||||
"detail": event_window_summary.get("status") or "unknown",
|
||||
},
|
||||
{
|
||||
"source_type": "chart_readonly_inventory",
|
||||
"title": "chart_readonly_sources",
|
||||
"detail": f"sources={len(chart_readonly_sources)} stale={len(chart_stale_source_evidence)}",
|
||||
},
|
||||
*[
|
||||
{
|
||||
"source_type": "external_waiting_item",
|
||||
@@ -2381,6 +2463,74 @@ def _external_context_event_window_prompt(*, status: str, next_event: dict) -> s
|
||||
return f"Review {title} as read-only external context before the next decision note."
|
||||
|
||||
|
||||
def _build_external_context_chart_readonly_sources(
|
||||
*,
|
||||
news_window: dict,
|
||||
exchange_status: dict,
|
||||
announcements_snapshot: dict,
|
||||
) -> list[dict]:
|
||||
sources = [
|
||||
{
|
||||
"source_id": "internal_review_packet_chart",
|
||||
"source_type": "internal_chart_context",
|
||||
"status": "available",
|
||||
"freshness_status": news_window.get("freshness_code") or "unknown",
|
||||
"stale_source_evidence": None,
|
||||
"prompt": "Use the internal review packet chart context as the primary read-only chart source.",
|
||||
},
|
||||
{
|
||||
"source_id": "exchange_status_context",
|
||||
"source_type": "exchange_status_context",
|
||||
"status": exchange_status.get("status") or "unknown",
|
||||
"freshness_status": "source_current" if exchange_status.get("status") == "active" else "source_degraded",
|
||||
"stale_source_evidence": exchange_status.get("error") if exchange_status.get("status") == "error" else None,
|
||||
"prompt": "Use exchange status only as context, not execution permission.",
|
||||
},
|
||||
{
|
||||
"source_id": "announcement_context",
|
||||
"source_type": "announcement_context",
|
||||
"status": announcements_snapshot.get("status") or "unknown",
|
||||
"freshness_status": "source_current" if announcements_snapshot.get("status") == "active" else "source_degraded",
|
||||
"stale_source_evidence": announcements_snapshot.get("error") if announcements_snapshot.get("status") == "error" else None,
|
||||
"prompt": "Use announcements as read-only chart context when evaluating event windows.",
|
||||
},
|
||||
{
|
||||
"source_id": "external_chart_adapter",
|
||||
"source_type": "external_chart_readonly",
|
||||
"status": "not_configured",
|
||||
"freshness_status": "missing_source",
|
||||
"stale_source_evidence": "external_chart_readonly_url_missing",
|
||||
"prompt": "Configure a read-only chart URL before relying on external chart screenshots.",
|
||||
},
|
||||
]
|
||||
return sources
|
||||
|
||||
|
||||
def _build_external_context_chart_stale_source_evidence(chart_readonly_sources: list[dict]) -> list[dict]:
|
||||
stale = []
|
||||
for source in chart_readonly_sources:
|
||||
freshness = str(source.get("freshness_status") or "")
|
||||
status = str(source.get("status") or "")
|
||||
if freshness in {"missing_source", "source_degraded"} or status in {"error", "not_configured"}:
|
||||
stale.append(
|
||||
{
|
||||
"source_id": source.get("source_id"),
|
||||
"source_type": source.get("source_type"),
|
||||
"status": status,
|
||||
"freshness_status": freshness,
|
||||
"evidence": source.get("stale_source_evidence") or "source_not_current",
|
||||
}
|
||||
)
|
||||
return stale
|
||||
|
||||
|
||||
def _external_context_chart_context_prompt(chart_readonly_sources: list[dict]) -> str:
|
||||
stale_count = len(_build_external_context_chart_stale_source_evidence(chart_readonly_sources))
|
||||
if stale_count:
|
||||
return f"{stale_count} chart context source(s) need freshness review before use."
|
||||
return "All chart context sources are available for read-only analysis."
|
||||
|
||||
|
||||
def _build_decision_assistant_personalized_reminders(
|
||||
*,
|
||||
top_opportunities: list[dict],
|
||||
@@ -2463,6 +2613,10 @@ def _build_decision_assistant_digest_surface(
|
||||
for item in personalized_reminders
|
||||
if bool(item.get("high_value")) or int(item.get("importance_score") or 0) >= 70
|
||||
]
|
||||
digest_review_back_actions = _build_digest_review_back_actions(
|
||||
digest_history_preview=digest_history_preview,
|
||||
digest_history_summary=digest_history_summary,
|
||||
)
|
||||
return {
|
||||
"status": "active" if personalized_reminders or top_opportunities else "idle",
|
||||
"focus_mode": focus_mode,
|
||||
@@ -2473,6 +2627,9 @@ def _build_decision_assistant_digest_surface(
|
||||
"important_change_filter": _digest_important_change_filter(personalized_reminders),
|
||||
"importance_templates": _digest_importance_templates(),
|
||||
"next_high_value_change": high_value_reminders[0] if high_value_reminders else None,
|
||||
"digest_review_back_actions": digest_review_back_actions[:6],
|
||||
"digest_review_back_action_count": len(digest_review_back_actions),
|
||||
"next_digest_review_back_action": digest_review_back_actions[0] if digest_review_back_actions else None,
|
||||
"top_opportunity_title": top_opportunities[0].get("title") if top_opportunities else None,
|
||||
"learning_status": learning_rollup.get("status") or "idle",
|
||||
"history_count": len(digest_history_preview),
|
||||
@@ -2544,6 +2701,41 @@ def _digest_importance_templates() -> list[dict]:
|
||||
]
|
||||
|
||||
|
||||
def _build_digest_review_back_actions(*, digest_history_preview: list[dict], digest_history_summary: dict) -> list[dict]:
|
||||
actions = []
|
||||
recommended_kind = str(digest_history_summary.get("recommended_kind") or "")
|
||||
if recommended_kind:
|
||||
actions.append(
|
||||
{
|
||||
"action_id": f"review_history:{recommended_kind}",
|
||||
"label": f"Review {recommended_kind}",
|
||||
"kind": recommended_kind,
|
||||
"priority": "high",
|
||||
"prompt": f"Open the latest {recommended_kind} digest group before continuing.",
|
||||
}
|
||||
)
|
||||
for item in digest_history_preview[:5]:
|
||||
kind = str(item.get("kind") or "history")
|
||||
if kind == recommended_kind:
|
||||
continue
|
||||
actions.append(
|
||||
{
|
||||
"action_id": f"review_history:{kind}",
|
||||
"label": f"Review {kind}",
|
||||
"kind": kind,
|
||||
"priority": "medium" if kind in {"alert", "note", "previous_packet"} else "low",
|
||||
"prompt": f"Review {item.get('title') or kind} from digest history.",
|
||||
}
|
||||
)
|
||||
actions.sort(
|
||||
key=lambda row: (
|
||||
{"high": 1, "medium": 2, "low": 3}.get(str(row.get("priority") or "low"), 3),
|
||||
str(row.get("kind") or ""),
|
||||
)
|
||||
)
|
||||
return actions
|
||||
|
||||
|
||||
def _build_decision_assistant_digest_history_preview(
|
||||
*,
|
||||
personalized_reminders: list[dict],
|
||||
@@ -3649,6 +3841,7 @@ def _build_decision_assistant_setup_learning_rollup(
|
||||
"failure_reason_attribution": learning_attribution_summary.get("failure_reason_attribution") or [],
|
||||
"missed_opportunity_attribution": learning_attribution_summary.get("missed_opportunity_attribution") or [],
|
||||
"setup_session_market_state_stats": learning_attribution_summary.get("setup_session_market_state_stats") or [],
|
||||
"weekly_trend_breakdown": learning_attribution_summary.get("weekly_trend_breakdown") or {},
|
||||
"weekly_trend_summary": learning_attribution_summary.get("weekly_trend_summary"),
|
||||
}
|
||||
|
||||
@@ -3680,6 +3873,7 @@ def _build_setup_learning_attribution_summary(
|
||||
setup_type=setup_type,
|
||||
session_performance=session_performance,
|
||||
)
|
||||
weekly_trend_breakdown = _build_setup_weekly_trend_breakdown(setup_session_market_state_stats)
|
||||
weekly_trend_summary = _build_setup_weekly_trend_summary(
|
||||
setup_type=setup_type,
|
||||
session_code=session_code,
|
||||
@@ -3692,6 +3886,7 @@ def _build_setup_learning_attribution_summary(
|
||||
"failure_reason_attribution": failure_reason_attribution,
|
||||
"missed_opportunity_attribution": missed_opportunity_attribution,
|
||||
"setup_session_market_state_stats": setup_session_market_state_stats,
|
||||
"weekly_trend_breakdown": weekly_trend_breakdown,
|
||||
"weekly_trend_summary": weekly_trend_summary,
|
||||
"failure_reason_count": len(failure_reason_attribution),
|
||||
"missed_opportunity_count": len(missed_opportunity_attribution),
|
||||
@@ -3868,6 +4063,39 @@ def _build_setup_weekly_trend_summary(
|
||||
)
|
||||
|
||||
|
||||
def _build_setup_weekly_trend_breakdown(setup_session_market_state_stats: list[dict]) -> dict:
|
||||
if not setup_session_market_state_stats:
|
||||
return {
|
||||
"status": "idle",
|
||||
"best_session": None,
|
||||
"weakest_session": None,
|
||||
"liquid_session_count": 0,
|
||||
"thin_session_count": 0,
|
||||
}
|
||||
best_session = dict(setup_session_market_state_stats[0] or {})
|
||||
weakest_session = dict(
|
||||
sorted(
|
||||
setup_session_market_state_stats,
|
||||
key=lambda row: (
|
||||
float(row.get("framework_execution_rate") or 0.0),
|
||||
float(row.get("avg_r_multiple") or 0.0),
|
||||
-int(row.get("risk_violation_count") or 0),
|
||||
),
|
||||
)[0]
|
||||
)
|
||||
liquid_count = sum(1 for row in setup_session_market_state_stats if row.get("market_state") == "liquid_session")
|
||||
thin_count = sum(1 for row in setup_session_market_state_stats if row.get("market_state") == "thin_or_transition_session")
|
||||
return {
|
||||
"status": "active",
|
||||
"best_session": best_session.get("session_code"),
|
||||
"weakest_session": weakest_session.get("session_code"),
|
||||
"liquid_session_count": liquid_count,
|
||||
"thin_session_count": thin_count,
|
||||
"best_framework_rate": best_session.get("framework_execution_rate"),
|
||||
"weakest_framework_rate": weakest_session.get("framework_execution_rate"),
|
||||
}
|
||||
|
||||
|
||||
def _build_decision_assistant_scan_summary_cards(
|
||||
*,
|
||||
top_opportunities: list[dict],
|
||||
|
||||
@@ -471,6 +471,7 @@ function renderDecisionAssistant(summary) {
|
||||
const digestHistoryGroups = summary.digest_history_groups || [];
|
||||
const digestSurface = summary.digest_surface || null;
|
||||
const setupLearningRollup = summary.setup_learning_rollup || null;
|
||||
const continuousWorkspaceRail = summary.continuous_workspace_rail || [];
|
||||
const savedHandoffAge = resolveAssistantHandoffAge(assistantScanHandoffMarker, scanHandoffSummary?.default_stale_after_seconds);
|
||||
const assistantSections = [
|
||||
scanSummaryCards.length ? { title: 'Scan' } : null,
|
||||
@@ -544,6 +545,15 @@ function renderDecisionAssistant(summary) {
|
||||
scanGrid.appendChild(card);
|
||||
});
|
||||
decisionAssistantPanel.appendChild(scanGrid);
|
||||
if (continuousWorkspaceRail.length) {
|
||||
decisionAssistantPanel.appendChild(
|
||||
buildInfoGrid([
|
||||
['continuous workspace', summary.continuous_workspace_status || 'idle'],
|
||||
['workspace rail items', String(continuousWorkspaceRail.length)],
|
||||
['workspace rail', formatList(continuousWorkspaceRail.map((item) => `${item.section}: ${item.status}`))],
|
||||
])
|
||||
);
|
||||
}
|
||||
if (scanCompletionSummary) {
|
||||
decisionAssistantPanel.appendChild(
|
||||
buildInfoGrid([
|
||||
@@ -713,6 +723,9 @@ function renderDecisionAssistant(summary) {
|
||||
['important change filter', `${digestSurface.important_change_filter?.mode || 'n/a'} / ${digestSurface.important_change_filter?.threshold ?? 'n/a'}`],
|
||||
['importance templates', formatJsonInline(digestSurface.important_change_filter?.template_counts || {})],
|
||||
['next high value change', digestSurface.next_high_value_change?.title || 'n/a'],
|
||||
['review-back actions', String(digestSurface.digest_review_back_action_count ?? 0)],
|
||||
['next review-back', digestSurface.next_digest_review_back_action?.label || 'n/a'],
|
||||
['review-back prompts', formatList((digestSurface.digest_review_back_actions || []).map((item) => item.prompt || item.label))],
|
||||
['digest history', String(digestSurface.history_count ?? 0)],
|
||||
['digest kinds', formatList(digestSurface.history_summary?.kinds || [])],
|
||||
['recommended history kind', digestSurface.history_summary?.recommended_kind || 'n/a'],
|
||||
@@ -1254,6 +1267,10 @@ function renderDecisionAssistant(summary) {
|
||||
['high volatility window', String(Boolean(externalContext.high_volatility_window))],
|
||||
['low liquidity window', String(Boolean(externalContext.low_liquidity_window))],
|
||||
['exchange anomaly window', String(Boolean(externalContext.exchange_anomaly_window))],
|
||||
['chart sources', String(externalContext.chart_readonly_source_count ?? 0)],
|
||||
['stale chart sources', String(externalContext.chart_stale_source_count ?? 0)],
|
||||
['chart context prompt', externalContext.chart_context_prompt || 'n/a'],
|
||||
['chart stale evidence', formatList((externalContext.chart_stale_source_evidence || []).map((item) => `${item.source_id}: ${item.evidence}`))],
|
||||
['exchange status', `${externalContext.exchange_status?.provider_name || 'unknown'} / ${externalContext.exchange_status?.status || 'unknown'}`],
|
||||
['exchange events', formatList(externalContext.exchange_status_titles || [])],
|
||||
['announcements', `${externalContext.announcements_snapshot?.provider_name || 'unknown'} / ${externalContext.announcements_snapshot?.status || 'unknown'}`],
|
||||
@@ -1299,6 +1316,25 @@ function renderDecisionAssistant(summary) {
|
||||
});
|
||||
decisionAssistantPanel.appendChild(calendarGrid);
|
||||
}
|
||||
const chartSources = externalContext.chart_readonly_sources || [];
|
||||
if (chartSources.length) {
|
||||
const chartGrid = createElement('div', 'cards assistant-tight-grid');
|
||||
chartSources.forEach((item) => {
|
||||
chartGrid.appendChild(
|
||||
buildItemCard(
|
||||
item.source_id || 'chart source',
|
||||
item.status || 'source',
|
||||
[
|
||||
['type', item.source_type || 'n/a'],
|
||||
['freshness', item.freshness_status || 'n/a'],
|
||||
['stale evidence', item.stale_source_evidence || 'n/a'],
|
||||
['prompt', item.prompt || 'n/a'],
|
||||
]
|
||||
)
|
||||
);
|
||||
});
|
||||
decisionAssistantPanel.appendChild(chartGrid);
|
||||
}
|
||||
}
|
||||
if (setupLearningRollup) {
|
||||
appendSectionTitle(decisionAssistantPanel, 'Setup Replay');
|
||||
@@ -1312,6 +1348,9 @@ function renderDecisionAssistant(summary) {
|
||||
['failure attributions', String(setupLearningRollup.learning_attribution_summary?.failure_reason_count ?? 0)],
|
||||
['missed opportunities', String(setupLearningRollup.learning_attribution_summary?.missed_opportunity_count ?? 0)],
|
||||
['weekly trend summary', setupLearningRollup.weekly_trend_summary || 'n/a'],
|
||||
['weekly best session', setupLearningRollup.weekly_trend_breakdown?.best_session || 'n/a'],
|
||||
['weekly weakest session', setupLearningRollup.weekly_trend_breakdown?.weakest_session || 'n/a'],
|
||||
['weekly liquid/thin', `${setupLearningRollup.weekly_trend_breakdown?.liquid_session_count ?? 0} / ${setupLearningRollup.weekly_trend_breakdown?.thin_session_count ?? 0}`],
|
||||
['failure reasons', formatList((setupLearningRollup.failure_reason_attribution || []).map((item) => `${item.reason}: ${item.severity}`))],
|
||||
['missed opportunity reasons', formatList((setupLearningRollup.missed_opportunity_attribution || []).map((item) => `${item.session_code}: ${item.reason}`))],
|
||||
['setup/session stats', formatList((setupLearningRollup.setup_session_market_state_stats || []).map((item) => `${item.session_code}/${item.market_state}: ${formatPercent(item.framework_execution_rate)}`))],
|
||||
|
||||
@@ -172,6 +172,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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("continuous_workspace_rail", summary)
|
||||
self.assertIn("continuous_workspace_status", summary)
|
||||
self.assertTrue(summary["continuous_workspace_rail"])
|
||||
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])
|
||||
@@ -311,6 +314,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
self.assertIn("important_change_filter", summary["digest_surface"])
|
||||
self.assertIn("importance_templates", 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("refresh_prompts", summary["digest_surface"]["history_summary"])
|
||||
self.assertIn("digest_history_groups", summary)
|
||||
self.assertIn("group_status", summary["digest_history_groups"][0])
|
||||
@@ -362,6 +368,7 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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.assertGreaterEqual(summary["setup_learning_rollup"]["learning_attribution_summary"]["failure_reason_count"], 1)
|
||||
self.assertIn("sample_count", summary["setup_learning_rollup"]["replay_statistics"])
|
||||
@@ -513,6 +520,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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.assertGreaterEqual(summary["external_context_summary"]["chart_stale_source_count"], 1)
|
||||
self.assertIn("CPI", summary["external_context_summary"]["headline"])
|
||||
self.assertTrue(summary["no_trade_reason_items"])
|
||||
self.assertIn("watch_london_open", summary["next_actions"])
|
||||
|
||||
Reference in New Issue
Block a user