Batch workflow digest learning workspace enhancements
This commit is contained in:
+12
-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 E7 internal non-trading polish by reclassifying the external chart adapter as an optional reference: internal review-packet chart context remains primary, external chart URLs are advisory-only enhancements, and missing external configuration no longer blocks the main analysis loop. 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 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.
|
||||
|
||||
## Current Branch
|
||||
|
||||
@@ -122,6 +122,10 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_workspace_loop_slice: workflow_section_navigation_loop_queue
|
||||
- latest_decision_assistant_learning_focus_slice: learning_focus_queue
|
||||
- latest_decision_assistant_learning_loop_slice: replay_learning_loop_queue
|
||||
- latest_decision_assistant_workflow_artifact_slice: workflow_phase_artifacts
|
||||
- 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_summary_status: implemented
|
||||
- latest_decision_assistant_change_summary_status: implemented
|
||||
- latest_decision_assistant_evidence_status: implemented
|
||||
@@ -228,10 +232,14 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_workspace_loop_status: implemented
|
||||
- latest_decision_assistant_learning_focus_status: implemented
|
||||
- latest_decision_assistant_learning_loop_status: implemented
|
||||
- latest_decision_assistant_workflow_artifact_status: implemented
|
||||
- latest_decision_assistant_digest_action_status: implemented
|
||||
- latest_decision_assistant_learning_matrix_status: implemented
|
||||
- latest_decision_assistant_workspace_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: chart_optional_reference_non_blocking_visible
|
||||
- latest_remote_decision_assistant_review_packet_status: workflow_digest_learning_workspace_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
|
||||
@@ -645,11 +653,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- Latest W3/H23 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 `assistant_loop_queue`, `assistant_loop_summary`, the Loop Queue UI anchor, and the deployed Workflow navigation entry.
|
||||
- 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.
|
||||
- Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter.
|
||||
|
||||
## Exact Next Step
|
||||
|
||||
E7 optional external-chart mode is closed. External chart URLs are now 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.
|
||||
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.
|
||||
|
||||
## Validation Commands Used Recently
|
||||
|
||||
|
||||
@@ -153,6 +153,10 @@
|
||||
- [x] D24 replay learning focus queue / next-focus prompt slice
|
||||
- [x] H24 replay learning integrated into loop queue slice
|
||||
- [x] E7 external chart optional reference / internal-primary mode slice
|
||||
- [x] W4 workflow artifact summary / phase brief slice
|
||||
- [x] D25 learning focus matrix slice
|
||||
- [x] F25 digest action queue slice
|
||||
- [x] H25 workspace continuity handoff slice
|
||||
- [x] P8 local closure complete
|
||||
- [x] P9 local closure complete
|
||||
- [x] P10 local closure complete
|
||||
@@ -268,6 +272,10 @@
|
||||
- [x] latest_decision_assistant_learning_focus_slice: learning_focus_queue
|
||||
- [x] latest_decision_assistant_learning_loop_slice: replay_learning_loop_queue
|
||||
- [x] latest_decision_assistant_chart_reference_slice: optional_external_chart_reference_internal_primary
|
||||
- [x] latest_decision_assistant_workflow_artifact_slice: workflow_phase_artifacts
|
||||
- [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_summary_status: implemented
|
||||
- [x] latest_decision_assistant_change_summary_status: implemented
|
||||
- [x] latest_decision_assistant_evidence_status: implemented
|
||||
@@ -371,10 +379,14 @@
|
||||
- [x] latest_decision_assistant_workspace_loop_status: implemented
|
||||
- [x] latest_decision_assistant_learning_focus_status: implemented
|
||||
- [x] latest_decision_assistant_learning_loop_status: implemented
|
||||
- [x] latest_decision_assistant_workflow_artifact_status: implemented
|
||||
- [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_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: chart_optional_reference_non_blocking_visible
|
||||
- [x] latest_remote_decision_assistant_review_packet_status: workflow_digest_learning_workspace_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
|
||||
@@ -682,6 +694,8 @@
|
||||
- [x] Deployed D24/H24 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet field smoke for `learning_focus_queue`, `next_learning_focus_prompt`, and replay-learning presence inside `assistant_loop_queue`
|
||||
- [x] Completed E7 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 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`
|
||||
|
||||
## Blocked
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ def build_decision_assistant_summary(
|
||||
workflow_digest=workflow_digest,
|
||||
current_session=current_session,
|
||||
)
|
||||
workflow_artifacts = _build_decision_assistant_workflow_artifacts(workflow_digest)
|
||||
workflow_artifact_summary = _build_decision_assistant_workflow_artifact_summary(workflow_artifacts)
|
||||
next_actions = _build_decision_assistant_next_actions(
|
||||
launch_readiness=launch_readiness,
|
||||
decision_packet=decision_packet,
|
||||
@@ -467,6 +469,10 @@ def build_decision_assistant_summary(
|
||||
setup_learning_rollup=setup_learning_rollup,
|
||||
)
|
||||
assistant_loop_summary = _build_decision_assistant_loop_summary(assistant_loop_queue)
|
||||
workspace_continuity_handoff = _build_decision_assistant_workspace_continuity_handoff(
|
||||
continuous_workspace_rail=continuous_workspace_rail,
|
||||
assistant_loop_queue=assistant_loop_queue,
|
||||
)
|
||||
return {
|
||||
"instrument_id": instrument_id,
|
||||
"timeframe": timeframe,
|
||||
@@ -486,6 +492,8 @@ def build_decision_assistant_summary(
|
||||
"workflow_digest": workflow_digest,
|
||||
"workflow_rhythm_summary": workflow_rhythm_summary,
|
||||
"workflow_session_completion_summary": workflow_session_completion_summary,
|
||||
"workflow_artifacts": workflow_artifacts,
|
||||
"workflow_artifact_summary": workflow_artifact_summary,
|
||||
"evidence_chain": evidence_chain,
|
||||
"explanation_drilldown": explanation_drilldown,
|
||||
"reason_citations": reason_citations,
|
||||
@@ -505,6 +513,7 @@ def build_decision_assistant_summary(
|
||||
"scan_handoff_summary": scan_handoff_summary,
|
||||
"assistant_loop_queue": assistant_loop_queue,
|
||||
"assistant_loop_summary": assistant_loop_summary,
|
||||
"workspace_continuity_handoff": workspace_continuity_handoff,
|
||||
"continuous_workspace_rail": continuous_workspace_rail,
|
||||
"continuous_workspace_status": continuous_workspace_summary.get("status"),
|
||||
"continuous_workspace_summary": continuous_workspace_summary,
|
||||
@@ -822,6 +831,37 @@ def _build_decision_assistant_loop_summary(assistant_loop_queue: list[dict]) ->
|
||||
}
|
||||
|
||||
|
||||
def _build_decision_assistant_workspace_continuity_handoff(
|
||||
*,
|
||||
continuous_workspace_rail: list[dict],
|
||||
assistant_loop_queue: list[dict],
|
||||
) -> dict:
|
||||
if not continuous_workspace_rail:
|
||||
return {
|
||||
"status": "idle",
|
||||
"ready_sections": [],
|
||||
"watch_sections": [],
|
||||
"loop_sections": [],
|
||||
"next_loop_prompt": None,
|
||||
}
|
||||
ready_sections = [str(item.get("section") or "") for item in continuous_workspace_rail if str(item.get("status") or "") == "active"]
|
||||
watch_sections = [str(item.get("section") or "") for item in continuous_workspace_rail if str(item.get("status") or "") == "watch"]
|
||||
loop_sections = [str(item.get("title") or item.get("queue_id") or "") for item in assistant_loop_queue[:4]]
|
||||
return {
|
||||
"status": "active",
|
||||
"ready_sections": ready_sections,
|
||||
"watch_sections": watch_sections,
|
||||
"loop_sections": loop_sections,
|
||||
"loop_section_count": len(loop_sections),
|
||||
"next_loop_prompt": (assistant_loop_queue[0] or {}).get("prompt") if assistant_loop_queue else None,
|
||||
"continuity_hint": (
|
||||
f"Keep {ready_sections[0]} in front while carrying {', '.join(loop_sections[:2])} through the current operator loop."
|
||||
if ready_sections and loop_sections
|
||||
else "Carry the active workspace sections through the current operator loop."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _continuous_workspace_status(continuous_workspace_rail: list[dict]) -> str:
|
||||
if not continuous_workspace_rail:
|
||||
return "idle"
|
||||
@@ -1329,6 +1369,72 @@ def _build_decision_assistant_workflow_session_completion_summary(
|
||||
}
|
||||
|
||||
|
||||
def _build_decision_assistant_workflow_artifacts(workflow_digest: list[dict]) -> list[dict]:
|
||||
artifact_kind_map = {
|
||||
"pre_market": "pre_market_digest",
|
||||
"intraday": "intraday_monitor_digest",
|
||||
"post_trade": "post_trade_review_digest",
|
||||
}
|
||||
artifacts = []
|
||||
for index, phase in enumerate(workflow_digest, start=1):
|
||||
task_status = str(phase.get("session_task_status") or "pending")
|
||||
if task_status == "complete":
|
||||
artifact_state = "ready_for_handoff"
|
||||
elif task_status == "in_progress":
|
||||
artifact_state = "needs_update"
|
||||
else:
|
||||
artifact_state = "pending_capture"
|
||||
artifacts.append(
|
||||
{
|
||||
"artifact_id": f"workflow_artifact:{phase.get('phase') or index}",
|
||||
"phase": phase.get("phase"),
|
||||
"title": phase.get("title"),
|
||||
"artifact_kind": artifact_kind_map.get(str(phase.get("phase") or ""), "workflow_artifact"),
|
||||
"artifact_state": artifact_state,
|
||||
"session_task_label": phase.get("session_task_label"),
|
||||
"prompt": phase.get("session_task_prompt") or phase.get("next_rhythm_action") or phase.get("next_step"),
|
||||
"next_step": phase.get("next_step"),
|
||||
"daily_rhythm_slot": phase.get("daily_rhythm_slot"),
|
||||
}
|
||||
)
|
||||
return artifacts
|
||||
|
||||
|
||||
def _build_decision_assistant_workflow_artifact_summary(workflow_artifacts: list[dict]) -> dict:
|
||||
if not workflow_artifacts:
|
||||
return {
|
||||
"status": "idle",
|
||||
"artifact_count": 0,
|
||||
"state_counts": {},
|
||||
"next_artifact_kind": None,
|
||||
"next_artifact_prompt": None,
|
||||
}
|
||||
state_counts: dict[str, int] = {}
|
||||
for item in workflow_artifacts:
|
||||
state = str(item.get("artifact_state") or "pending_capture")
|
||||
state_counts[state] = int(state_counts.get(state) or 0) + 1
|
||||
next_artifact = dict(
|
||||
sorted(
|
||||
workflow_artifacts,
|
||||
key=lambda row: (
|
||||
{"pending_capture": 1, "needs_update": 2, "ready_for_handoff": 3}.get(str(row.get("artifact_state") or "pending_capture"), 4),
|
||||
str(row.get("phase") or ""),
|
||||
),
|
||||
)[0]
|
||||
)
|
||||
return {
|
||||
"status": "active",
|
||||
"artifact_count": len(workflow_artifacts),
|
||||
"state_counts": state_counts,
|
||||
"ready_for_handoff_count": int(state_counts.get("ready_for_handoff") or 0),
|
||||
"needs_update_count": int(state_counts.get("needs_update") or 0),
|
||||
"pending_capture_count": int(state_counts.get("pending_capture") or 0),
|
||||
"next_artifact_kind": next_artifact.get("artifact_kind"),
|
||||
"next_artifact_phase": next_artifact.get("phase"),
|
||||
"next_artifact_prompt": next_artifact.get("prompt"),
|
||||
}
|
||||
|
||||
|
||||
def _build_decision_assistant_explanation_drilldown(
|
||||
*,
|
||||
decision_packet: dict,
|
||||
@@ -2992,6 +3098,12 @@ def _build_decision_assistant_digest_surface(
|
||||
digest_history_groups=digest_history_groups,
|
||||
digest_history_summary=digest_history_summary,
|
||||
)
|
||||
digest_action_queue = _build_digest_action_queue(
|
||||
high_value_reminders=high_value_reminders,
|
||||
digest_review_back_actions=digest_review_back_actions,
|
||||
history_browser=history_browser,
|
||||
digest_history_summary=digest_history_summary,
|
||||
)
|
||||
return {
|
||||
"status": "active" if personalized_reminders or top_opportunities else "idle",
|
||||
"focus_mode": focus_mode,
|
||||
@@ -3010,6 +3122,9 @@ def _build_decision_assistant_digest_surface(
|
||||
"history_browser_status": history_browser.get("status"),
|
||||
"history_browser_option_count": history_browser.get("option_count"),
|
||||
"next_history_browser_action": history_browser.get("next_action"),
|
||||
"digest_action_queue": digest_action_queue[:6],
|
||||
"digest_action_queue_count": len(digest_action_queue),
|
||||
"next_digest_action": digest_action_queue[0] if digest_action_queue 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),
|
||||
@@ -3220,6 +3335,68 @@ def _build_digest_history_browser(*, digest_history_groups: list[dict], digest_h
|
||||
}
|
||||
|
||||
|
||||
def _build_digest_action_queue(
|
||||
*,
|
||||
high_value_reminders: list[dict],
|
||||
digest_review_back_actions: list[dict],
|
||||
history_browser: dict,
|
||||
digest_history_summary: dict,
|
||||
) -> list[dict]:
|
||||
queue = []
|
||||
if high_value_reminders:
|
||||
top = dict(high_value_reminders[0] or {})
|
||||
queue.append(
|
||||
{
|
||||
"action_id": f"high_value:{top.get('title') or 'reminder'}",
|
||||
"source": "high_value_reminder",
|
||||
"label": top.get("title") or "Review high-value reminder",
|
||||
"priority": "high",
|
||||
"prompt": top.get("message") or top.get("high_value_reason") or "Review the highest-value reminder.",
|
||||
}
|
||||
)
|
||||
next_history = dict(history_browser.get("next_action") or {})
|
||||
if next_history.get("label"):
|
||||
queue.append(
|
||||
{
|
||||
"action_id": next_history.get("action_id") or "history_browser",
|
||||
"source": "history_browser",
|
||||
"label": next_history.get("label") or "Review history browser",
|
||||
"priority": next_history.get("priority") or "medium",
|
||||
"prompt": next_history.get("prompt") or "Review the next history browser action.",
|
||||
}
|
||||
)
|
||||
next_review_back = dict((digest_review_back_actions or [{}])[0] or {})
|
||||
if next_review_back.get("label"):
|
||||
queue.append(
|
||||
{
|
||||
"action_id": next_review_back.get("action_id") or "review_back",
|
||||
"source": "review_back",
|
||||
"label": next_review_back.get("label") or "Review digest recall",
|
||||
"priority": next_review_back.get("priority") or "medium",
|
||||
"prompt": next_review_back.get("prompt") or "Continue digest review-back.",
|
||||
}
|
||||
)
|
||||
next_sla_recovery = dict(digest_history_summary.get("next_sla_recovery_recommendation") or {})
|
||||
if next_sla_recovery.get("label"):
|
||||
queue.append(
|
||||
{
|
||||
"action_id": f"sla_recovery:{next_sla_recovery.get('kind') or 'digest'}",
|
||||
"source": "sla_recovery",
|
||||
"label": next_sla_recovery.get("label") or "Resolve digest SLA",
|
||||
"priority": next_sla_recovery.get("priority") or "medium",
|
||||
"prompt": next_sla_recovery.get("prompt") or digest_history_summary.get("next_sla_recovery_prompt") or "Resolve digest SLA backlog.",
|
||||
}
|
||||
)
|
||||
queue.sort(
|
||||
key=lambda row: (
|
||||
{"high": 1, "medium": 2, "low": 3, "none": 4}.get(str(row.get("priority") or "none"), 4),
|
||||
str(row.get("source") or ""),
|
||||
str(row.get("label") or ""),
|
||||
)
|
||||
)
|
||||
return queue
|
||||
|
||||
|
||||
def _digest_history_browser_hint(*, recommended_kind: str, total_count: int) -> str:
|
||||
if recommended_kind:
|
||||
return f"Open {recommended_kind} first, then review the remaining {total_count} digest history item(s) by freshness debt."
|
||||
@@ -4355,6 +4532,10 @@ def _build_decision_assistant_setup_learning_rollup(
|
||||
session_code=session_code,
|
||||
learning_attribution_summary=learning_attribution_summary,
|
||||
)
|
||||
learning_focus_matrix = _build_setup_learning_focus_matrix(
|
||||
learning_focus_queue=learning_focus_queue,
|
||||
setup_session_market_state_stats=learning_attribution_summary.get("setup_session_market_state_stats") or [],
|
||||
)
|
||||
return {
|
||||
"status": "active" if lessons or current_session_stats else "idle",
|
||||
"setup_type": model_code or "unknown",
|
||||
@@ -4383,6 +4564,7 @@ def _build_decision_assistant_setup_learning_rollup(
|
||||
"learning_focus_queue_count": len(learning_focus_queue),
|
||||
"next_learning_focus": learning_focus_queue[0] if learning_focus_queue else None,
|
||||
"next_learning_focus_prompt": learning_focus_queue[0].get("prompt") if learning_focus_queue else None,
|
||||
"learning_focus_matrix": learning_focus_matrix,
|
||||
}
|
||||
|
||||
|
||||
@@ -4707,6 +4889,59 @@ def _build_setup_learning_focus_queue(*, setup_type: str, session_code: str, lea
|
||||
return queue[:6]
|
||||
|
||||
|
||||
def _build_setup_learning_focus_matrix(*, learning_focus_queue: list[dict], setup_session_market_state_stats: list[dict]) -> dict:
|
||||
if not learning_focus_queue and not setup_session_market_state_stats:
|
||||
return {
|
||||
"status": "idle",
|
||||
"session_focus_counts": {},
|
||||
"market_state_focus_counts": {},
|
||||
"recommended_session_code": None,
|
||||
"recommended_market_state": None,
|
||||
}
|
||||
session_focus_counts: dict[str, int] = {}
|
||||
market_state_focus_counts: dict[str, int] = {}
|
||||
for item in learning_focus_queue:
|
||||
session_code = str(item.get("target_session_code") or "OFF_HOURS")
|
||||
market_state = str(item.get("target_market_state") or "unknown_market_state")
|
||||
session_focus_counts[session_code] = int(session_focus_counts.get(session_code) or 0) + 1
|
||||
market_state_focus_counts[market_state] = int(market_state_focus_counts.get(market_state) or 0) + 1
|
||||
recommended_session_code = next(
|
||||
(
|
||||
key
|
||||
for key, _count in sorted(
|
||||
session_focus_counts.items(),
|
||||
key=lambda row: (row[1], row[0]),
|
||||
reverse=True,
|
||||
)
|
||||
),
|
||||
None,
|
||||
)
|
||||
recommended_market_state = next(
|
||||
(
|
||||
key
|
||||
for key, _count in sorted(
|
||||
market_state_focus_counts.items(),
|
||||
key=lambda row: (row[1], row[0]),
|
||||
reverse=True,
|
||||
)
|
||||
),
|
||||
None,
|
||||
)
|
||||
return {
|
||||
"status": "active",
|
||||
"session_focus_counts": session_focus_counts,
|
||||
"market_state_focus_counts": market_state_focus_counts,
|
||||
"recommended_session_code": recommended_session_code,
|
||||
"recommended_market_state": recommended_market_state,
|
||||
"focus_card_count": len(learning_focus_queue),
|
||||
"matrix_hint": (
|
||||
f"Bias the next replay pass toward {recommended_session_code} / {recommended_market_state}."
|
||||
if recommended_session_code or recommended_market_state
|
||||
else "No learning focus bias is available yet."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _learning_focus_priority_from_severity(severity: str) -> str:
|
||||
if severity == "high":
|
||||
return "high"
|
||||
|
||||
@@ -521,6 +521,7 @@ function renderDecisionAssistant(summary) {
|
||||
const setupLearningRollup = summary.setup_learning_rollup || null;
|
||||
const continuousWorkspaceRail = summary.continuous_workspace_rail || [];
|
||||
const continuousWorkspaceSummary = summary.continuous_workspace_summary || null;
|
||||
const workspaceContinuityHandoff = summary.workspace_continuity_handoff || null;
|
||||
const visibleReminders = filterAssistantReminders(personalizedReminders, assistantDigestPreferenceMode);
|
||||
const reminderCards = visibleReminders.length ? visibleReminders : personalizedReminders;
|
||||
const savedHandoffAge = resolveAssistantHandoffAge(assistantScanHandoffMarker, scanHandoffSummary?.default_stale_after_seconds);
|
||||
@@ -776,6 +777,11 @@ function renderDecisionAssistant(summary) {
|
||||
['workspace next prompt', continuousWorkspaceSummary?.next_handoff_prompt || 'n/a'],
|
||||
['workspace path', continuousWorkspaceSummary?.rail_path || formatList(continuousWorkspaceRail.map((item) => item.section || 'section'))],
|
||||
['continuity hint', continuousWorkspaceSummary?.continuity_hint || 'n/a'],
|
||||
['workspace handoff', workspaceContinuityHandoff?.status || 'n/a'],
|
||||
['ready sections', formatList(workspaceContinuityHandoff?.ready_sections || [])],
|
||||
['watch sections', formatList(workspaceContinuityHandoff?.watch_sections || [])],
|
||||
['loop sections', formatList(workspaceContinuityHandoff?.loop_sections || [])],
|
||||
['next loop prompt', workspaceContinuityHandoff?.next_loop_prompt || 'n/a'],
|
||||
])
|
||||
);
|
||||
const railGrid = createElement('div', 'cards assistant-workspace-rail-grid');
|
||||
@@ -858,6 +864,9 @@ function renderDecisionAssistant(summary) {
|
||||
['history browser options', String(digestHistoryBrowser?.option_count ?? 0)],
|
||||
['next browser filter', digestHistoryBrowser?.next_filter_label || 'n/a'],
|
||||
['next browser prompt', digestHistoryBrowser?.next_filter_prompt || 'n/a'],
|
||||
['digest action queue', String(digestSurface.digest_action_queue_count ?? 0)],
|
||||
['next digest action', digestSurface.next_digest_action?.label || 'n/a'],
|
||||
['next digest prompt', digestSurface.next_digest_action?.prompt || 'n/a'],
|
||||
['digest history', String(digestSurface.history_count ?? 0)],
|
||||
['digest kinds', formatList(digestSurface.history_summary?.kinds || [])],
|
||||
['recommended history kind', digestSurface.history_summary?.recommended_kind || 'n/a'],
|
||||
@@ -908,6 +917,22 @@ function renderDecisionAssistant(summary) {
|
||||
])
|
||||
);
|
||||
}
|
||||
if (digestSurface?.digest_action_queue?.length) {
|
||||
const digestActionGrid = createElement('div', 'cards assistant-tight-grid');
|
||||
(digestSurface.digest_action_queue || []).forEach((item) => {
|
||||
digestActionGrid.appendChild(
|
||||
buildItemCard(
|
||||
item.label || 'digest action',
|
||||
item.priority || item.source || 'n/a',
|
||||
[
|
||||
['source', item.source || 'n/a'],
|
||||
['prompt', item.prompt || 'n/a'],
|
||||
]
|
||||
)
|
||||
);
|
||||
});
|
||||
decisionAssistantPanel.appendChild(digestActionGrid);
|
||||
}
|
||||
if (digestHistoryBrowser?.filter_options?.length) {
|
||||
const browserBar = buildActionBar(
|
||||
digestHistoryBrowser.filter_options.map((item) => ({
|
||||
@@ -1271,6 +1296,19 @@ function renderDecisionAssistant(summary) {
|
||||
])
|
||||
);
|
||||
}
|
||||
if (summary.workflow_artifact_summary) {
|
||||
decisionAssistantPanel.appendChild(
|
||||
buildInfoGrid([
|
||||
['workflow artifacts', summary.workflow_artifact_summary.status || 'idle'],
|
||||
['artifact count', String(summary.workflow_artifact_summary.artifact_count ?? 0)],
|
||||
['artifacts ready', String(summary.workflow_artifact_summary.ready_for_handoff_count ?? 0)],
|
||||
['artifacts updating', String(summary.workflow_artifact_summary.needs_update_count ?? 0)],
|
||||
['artifacts pending', String(summary.workflow_artifact_summary.pending_capture_count ?? 0)],
|
||||
['next artifact', summary.workflow_artifact_summary.next_artifact_kind || 'n/a'],
|
||||
['next artifact prompt', summary.workflow_artifact_summary.next_artifact_prompt || 'n/a'],
|
||||
])
|
||||
);
|
||||
}
|
||||
const workflowGrid = createElement('div', 'cards');
|
||||
workflowDigest.forEach((item) => {
|
||||
workflowGrid.appendChild(
|
||||
@@ -1292,6 +1330,25 @@ function renderDecisionAssistant(summary) {
|
||||
);
|
||||
});
|
||||
decisionAssistantPanel.appendChild(workflowGrid);
|
||||
const workflowArtifacts = summary.workflow_artifacts || [];
|
||||
if (workflowArtifacts.length) {
|
||||
const artifactGrid = createElement('div', 'cards assistant-tight-grid');
|
||||
workflowArtifacts.forEach((item) => {
|
||||
artifactGrid.appendChild(
|
||||
buildItemCard(
|
||||
item.title || item.phase || 'artifact',
|
||||
item.artifact_state || 'pending',
|
||||
[
|
||||
['kind', item.artifact_kind || 'n/a'],
|
||||
['session task', item.session_task_label || 'n/a'],
|
||||
['slot', item.daily_rhythm_slot || 'n/a'],
|
||||
['prompt', item.prompt || 'n/a'],
|
||||
]
|
||||
)
|
||||
);
|
||||
});
|
||||
decisionAssistantPanel.appendChild(artifactGrid);
|
||||
}
|
||||
}
|
||||
if (opportunities.length) {
|
||||
appendSectionTitle(decisionAssistantPanel, 'Opportunities');
|
||||
@@ -1531,6 +1588,9 @@ function renderDecisionAssistant(summary) {
|
||||
['learning focus queue', String(setupLearningRollup.learning_focus_queue_count ?? 0)],
|
||||
['next learning focus', setupLearningRollup.next_learning_focus?.title || 'n/a'],
|
||||
['next learning prompt', setupLearningRollup.next_learning_focus_prompt || 'n/a'],
|
||||
['learning focus session', setupLearningRollup.learning_focus_matrix?.recommended_session_code || 'n/a'],
|
||||
['learning focus state', setupLearningRollup.learning_focus_matrix?.recommended_market_state || 'n/a'],
|
||||
['learning matrix hint', setupLearningRollup.learning_focus_matrix?.matrix_hint || 'n/a'],
|
||||
['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'],
|
||||
@@ -1738,6 +1798,16 @@ function renderDecisionAssistant(summary) {
|
||||
});
|
||||
decisionAssistantPanel.appendChild(focusGrid);
|
||||
}
|
||||
if (setupLearningRollup.learning_focus_matrix) {
|
||||
decisionAssistantPanel.appendChild(
|
||||
buildInfoGrid([
|
||||
['focus matrix', setupLearningRollup.learning_focus_matrix.status || 'idle'],
|
||||
['session focus counts', formatJsonInline(setupLearningRollup.learning_focus_matrix.session_focus_counts || {})],
|
||||
['market-state counts', formatJsonInline(setupLearningRollup.learning_focus_matrix.market_state_focus_counts || {})],
|
||||
['focus cards', String(setupLearningRollup.learning_focus_matrix.focus_card_count ?? 0)],
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
if (digestHistoryPreview.length) {
|
||||
const filteredHistory = assistantHistoryKindFilter === 'all'
|
||||
|
||||
@@ -176,6 +176,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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"])
|
||||
@@ -185,6 +189,7 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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])
|
||||
@@ -346,6 +351,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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])
|
||||
@@ -407,6 +416,7 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
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"])
|
||||
|
||||
Reference in New Issue
Block a user