Add watchlist and review continuity batch
This commit is contained in:
+10
-3
@@ -2,7 +2,7 @@
|
||||
|
||||
## Current Goal
|
||||
|
||||
Automation execution-layer PXX remains closed and its known scope stays frozen. Under explicit user direction, the active product phase is a dense `Decision Assistant` operator workspace with grouped recall, broader replay baselines, note templates, quick-save shortcuts, read-only market context, and a continuous operator cockpit. This run closed W4/D25/F25/H25 as one internal batch: workflow phase artifacts now summarize pre/intraday/post handoff state, digest carries a prioritized action queue, replay learning exposes a session/market-state focus matrix, and workspace continuity now shows ready/watch/loop handoff context. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
|
||||
Automation execution-layer PXX remains closed and its known scope stays frozen. Under explicit user direction, the active product phase is a dense `Decision Assistant` operator workspace with grouped recall, broader replay baselines, note templates, quick-save shortcuts, read-only market context, and a continuous operator cockpit. This run closed C3/G20/H26 as one internal batch: watchlist now exposes a focus matrix and next-focus prompt, review entry now exposes a prioritized review-action queue and handoff summary, and the main `assistant_loop_queue` now carries watchlist focus alongside review-entry actions. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
|
||||
|
||||
## Current Branch
|
||||
|
||||
@@ -126,6 +126,9 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_digest_action_slice: digest_action_queue
|
||||
- latest_decision_assistant_learning_matrix_slice: learning_focus_matrix
|
||||
- latest_decision_assistant_workspace_handoff_slice: workspace_continuity_handoff
|
||||
- latest_decision_assistant_watchlist_focus_slice: watchlist_focus_matrix
|
||||
- latest_decision_assistant_review_action_slice: review_entry_action_queue
|
||||
- latest_decision_assistant_review_handoff_slice: review_entry_handoff_summary
|
||||
- latest_decision_assistant_summary_status: implemented
|
||||
- latest_decision_assistant_change_summary_status: implemented
|
||||
- latest_decision_assistant_evidence_status: implemented
|
||||
@@ -236,10 +239,13 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_digest_action_status: implemented
|
||||
- latest_decision_assistant_learning_matrix_status: implemented
|
||||
- latest_decision_assistant_workspace_handoff_status: implemented
|
||||
- latest_decision_assistant_watchlist_focus_status: implemented
|
||||
- latest_decision_assistant_review_action_status: implemented
|
||||
- latest_decision_assistant_review_handoff_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: workflow_digest_learning_workspace_batch_visible
|
||||
- latest_remote_decision_assistant_review_packet_status: watchlist_review_loop_batch_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
|
||||
@@ -654,11 +660,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- Latest D24/H24 validation passed: focused operator workspace tests, 68 related unit/API/static tests, 713 full 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 `learning_focus_queue`, `next_learning_focus_prompt`, and replay-learning presence inside `assistant_loop_queue`.
|
||||
- Latest E7 validation passed: focused operator workspace tests, 68 related unit/API/static tests, 713 full 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_adapter_mode=optional_reference`, `chart_primary_mode=internal_review_packet_html`, and a non-blocking external chart adapter even when other supporting context sources are stale.
|
||||
- Latest W4/D25/F25/H25 validation passed: focused operator workspace tests, 68 related unit/API/static tests, 713 full 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 `workflow_artifact_summary`, `digest_action_queue`, `learning_focus_matrix`, and `workspace_continuity_handoff` plus matching UI strings.
|
||||
- Latest C3/G20/H26 validation passed: focused operator workspace tests, 68 related unit/API/static tests, 713 full 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 `watchlist focus matrix`, `review_entry_action_queue`, `review_entry_handoff_summary`, and watchlist-focus presence inside `assistant_loop_queue`.
|
||||
- Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter.
|
||||
|
||||
## Exact Next Step
|
||||
|
||||
W4/D25/F25/H25 batch is closed. External chart URLs remain optional reference enhancements and are not required for the current analysis-only loop. Internal non-trading backlog still exists, but PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
|
||||
C3/G20/H26 batch is closed. External chart URLs remain optional reference enhancements and are not required for the current analysis-only loop. Internal non-trading backlog still exists, but PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
|
||||
|
||||
## Validation Commands Used Recently
|
||||
|
||||
|
||||
@@ -157,6 +157,9 @@
|
||||
- [x] D25 learning focus matrix slice
|
||||
- [x] F25 digest action queue slice
|
||||
- [x] H25 workspace continuity handoff slice
|
||||
- [x] C3 watchlist focus matrix / next-focus prompt slice
|
||||
- [x] G20 review entry action queue slice
|
||||
- [x] H26 review/watchlist continuity into loop queue slice
|
||||
- [x] P8 local closure complete
|
||||
- [x] P9 local closure complete
|
||||
- [x] P10 local closure complete
|
||||
@@ -276,6 +279,9 @@
|
||||
- [x] latest_decision_assistant_digest_action_slice: digest_action_queue
|
||||
- [x] latest_decision_assistant_learning_matrix_slice: learning_focus_matrix
|
||||
- [x] latest_decision_assistant_workspace_handoff_slice: workspace_continuity_handoff
|
||||
- [x] latest_decision_assistant_watchlist_focus_slice: watchlist_focus_matrix
|
||||
- [x] latest_decision_assistant_review_action_slice: review_entry_action_queue
|
||||
- [x] latest_decision_assistant_review_handoff_slice: review_entry_handoff_summary
|
||||
- [x] latest_decision_assistant_summary_status: implemented
|
||||
- [x] latest_decision_assistant_change_summary_status: implemented
|
||||
- [x] latest_decision_assistant_evidence_status: implemented
|
||||
@@ -383,10 +389,13 @@
|
||||
- [x] latest_decision_assistant_digest_action_status: implemented
|
||||
- [x] latest_decision_assistant_learning_matrix_status: implemented
|
||||
- [x] latest_decision_assistant_workspace_handoff_status: implemented
|
||||
- [x] latest_decision_assistant_watchlist_focus_status: implemented
|
||||
- [x] latest_decision_assistant_review_action_status: implemented
|
||||
- [x] latest_decision_assistant_review_handoff_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: workflow_digest_learning_workspace_batch_visible
|
||||
- [x] latest_remote_decision_assistant_review_packet_status: watchlist_review_loop_batch_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
|
||||
@@ -696,6 +705,8 @@
|
||||
- [x] Deployed E7 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet field smoke for `chart_adapter_mode=optional_reference`, `chart_primary_mode=internal_review_packet_html`, and a non-blocking external chart adapter even when supporting context sources are stale
|
||||
- [x] Completed W4/D25/F25/H25 focused validation: operator workspace tests, 68 related unit/API/static tests, 713 full unit tests, 73 script static checks, and dashboard JS syntax
|
||||
- [x] Deployed W4/D25/F25/H25 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet/static field smoke for `workflow_artifact_summary`, `digest_action_queue`, `learning_focus_matrix`, and `workspace_continuity_handoff`
|
||||
- [x] Completed C3/G20/H26 focused validation: operator workspace tests, 68 related unit/API/static tests, 713 full unit tests, 73 script static checks, and dashboard JS syntax
|
||||
- [x] Deployed C3/G20/H26 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet/static field smoke for `watchlist focus matrix`, `review_entry_action_queue`, `review_entry_handoff_summary`, and watchlist-focus presence inside `assistant_loop_queue`
|
||||
|
||||
## Blocked
|
||||
|
||||
|
||||
@@ -467,6 +467,7 @@ def build_decision_assistant_summary(
|
||||
digest_history_browser=digest_history_browser,
|
||||
review_entrypoints=review_entrypoints,
|
||||
setup_learning_rollup=setup_learning_rollup,
|
||||
candidate_watchlist_summary=candidate_watchlist_summary,
|
||||
)
|
||||
assistant_loop_summary = _build_decision_assistant_loop_summary(assistant_loop_queue)
|
||||
workspace_continuity_handoff = _build_decision_assistant_workspace_continuity_handoff(
|
||||
@@ -697,6 +698,7 @@ def _build_decision_assistant_loop_queue(
|
||||
digest_history_browser: dict,
|
||||
review_entrypoints: dict,
|
||||
setup_learning_rollup: dict,
|
||||
candidate_watchlist_summary: dict,
|
||||
) -> list[dict]:
|
||||
queue = []
|
||||
if workflow_session_completion_summary.get("next_task_label"):
|
||||
@@ -725,6 +727,20 @@ def _build_decision_assistant_loop_queue(
|
||||
"priority_rank": 2,
|
||||
}
|
||||
)
|
||||
next_watchlist_focus = dict(candidate_watchlist_summary.get("next_watchlist_focus") or {})
|
||||
if next_watchlist_focus.get("title"):
|
||||
queue.append(
|
||||
{
|
||||
"queue_id": "watchlist_focus",
|
||||
"lane": "watchlist",
|
||||
"title": str(next_watchlist_focus.get("title") or "Watchlist"),
|
||||
"status": str(candidate_watchlist_summary.get("focus_matrix", {}).get("status") or "active"),
|
||||
"target_section": "watchlist-review-now",
|
||||
"action_label": "Open Watchlist",
|
||||
"prompt": str(candidate_watchlist_summary.get("next_watchlist_focus_prompt") or "Continue watchlist review."),
|
||||
"priority_rank": 3,
|
||||
}
|
||||
)
|
||||
if continuous_workspace_summary.get("next_section"):
|
||||
queue.append(
|
||||
{
|
||||
@@ -735,7 +751,7 @@ def _build_decision_assistant_loop_queue(
|
||||
"target_section": str(continuous_workspace_summary.get("next_target_section") or "workspace-rail"),
|
||||
"action_label": str(continuous_workspace_summary.get("next_action") or "Open Workspace"),
|
||||
"prompt": str(continuous_workspace_summary.get("next_handoff_prompt") or "Continue the workspace rail."),
|
||||
"priority_rank": 3,
|
||||
"priority_rank": 4,
|
||||
}
|
||||
)
|
||||
next_browser_action = dict(digest_history_browser.get("next_action") or {})
|
||||
@@ -749,7 +765,7 @@ def _build_decision_assistant_loop_queue(
|
||||
"target_section": "digest",
|
||||
"action_label": "Open Digest",
|
||||
"prompt": str(next_browser_action.get("prompt") or "Continue digest review."),
|
||||
"priority_rank": 4,
|
||||
"priority_rank": 5,
|
||||
}
|
||||
)
|
||||
next_learning_focus = dict(setup_learning_rollup.get("next_learning_focus") or {})
|
||||
@@ -763,20 +779,21 @@ def _build_decision_assistant_loop_queue(
|
||||
"target_section": "setup-replay",
|
||||
"action_label": "Open Replay",
|
||||
"prompt": str(setup_learning_rollup.get("next_learning_focus_prompt") or next_learning_focus.get("prompt") or "Continue replay learning."),
|
||||
"priority_rank": 5,
|
||||
"priority_rank": 6,
|
||||
}
|
||||
)
|
||||
if review_entrypoints.get("next_review_action"):
|
||||
next_review_entry_action = dict(review_entrypoints.get("next_review_entry_action") or {})
|
||||
if next_review_entry_action.get("label") or review_entrypoints.get("next_review_action"):
|
||||
queue.append(
|
||||
{
|
||||
"queue_id": "review_entry",
|
||||
"lane": "review",
|
||||
"title": str(review_entrypoints.get("template_summary", {}).get("recommended_label") or "Review Entry"),
|
||||
"title": str(next_review_entry_action.get("label") or review_entrypoints.get("template_summary", {}).get("recommended_label") or "Review Entry"),
|
||||
"status": str(review_entrypoints.get("status") or "active"),
|
||||
"target_section": "review-entry",
|
||||
"action_label": "Open Review",
|
||||
"prompt": str(review_entrypoints.get("next_review_action") or "Open review entry."),
|
||||
"priority_rank": 6,
|
||||
"prompt": str(next_review_entry_action.get("prompt") or review_entrypoints.get("next_review_action") or "Open review entry."),
|
||||
"priority_rank": 7,
|
||||
}
|
||||
)
|
||||
if scan_handoff_summary.get("next_resume_prompt"):
|
||||
@@ -789,7 +806,7 @@ def _build_decision_assistant_loop_queue(
|
||||
"target_section": str(scan_handoff_summary.get("next_target_section") or "scan"),
|
||||
"action_label": "Open Handoff",
|
||||
"prompt": str(scan_handoff_summary.get("next_resume_prompt") or "Resume persisted handoff."),
|
||||
"priority_rank": 7,
|
||||
"priority_rank": 8,
|
||||
}
|
||||
)
|
||||
queue.sort(
|
||||
@@ -1034,12 +1051,63 @@ def _build_candidate_watchlist_tiers(*, candidates: list[dict], session_focus: d
|
||||
|
||||
|
||||
def _build_candidate_watchlist_summary(tiers: dict) -> dict:
|
||||
focus_matrix = _build_candidate_watchlist_focus_matrix(tiers)
|
||||
return {
|
||||
"review_now_count": len(list(tiers.get("review_now") or [])),
|
||||
"strong_watch_count": len(list(tiers.get("strong_watch") or [])),
|
||||
"soft_watch_count": len(list(tiers.get("soft_watch") or [])),
|
||||
"archive_only_count": len(list(tiers.get("archive_only") or [])),
|
||||
"top_review_candidate_id": (tiers.get("review_now") or [{}])[0].get("candidate_id") if list(tiers.get("review_now") or []) else None,
|
||||
"focus_matrix": focus_matrix,
|
||||
"next_watchlist_focus": focus_matrix.get("next_focus"),
|
||||
"next_watchlist_focus_prompt": focus_matrix.get("next_focus_prompt"),
|
||||
}
|
||||
|
||||
|
||||
def _build_candidate_watchlist_focus_matrix(tiers: dict) -> dict:
|
||||
tier_order = ["review_now", "strong_watch", "soft_watch", "archive_only"]
|
||||
rows = []
|
||||
for tier in tier_order:
|
||||
for item in list(tiers.get(tier) or []):
|
||||
rows.append(dict(item))
|
||||
if not rows:
|
||||
return {
|
||||
"status": "idle",
|
||||
"tier_counts": {},
|
||||
"session_counts": {},
|
||||
"recommended_tier": None,
|
||||
"recommended_session_code": None,
|
||||
"next_focus": None,
|
||||
"next_focus_prompt": None,
|
||||
}
|
||||
tier_counts: dict[str, int] = {}
|
||||
session_counts: dict[str, int] = {}
|
||||
for item in rows:
|
||||
tier = str(item.get("tier") or "archive_only")
|
||||
session_code = str(item.get("session_code") or "OFF_HOURS")
|
||||
tier_counts[tier] = int(tier_counts.get(tier) or 0) + 1
|
||||
session_counts[session_code] = int(session_counts.get(session_code) or 0) + 1
|
||||
next_focus = dict(
|
||||
sorted(
|
||||
rows,
|
||||
key=lambda row: (
|
||||
{"review_now": 1, "strong_watch": 2, "soft_watch": 3, "archive_only": 4}.get(str(row.get("tier") or "archive_only"), 5),
|
||||
-float(row.get("priority_score") or 0.0),
|
||||
str(row.get("session_code") or ""),
|
||||
),
|
||||
)[0]
|
||||
)
|
||||
return {
|
||||
"status": "active",
|
||||
"tier_counts": tier_counts,
|
||||
"session_counts": session_counts,
|
||||
"recommended_tier": next_focus.get("tier"),
|
||||
"recommended_session_code": next_focus.get("session_code"),
|
||||
"next_focus": next_focus,
|
||||
"next_focus_prompt": (
|
||||
f"Start with {next_focus.get('title') or 'candidate'} in {next_focus.get('session_code') or 'session'} "
|
||||
f"because it leads the {next_focus.get('tier') or 'watchlist'} tier."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -1737,6 +1805,16 @@ def _build_decision_assistant_review_entrypoints(
|
||||
item["guardrail_audit_filter_reason"] = outcome.get("guardrail_audit_filter_reason")
|
||||
item["guardrail_audit_filtered_trail"] = outcome.get("guardrail_audit_filtered_trail") or []
|
||||
item["guardrail_audit_filtered_count"] = outcome.get("guardrail_audit_filtered_count") or 0
|
||||
review_entry_action_queue = _build_review_entry_action_queue(
|
||||
note_templates=note_templates,
|
||||
template_summary=_build_review_entry_template_summary(note_templates, recommended_template_id=recommended_template_id),
|
||||
template_reuse_metrics=template_reuse_metrics,
|
||||
next_actions=next_actions,
|
||||
)
|
||||
review_entry_handoff_summary = _build_review_entry_handoff_summary(
|
||||
note_templates=note_templates,
|
||||
review_entry_action_queue=review_entry_action_queue,
|
||||
)
|
||||
return {
|
||||
"status": "active",
|
||||
"note_count": len(recent_notes),
|
||||
@@ -1749,6 +1827,10 @@ def _build_decision_assistant_review_entrypoints(
|
||||
"recommended_template_id": recommended_template_id,
|
||||
"template_summary": _build_review_entry_template_summary(note_templates, recommended_template_id=recommended_template_id),
|
||||
"template_reuse_metrics": template_reuse_metrics,
|
||||
"review_entry_action_queue": review_entry_action_queue[:6],
|
||||
"review_entry_action_queue_count": len(review_entry_action_queue),
|
||||
"next_review_entry_action": review_entry_action_queue[0] if review_entry_action_queue else None,
|
||||
"review_entry_handoff_summary": review_entry_handoff_summary,
|
||||
"actions": [
|
||||
{"action_id": "open_operator_notes", "label": "Open Notes"},
|
||||
{"action_id": "open_post_trade_review", "label": "Open Review"},
|
||||
@@ -2753,6 +2835,79 @@ def _build_review_entry_template_summary(note_templates: list[dict], *, recommen
|
||||
}
|
||||
|
||||
|
||||
def _build_review_entry_action_queue(
|
||||
*,
|
||||
note_templates: list[dict],
|
||||
template_summary: dict,
|
||||
template_reuse_metrics: dict,
|
||||
next_actions: list[str],
|
||||
) -> list[dict]:
|
||||
queue = []
|
||||
if template_summary.get("recommended_label"):
|
||||
queue.append(
|
||||
{
|
||||
"action_id": "use_recommended_template",
|
||||
"label": str(template_summary.get("recommended_label") or "Use recommended template"),
|
||||
"priority": str(template_summary.get("recommended_priority") or "medium"),
|
||||
"prompt": str(template_summary.get("recommended_reuse_hint") or template_summary.get("recommended_reason") or "Use the recommended template."),
|
||||
"source": "template_summary",
|
||||
}
|
||||
)
|
||||
feedback_loop = dict(template_reuse_metrics.get("feedback_loop_summary") or {})
|
||||
if feedback_loop.get("next_feedback_prompt"):
|
||||
queue.append(
|
||||
{
|
||||
"action_id": "capture_feedback",
|
||||
"label": "Capture Feedback",
|
||||
"priority": "medium",
|
||||
"prompt": str(feedback_loop.get("next_feedback_prompt") or "Capture the next template feedback signal."),
|
||||
"source": "feedback_loop",
|
||||
}
|
||||
)
|
||||
aggregation = dict(template_reuse_metrics.get("template_feedback_aggregation") or {})
|
||||
if aggregation.get("next_preference_recovery_prompt"):
|
||||
queue.append(
|
||||
{
|
||||
"action_id": "preference_recovery",
|
||||
"label": "Preference Recovery",
|
||||
"priority": "medium",
|
||||
"prompt": str(aggregation.get("next_preference_recovery_prompt") or "Review preference recovery evidence."),
|
||||
"source": "preference_recovery",
|
||||
}
|
||||
)
|
||||
if next_actions:
|
||||
queue.append(
|
||||
{
|
||||
"action_id": "weekly_follow_up",
|
||||
"label": "Weekly Follow-up",
|
||||
"priority": "low",
|
||||
"prompt": str(next_actions[0] or "Continue the weekly follow-up action."),
|
||||
"source": "weekly_review",
|
||||
}
|
||||
)
|
||||
queue.sort(
|
||||
key=lambda row: (
|
||||
{"high": 1, "medium": 2, "low": 3}.get(str(row.get("priority") or "low"), 4),
|
||||
str(row.get("source") or ""),
|
||||
)
|
||||
)
|
||||
return queue
|
||||
|
||||
|
||||
def _build_review_entry_handoff_summary(*, note_templates: list[dict], review_entry_action_queue: list[dict]) -> dict:
|
||||
lane_counts: dict[str, int] = {}
|
||||
for item in note_templates:
|
||||
lane = str(item.get("audit_lane_group") or "stable")
|
||||
lane_counts[lane] = int(lane_counts.get(lane) or 0) + 1
|
||||
return {
|
||||
"status": "active" if note_templates else "idle",
|
||||
"lane_counts": lane_counts,
|
||||
"template_count": len(note_templates),
|
||||
"action_queue_count": len(review_entry_action_queue),
|
||||
"next_handoff_prompt": (review_entry_action_queue[0] or {}).get("prompt") if review_entry_action_queue else None,
|
||||
}
|
||||
|
||||
|
||||
def _build_external_context_headline(
|
||||
*,
|
||||
news_window: dict,
|
||||
|
||||
@@ -824,6 +824,8 @@ function renderDecisionAssistant(summary) {
|
||||
['launch mode', evidence.launch_mode || 'unknown'],
|
||||
['top opportunity', opportunities[0]?.title || 'n/a'],
|
||||
['watchlist tiers', `review ${watchlistSummary.review_now_count ?? 0} | strong ${watchlistSummary.strong_watch_count ?? 0} | soft ${watchlistSummary.soft_watch_count ?? 0}`],
|
||||
['next watchlist focus', watchlistSummary.focus_matrix?.next_focus?.title || 'n/a'],
|
||||
['watchlist focus prompt', watchlistSummary.next_watchlist_focus_prompt || 'n/a'],
|
||||
['section nav', assistantSections.map((item) => item.title).join(' | ') || 'n/a'],
|
||||
['no-trade reasons', formatList(summary.no_trade_reasons || [])],
|
||||
['next actions', formatList(summary.next_actions || [])],
|
||||
@@ -1094,6 +1096,9 @@ function renderDecisionAssistant(summary) {
|
||||
['journal entries', String(reviewEntrypoints.journal_entry_count ?? 0)],
|
||||
['review queue', String(reviewEntrypoints.review_queue_count ?? 0)],
|
||||
['next review action', reviewEntrypoints.next_review_action || 'n/a'],
|
||||
['review action queue', String(reviewEntrypoints.review_entry_action_queue_count ?? 0)],
|
||||
['next review queue action', reviewEntrypoints.next_review_entry_action?.label || 'n/a'],
|
||||
['next review queue prompt', reviewEntrypoints.next_review_entry_action?.prompt || 'n/a'],
|
||||
['recommended template', reviewEntrypoints.recommended_template_id || 'n/a'],
|
||||
['recommended label', reviewEntrypoints.template_summary?.recommended_label || 'n/a'],
|
||||
['recommended priority', reviewEntrypoints.template_summary?.recommended_priority || 'n/a'],
|
||||
@@ -1143,6 +1148,8 @@ function renderDecisionAssistant(summary) {
|
||||
['next feedback template', reviewEntrypoints.template_reuse_metrics?.feedback_loop_summary?.next_feedback_template_id || 'n/a'],
|
||||
['next feedback prompt', reviewEntrypoints.template_reuse_metrics?.feedback_loop_summary?.next_feedback_prompt || 'n/a'],
|
||||
['template priorities', formatJsonInline(reviewEntrypoints.template_summary?.priorities || {})],
|
||||
['handoff lanes', formatJsonInline(reviewEntrypoints.review_entry_handoff_summary?.lane_counts || {})],
|
||||
['review handoff prompt', reviewEntrypoints.review_entry_handoff_summary?.next_handoff_prompt || 'n/a'],
|
||||
])
|
||||
);
|
||||
decisionAssistantPanel.appendChild(
|
||||
@@ -1161,6 +1168,22 @@ function renderDecisionAssistant(summary) {
|
||||
},
|
||||
])
|
||||
);
|
||||
if (reviewEntrypoints.review_entry_action_queue?.length) {
|
||||
const reviewActionGrid = createElement('div', 'cards assistant-tight-grid');
|
||||
(reviewEntrypoints.review_entry_action_queue || []).forEach((item) => {
|
||||
reviewActionGrid.appendChild(
|
||||
buildItemCard(
|
||||
item.label || 'review action',
|
||||
item.priority || item.source || 'n/a',
|
||||
[
|
||||
['source', item.source || 'n/a'],
|
||||
['prompt', item.prompt || 'n/a'],
|
||||
]
|
||||
)
|
||||
);
|
||||
});
|
||||
decisionAssistantPanel.appendChild(reviewActionGrid);
|
||||
}
|
||||
const noteTemplates = reviewEntrypoints.note_templates || [];
|
||||
if (noteTemplates.length) {
|
||||
const recommendedTemplate = noteTemplates.find((item) => item.template_id === reviewEntrypoints.recommended_template_id) || noteTemplates[0];
|
||||
@@ -1378,6 +1401,18 @@ function renderDecisionAssistant(summary) {
|
||||
const items = watchlistTiers[key] || [];
|
||||
if (!items.length) return;
|
||||
appendSectionTitle(decisionAssistantPanel, `Watchlist: ${label}`);
|
||||
if (key === 'review_now' && watchlistSummary.focus_matrix) {
|
||||
decisionAssistantPanel.appendChild(
|
||||
buildInfoGrid([
|
||||
['watchlist focus', watchlistSummary.focus_matrix.status || 'idle'],
|
||||
['recommended tier', watchlistSummary.focus_matrix.recommended_tier || 'n/a'],
|
||||
['recommended session', watchlistSummary.focus_matrix.recommended_session_code || 'n/a'],
|
||||
['watchlist next prompt', watchlistSummary.next_watchlist_focus_prompt || 'n/a'],
|
||||
['tier counts', formatJsonInline(watchlistSummary.focus_matrix.tier_counts || {})],
|
||||
['session counts', formatJsonInline(watchlistSummary.focus_matrix.session_counts || {})],
|
||||
])
|
||||
);
|
||||
}
|
||||
const tierGrid = createElement('div', 'cards assistant-tight-grid');
|
||||
items.forEach((item) => {
|
||||
tierGrid.appendChild(
|
||||
|
||||
@@ -165,6 +165,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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")
|
||||
@@ -213,6 +216,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user