Add replay learning focus queue

This commit is contained in:
Codex
2026-05-13 15:19:29 +08:00
parent e7f58c00de
commit 1e7189abfa
5 changed files with 162 additions and 6 deletions
+8 -3
View File
@@ -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 W3/H23 internal non-trading polish by adding a unified `assistant_loop_queue` / `assistant_loop_summary` that stitches together Workflow, Scan, Workspace, Digest, Review, and Handoff next-step signals, plus a visible Workflow section entry in the assistant navigation. 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 D24/H24 internal non-trading polish by adding a replay-learning `learning_focus_queue` / `next_learning_focus_prompt`, then wiring that learning focus back into the unified `assistant_loop_queue` so the next replay priority becomes part of the main cockpit loop. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
## Current Branch
@@ -119,6 +119,8 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
- latest_decision_assistant_digest_preference_slice: digest_preference_profile_local_mode
- latest_decision_assistant_workflow_queue_slice: assistant_loop_queue
- 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_summary_status: implemented
- latest_decision_assistant_change_summary_status: implemented
- latest_decision_assistant_evidence_status: implemented
@@ -222,10 +224,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
- latest_decision_assistant_digest_preference_status: implemented
- latest_decision_assistant_workflow_queue_status: implemented
- latest_decision_assistant_workspace_loop_status: implemented
- latest_decision_assistant_learning_focus_status: implemented
- latest_decision_assistant_learning_loop_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: assistant_loop_queue_workflow_nav_visible
- latest_remote_decision_assistant_review_packet_status: learning_focus_queue_replay_loop_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
@@ -637,11 +641,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
- Latest H21/F23 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 `continuous_workspace_summary` plus `digest_history_browser`.
- Latest W2/H22/F24 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_session_completion_summary`, `digest_preference_profile`, and the deployed Evidence section anchor/navigation strings.
- 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`.
- Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter.
## Exact Next Step
W3/H23 internal cockpit polish is closed. The remaining true external blocker for the external-context branch is still the real external-chart step: provide a read-only chart source URL/provider/access pattern if the inventory should become an actual external chart adapter. Internal non-trading backlog still exists, but PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
D24/H24 internal cockpit polish is closed. The remaining true external blocker for the external-context branch is still the real external-chart step: provide a read-only chart source URL/provider/access pattern if the inventory should become an actual external chart adapter. 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
+9 -1
View File
@@ -150,6 +150,8 @@
- [x] F24 digest preference profile / local mode slice
- [x] W3 assistant loop queue / next-step stitching slice
- [x] H23 Workflow section navigation / loop queue continuity slice
- [x] D24 replay learning focus queue / next-focus prompt slice
- [x] H24 replay learning integrated into loop queue slice
- [ ] 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
@@ -263,6 +265,8 @@
- [x] latest_decision_assistant_digest_preference_slice: digest_preference_profile_local_mode
- [x] latest_decision_assistant_workflow_queue_slice: assistant_loop_queue
- [x] latest_decision_assistant_workspace_loop_slice: workflow_section_navigation_loop_queue
- [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_summary_status: implemented
- [x] latest_decision_assistant_change_summary_status: implemented
- [x] latest_decision_assistant_evidence_status: implemented
@@ -363,10 +367,12 @@
- [x] latest_decision_assistant_digest_preference_status: implemented
- [x] latest_decision_assistant_workflow_queue_status: implemented
- [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_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: assistant_loop_queue_workflow_nav_visible
- [x] latest_remote_decision_assistant_review_packet_status: learning_focus_queue_replay_loop_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
@@ -670,6 +676,8 @@
- [x] Deployed W2/H22/F24 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet/static field smoke for `workflow_session_completion_summary`, `digest_preference_profile`, and Evidence navigation strings
- [x] Completed W3/H23 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 W3/H23 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet/static field smoke for `assistant_loop_queue`, `assistant_loop_summary`, Loop Queue UI strings, and Workflow navigation strings
- [x] Completed D24/H24 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 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`
## Blocked
@@ -464,6 +464,7 @@ def build_decision_assistant_summary(
continuous_workspace_summary=continuous_workspace_summary,
digest_history_browser=digest_history_browser,
review_entrypoints=review_entrypoints,
setup_learning_rollup=setup_learning_rollup,
)
assistant_loop_summary = _build_decision_assistant_loop_summary(assistant_loop_queue)
return {
@@ -686,6 +687,7 @@ def _build_decision_assistant_loop_queue(
continuous_workspace_summary: dict,
digest_history_browser: dict,
review_entrypoints: dict,
setup_learning_rollup: dict,
) -> list[dict]:
queue = []
if workflow_session_completion_summary.get("next_task_label"):
@@ -741,6 +743,20 @@ def _build_decision_assistant_loop_queue(
"priority_rank": 4,
}
)
next_learning_focus = dict(setup_learning_rollup.get("next_learning_focus") or {})
if next_learning_focus.get("title"):
queue.append(
{
"queue_id": "replay_learning",
"lane": "replay_learning",
"title": str(next_learning_focus.get("title") or "Replay Learning"),
"status": str(setup_learning_rollup.get("status") or "active"),
"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,
}
)
if review_entrypoints.get("next_review_action"):
queue.append(
{
@@ -751,7 +767,7 @@ def _build_decision_assistant_loop_queue(
"target_section": "review-entry",
"action_label": "Open Review",
"prompt": str(review_entrypoints.get("next_review_action") or "Open review entry."),
"priority_rank": 5,
"priority_rank": 6,
}
)
if scan_handoff_summary.get("next_resume_prompt"):
@@ -764,7 +780,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": 6,
"priority_rank": 7,
}
)
queue.sort(
@@ -4306,6 +4322,11 @@ def _build_decision_assistant_setup_learning_rollup(
low_quality_session=dict(weekly_review.get("low_quality_session") or {}),
replay_statistics=replay_statistics,
)
learning_focus_queue = _build_setup_learning_focus_queue(
setup_type=model_code,
session_code=session_code,
learning_attribution_summary=learning_attribution_summary,
)
return {
"status": "active" if lessons or current_session_stats else "idle",
"setup_type": model_code or "unknown",
@@ -4330,6 +4351,10 @@ def _build_decision_assistant_setup_learning_rollup(
"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"),
"learning_focus_queue": learning_focus_queue,
"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,
}
@@ -4583,6 +4608,96 @@ def _build_setup_weekly_trend_breakdown(setup_session_market_state_stats: list[d
}
def _build_setup_learning_focus_queue(*, setup_type: str, session_code: str, learning_attribution_summary: dict) -> list[dict]:
queue: list[dict] = []
failure_reasons = list(learning_attribution_summary.get("failure_reason_attribution") or [])
missed_opportunities = list(learning_attribution_summary.get("missed_opportunity_attribution") or [])
weekly_trend_breakdown = dict(learning_attribution_summary.get("weekly_trend_breakdown") or {})
setup_session_market_state_stats = list(learning_attribution_summary.get("setup_session_market_state_stats") or [])
for item in failure_reasons:
queue.append(
{
"focus_id": f"failure:{item.get('reason') or 'reason'}",
"focus_type": "failure_reason",
"priority": _learning_focus_priority_from_severity(str(item.get("severity") or "low")),
"title": _learning_focus_title_from_reason(str(item.get("reason") or "failure_reason")),
"prompt": str(item.get("detail") or "Review the failure reason before the next replay pass."),
"target_session_code": str(item.get("session_code") or session_code or "OFF_HOURS"),
"target_market_state": _setup_market_state_from_session(str(item.get("session_code") or session_code or "")),
"setup_type": setup_type or "unknown",
}
)
for item in missed_opportunities:
queue.append(
{
"focus_id": f"missed:{item.get('session_code') or 'session'}:{item.get('reason') or 'reason'}",
"focus_type": "missed_opportunity",
"priority": _learning_focus_priority_from_severity(str(item.get("severity") or "low")),
"title": f"Review {item.get('session_code') or 'session'} opportunity",
"prompt": str(item.get("detail") or "Review the missed opportunity before the next replay pass."),
"target_session_code": str(item.get("session_code") or session_code or "OFF_HOURS"),
"target_market_state": _setup_market_state_from_session(str(item.get("session_code") or session_code or "")),
"setup_type": setup_type or "unknown",
}
)
best_session = str(weekly_trend_breakdown.get("best_session") or "")
if best_session and best_session != str(session_code or ""):
queue.append(
{
"focus_id": f"best_session:{best_session}",
"focus_type": "session_baseline",
"priority": "medium",
"title": f"Review {best_session} baseline",
"prompt": f"{best_session} is the current best weekly session; compare its replay profile before the next setup pass.",
"target_session_code": best_session,
"target_market_state": _setup_market_state_from_session(best_session),
"setup_type": setup_type or "unknown",
}
)
if setup_session_market_state_stats:
top_market_state = str((setup_session_market_state_stats[0] or {}).get("market_state") or "")
if top_market_state:
queue.append(
{
"focus_id": f"market_state:{top_market_state}",
"focus_type": "market_state_focus",
"priority": "low",
"title": f"Review {top_market_state} context",
"prompt": f"Use the strongest {top_market_state} sample as the reference context for the next replay review.",
"target_session_code": str((setup_session_market_state_stats[0] or {}).get("session_code") or session_code or "OFF_HOURS"),
"target_market_state": top_market_state,
"setup_type": setup_type or "unknown",
}
)
queue.sort(
key=lambda row: (
{"high": 1, "medium": 2, "low": 3}.get(str(row.get("priority") or "low"), 4),
str(row.get("focus_type") or ""),
str(row.get("target_session_code") or ""),
)
)
return queue[:6]
def _learning_focus_priority_from_severity(severity: str) -> str:
if severity == "high":
return "high"
if severity == "medium":
return "medium"
return "low"
def _learning_focus_title_from_reason(reason: str) -> str:
mapping = {
"risk_violation": "Reduce risk violations",
"out_of_model_drift": "Tighten model discipline",
"framework_below_high_quality_baseline": "Raise framework execution rate",
"avg_r_below_high_quality_baseline": "Improve average R quality",
"insufficient_replay_samples": "Collect more replay samples",
}
return mapping.get(reason, reason.replace("_", " ").title())
def _build_decision_assistant_scan_summary_cards(
*,
top_opportunities: list[dict],
+22
View File
@@ -1525,6 +1525,9 @@ function renderDecisionAssistant(summary) {
['lesson count', String(setupLearningRollup.lesson_count ?? 0)],
['failure attributions', String(setupLearningRollup.learning_attribution_summary?.failure_reason_count ?? 0)],
['missed opportunities', String(setupLearningRollup.learning_attribution_summary?.missed_opportunity_count ?? 0)],
['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'],
['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'],
@@ -1698,6 +1701,7 @@ function renderDecisionAssistant(summary) {
decisionAssistantPanel.appendChild(leaderboardGrid);
}
const lessonItems = setupLearningRollup.top_lessons || [];
const learningFocusQueue = setupLearningRollup.learning_focus_queue || [];
if (lessonItems.length) {
const lessonGrid = createElement('div', 'cards');
lessonItems.forEach((item) => {
@@ -1713,6 +1717,24 @@ function renderDecisionAssistant(summary) {
});
decisionAssistantPanel.appendChild(lessonGrid);
}
if (learningFocusQueue.length) {
const focusGrid = createElement('div', 'cards assistant-tight-grid');
learningFocusQueue.forEach((item) => {
focusGrid.appendChild(
buildItemCard(
item.title || 'learning focus',
item.priority || item.focus_type || 'focus',
[
['type', item.focus_type || 'n/a'],
['session', item.target_session_code || 'n/a'],
['market state', item.target_market_state || 'n/a'],
['prompt', item.prompt || 'n/a'],
]
)
);
});
decisionAssistantPanel.appendChild(focusGrid);
}
}
if (digestHistoryPreview.length) {
const filteredHistory = assistantHistoryKindFilter === 'all'
@@ -181,6 +181,7 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertTrue(summary["assistant_loop_queue"])
self.assertEqual(summary["assistant_loop_summary"]["status"], "active")
self.assertTrue(summary["assistant_loop_summary"]["next_target_section"])
self.assertTrue(any(item["queue_id"] == "replay_learning" for item in summary["assistant_loop_queue"]))
self.assertIn("continuous_workspace_rail", summary)
self.assertIn("continuous_workspace_status", summary)
self.assertIn("continuous_workspace_summary", summary)
@@ -402,6 +403,11 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
self.assertIn("setup_session_market_state_stats", summary["setup_learning_rollup"])
self.assertIn("weekly_trend_breakdown", summary["setup_learning_rollup"])
self.assertIn("weekly_trend_summary", summary["setup_learning_rollup"])
self.assertIn("learning_focus_queue", summary["setup_learning_rollup"])
self.assertIn("learning_focus_queue_count", summary["setup_learning_rollup"])
self.assertIn("next_learning_focus", summary["setup_learning_rollup"])
self.assertIn("next_learning_focus_prompt", summary["setup_learning_rollup"])
self.assertTrue(summary["setup_learning_rollup"]["learning_focus_queue"])
self.assertGreaterEqual(summary["setup_learning_rollup"]["learning_attribution_summary"]["failure_reason_count"], 1)
self.assertIn("sample_count", summary["setup_learning_rollup"]["replay_statistics"])
self.assertIn("comparative_summary", summary["setup_learning_rollup"]["replay_statistics"])