Advance workflow and digest cockpit polish
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 H21/F23 internal non-trading polish by adding a richer continuous workspace summary/rail and a digest history browser with review sequence, filter options, and remote-visible next browser action fields. 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 W2/H22/F24 internal non-trading polish by adding workflow session-task completion summaries, a real Evidence section anchor inside the continuous workspace, and a digest preference profile with locally persisted reminder-view modes. Local validation passed, the current working tree was deployed, and remote smoke confirmed the latest Decision Assistant fields on the live host.
|
||||
|
||||
## Current Branch
|
||||
|
||||
@@ -106,14 +106,17 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_replay_evidence_aging_slice: promotion_evidence_aging_backlog_comparison_summary
|
||||
- latest_decision_assistant_external_calendar_slice: macro_news_calendar_event_window_summary
|
||||
- latest_decision_assistant_workflow_rhythm_slice: pre_intraday_post_rhythm_checklist
|
||||
- latest_decision_assistant_workflow_session_slice: workflow_session_completion_summary
|
||||
- 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_workspace_evidence_slice: evidence_section_anchor_navigation
|
||||
- latest_decision_assistant_weekly_trend_slice: weekly_trend_breakdown
|
||||
- latest_decision_assistant_digest_review_back_slice: digest_history_review_back_actions
|
||||
- latest_decision_assistant_workspace_refinement_slice: continuous_workspace_summary_navigation
|
||||
- latest_decision_assistant_digest_browser_slice: digest_history_browser_review_sequence
|
||||
- latest_decision_assistant_digest_preference_slice: digest_preference_profile_local_mode
|
||||
- latest_decision_assistant_summary_status: implemented
|
||||
- latest_decision_assistant_change_summary_status: implemented
|
||||
- latest_decision_assistant_evidence_status: implemented
|
||||
@@ -204,18 +207,21 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- latest_decision_assistant_replay_evidence_aging_status: implemented
|
||||
- latest_decision_assistant_external_calendar_status: implemented
|
||||
- latest_decision_assistant_workflow_rhythm_status: implemented
|
||||
- latest_decision_assistant_workflow_session_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_workspace_evidence_status: implemented
|
||||
- latest_decision_assistant_weekly_trend_status: implemented
|
||||
- latest_decision_assistant_digest_review_back_status: implemented
|
||||
- latest_decision_assistant_workspace_refinement_status: implemented
|
||||
- latest_decision_assistant_digest_browser_status: implemented
|
||||
- latest_decision_assistant_digest_preference_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: workspace_summary_digest_browser_visible
|
||||
- latest_remote_decision_assistant_review_packet_status: workflow_session_digest_preference_evidence_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
|
||||
@@ -625,11 +631,12 @@ Automation execution-layer PXX remains closed and its known scope stays frozen.
|
||||
- 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.
|
||||
- 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.
|
||||
- Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter.
|
||||
|
||||
## Exact Next Step
|
||||
|
||||
H21/F23 internal cockpit polish is closed. The remaining true external blocker 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. PXX remains closed; do not create P25/P26 automatically, and do not enable real trading.
|
||||
W2/H22/F24 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
|
||||
|
||||
|
||||
@@ -145,6 +145,9 @@
|
||||
- [x] F22 digest history review-back ergonomics
|
||||
- [x] H21 continuous workspace summary / rail navigation slice
|
||||
- [x] F23 digest history browser / review sequence slice
|
||||
- [x] W2 workflow session task completion summary slice
|
||||
- [x] H22 Evidence section continuity / workspace navigation slice
|
||||
- [x] F24 digest preference profile / local mode 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
|
||||
@@ -253,6 +256,9 @@
|
||||
- [x] latest_decision_assistant_digest_backlog_slice: proof_sla_backlog_freshness_debt_summary
|
||||
- [x] latest_decision_assistant_template_audit_lane_slice: recovery_shortcuts_audit_lane_grouping
|
||||
- [x] latest_decision_assistant_replay_evidence_backlog_slice: promotion_evidence_queue_backlog_trend_handoff
|
||||
- [x] latest_decision_assistant_workflow_session_slice: workflow_session_completion_summary
|
||||
- [x] latest_decision_assistant_workspace_evidence_slice: evidence_section_anchor_navigation
|
||||
- [x] latest_decision_assistant_digest_preference_slice: digest_preference_profile_local_mode
|
||||
- [x] latest_decision_assistant_summary_status: implemented
|
||||
- [x] latest_decision_assistant_change_summary_status: implemented
|
||||
- [x] latest_decision_assistant_evidence_status: implemented
|
||||
@@ -340,18 +346,21 @@
|
||||
- [x] latest_decision_assistant_replay_evidence_aging_status: implemented
|
||||
- [x] latest_decision_assistant_external_calendar_status: implemented
|
||||
- [x] latest_decision_assistant_workflow_rhythm_status: implemented
|
||||
- [x] latest_decision_assistant_workflow_session_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_workspace_evidence_status: implemented
|
||||
- [x] latest_decision_assistant_weekly_trend_status: implemented
|
||||
- [x] latest_decision_assistant_digest_review_back_status: implemented
|
||||
- [x] latest_decision_assistant_workspace_refinement_status: implemented
|
||||
- [x] latest_decision_assistant_digest_browser_status: implemented
|
||||
- [x] latest_decision_assistant_digest_preference_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: workspace_summary_digest_browser_visible
|
||||
- [x] latest_remote_decision_assistant_review_packet_status: workflow_session_digest_preference_evidence_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
|
||||
@@ -651,6 +660,8 @@
|
||||
- [x] Completed PXX closure gate with finite-scope freeze and no new Research Council phase creation
|
||||
- [x] Completed H21/F23 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 H21/F23 current tree and passed remote smoke: 11 deploy endpoints, 9 remote ops checks, and review-packet field smoke for `continuous_workspace_summary` / `digest_history_browser`
|
||||
- [x] Completed W2/H22/F24 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 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
|
||||
|
||||
## Blocked
|
||||
|
||||
|
||||
@@ -339,6 +339,10 @@ def build_decision_assistant_summary(
|
||||
current_session=current_session,
|
||||
)
|
||||
workflow_rhythm_summary = _build_decision_assistant_workflow_rhythm_summary(workflow_digest)
|
||||
workflow_session_completion_summary = _build_decision_assistant_workflow_session_completion_summary(
|
||||
workflow_digest=workflow_digest,
|
||||
current_session=current_session,
|
||||
)
|
||||
next_actions = _build_decision_assistant_next_actions(
|
||||
launch_readiness=launch_readiness,
|
||||
decision_packet=decision_packet,
|
||||
@@ -471,6 +475,7 @@ def build_decision_assistant_summary(
|
||||
"candidate_watchlist_summary": candidate_watchlist_summary,
|
||||
"workflow_digest": workflow_digest,
|
||||
"workflow_rhythm_summary": workflow_rhythm_summary,
|
||||
"workflow_session_completion_summary": workflow_session_completion_summary,
|
||||
"evidence_chain": evidence_chain,
|
||||
"explanation_drilldown": explanation_drilldown,
|
||||
"reason_citations": reason_citations,
|
||||
@@ -1053,6 +1058,10 @@ def _build_decision_assistant_workflow_digest(
|
||||
phase["rhythm_pending_count"] = max(0, len(checklist) - completed_count)
|
||||
phase["rhythm_status"] = "complete" if completed_count == len(checklist) else "attention_needed"
|
||||
phase["next_rhythm_action"] = _workflow_next_rhythm_action(phase)
|
||||
phase["session_task_label"] = _workflow_session_task_label(phase)
|
||||
phase["session_task_status"] = _workflow_session_task_status(phase)
|
||||
phase["session_task_completed"] = phase["session_task_status"] == "complete"
|
||||
phase["session_task_prompt"] = _workflow_session_task_prompt(phase)
|
||||
return phases
|
||||
|
||||
|
||||
@@ -1102,6 +1111,69 @@ def _build_decision_assistant_workflow_rhythm_summary(workflow_digest: list[dict
|
||||
}
|
||||
|
||||
|
||||
def _workflow_session_task_label(phase: dict) -> str:
|
||||
mapping = {
|
||||
"pre_market": "Prepare Session Context",
|
||||
"intraday": "Track Live Opportunity Queue",
|
||||
"post_trade": "Close Session Review Loop",
|
||||
}
|
||||
return mapping.get(str(phase.get("phase") or ""), "Complete Session Task")
|
||||
|
||||
|
||||
def _workflow_session_task_status(phase: dict) -> str:
|
||||
if int(phase.get("rhythm_pending_count") or 0) == 0:
|
||||
return "complete"
|
||||
if int(phase.get("rhythm_completed_count") or 0) > 0:
|
||||
return "in_progress"
|
||||
return "pending"
|
||||
|
||||
|
||||
def _workflow_session_task_prompt(phase: dict) -> str:
|
||||
if phase.get("session_task_status") == "complete":
|
||||
return f"{phase.get('session_task_label') or 'Session task'} is complete for this loop."
|
||||
return (
|
||||
f"{phase.get('session_task_label') or 'Session task'}: "
|
||||
f"{phase.get('next_rhythm_action') or phase.get('next_step') or 'continue the current workflow step.'}"
|
||||
)
|
||||
|
||||
|
||||
def _build_decision_assistant_workflow_session_completion_summary(
|
||||
*,
|
||||
workflow_digest: list[dict],
|
||||
current_session: str,
|
||||
) -> dict:
|
||||
task_count = len(workflow_digest)
|
||||
status_counts: dict[str, int] = {}
|
||||
for phase in workflow_digest:
|
||||
task_status = str(phase.get("session_task_status") or "pending")
|
||||
status_counts[task_status] = int(status_counts.get(task_status) or 0) + 1
|
||||
next_phase = next(
|
||||
(
|
||||
phase
|
||||
for phase in workflow_digest
|
||||
if str(phase.get("session_task_status") or "") != "complete"
|
||||
),
|
||||
workflow_digest[0] if workflow_digest else {},
|
||||
)
|
||||
return {
|
||||
"status": "complete" if task_count and int(status_counts.get("complete") or 0) == task_count else "attention_needed",
|
||||
"current_session": current_session,
|
||||
"task_count": task_count,
|
||||
"completed_task_count": int(status_counts.get("complete") or 0),
|
||||
"in_progress_task_count": int(status_counts.get("in_progress") or 0),
|
||||
"pending_task_count": int(status_counts.get("pending") or 0),
|
||||
"status_counts": status_counts,
|
||||
"next_task_phase": next_phase.get("phase"),
|
||||
"next_task_label": next_phase.get("session_task_label"),
|
||||
"next_task_prompt": next_phase.get("session_task_prompt"),
|
||||
"completion_ratio": (
|
||||
(int(status_counts.get("complete") or 0) / task_count)
|
||||
if task_count
|
||||
else 0.0
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _build_decision_assistant_explanation_drilldown(
|
||||
*,
|
||||
decision_packet: dict,
|
||||
@@ -2725,6 +2797,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_preference_profile = _build_digest_preference_profile(
|
||||
personalized_reminders=personalized_reminders,
|
||||
digest_history_summary=digest_history_summary,
|
||||
)
|
||||
digest_review_back_actions = _build_digest_review_back_actions(
|
||||
digest_history_preview=digest_history_preview,
|
||||
digest_history_summary=digest_history_summary,
|
||||
@@ -2742,6 +2818,7 @@ def _build_decision_assistant_digest_surface(
|
||||
"high_value_reminder_count": len(high_value_reminders),
|
||||
"important_change_filter": _digest_important_change_filter(personalized_reminders),
|
||||
"importance_templates": _digest_importance_templates(),
|
||||
"digest_preference_profile": digest_preference_profile,
|
||||
"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),
|
||||
@@ -2821,6 +2898,48 @@ def _digest_importance_templates() -> list[dict]:
|
||||
]
|
||||
|
||||
|
||||
def _build_digest_preference_profile(*, personalized_reminders: list[dict], digest_history_summary: dict) -> dict:
|
||||
template_counts = _digest_importance_template_counts(personalized_reminders)
|
||||
preferred_template = next(
|
||||
(
|
||||
template
|
||||
for template, _count in sorted(
|
||||
template_counts.items(),
|
||||
key=lambda row: (row[1], row[0]),
|
||||
reverse=True,
|
||||
)
|
||||
),
|
||||
None,
|
||||
)
|
||||
high_value_count = sum(1 for item in personalized_reminders if bool(item.get("high_value")))
|
||||
default_mode = "high_value_only" if high_value_count else "all_reminders"
|
||||
if preferred_template == "risk_first_change":
|
||||
default_mode = "risk_first"
|
||||
return {
|
||||
"status": "active" if personalized_reminders else "idle",
|
||||
"default_mode": default_mode,
|
||||
"available_modes": ["all_reminders", "high_value_only", "risk_first"],
|
||||
"preferred_history_kind": digest_history_summary.get("recommended_kind"),
|
||||
"preferred_importance_template": preferred_template,
|
||||
"high_value_only_recommended": high_value_count > 0,
|
||||
"profile_reason": _digest_preference_profile_reason(
|
||||
preferred_template=preferred_template,
|
||||
high_value_count=high_value_count,
|
||||
recommended_kind=str(digest_history_summary.get("recommended_kind") or ""),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _digest_preference_profile_reason(*, preferred_template: str | None, high_value_count: int, recommended_kind: str) -> str:
|
||||
if preferred_template == "risk_first_change":
|
||||
return "Risk-first reminders are dominant in the current digest mix."
|
||||
if high_value_count > 0:
|
||||
return f"{high_value_count} high-value reminder(s) are active; a tighter digest view is recommended."
|
||||
if recommended_kind:
|
||||
return f"{recommended_kind} is the leading history kind for this digest loop."
|
||||
return "No strong preference signal is available yet."
|
||||
|
||||
|
||||
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 "")
|
||||
|
||||
+78
-3
@@ -33,6 +33,7 @@ const ctx = canvas.getContext('2d');
|
||||
|
||||
let autoRefreshTimer = null;
|
||||
let assistantHistoryKindFilter = loadAssistantHistoryKindFilter();
|
||||
let assistantDigestPreferenceMode = loadAssistantDigestPreferenceMode();
|
||||
let assistantScanHandoffMarker = loadAssistantScanHandoffMarker();
|
||||
|
||||
async function fetchJson(url, options = {}) {
|
||||
@@ -96,6 +97,23 @@ function persistAssistantHistoryKindFilter(value) {
|
||||
}
|
||||
}
|
||||
|
||||
function loadAssistantDigestPreferenceMode() {
|
||||
try {
|
||||
return window.localStorage?.getItem('assistantDigestPreferenceMode') || 'all_reminders';
|
||||
} catch {
|
||||
return 'all_reminders';
|
||||
}
|
||||
}
|
||||
|
||||
function persistAssistantDigestPreferenceMode(value) {
|
||||
assistantDigestPreferenceMode = value || 'all_reminders';
|
||||
try {
|
||||
window.localStorage?.setItem('assistantDigestPreferenceMode', assistantDigestPreferenceMode);
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
function loadAssistantScanHandoffMarker() {
|
||||
try {
|
||||
const raw = window.localStorage?.getItem('assistantScanHandoffMarker');
|
||||
@@ -159,6 +177,21 @@ function scanCardActionLabel(sectionId) {
|
||||
return 'Open';
|
||||
}
|
||||
|
||||
function filterAssistantReminders(reminders, mode) {
|
||||
if (!Array.isArray(reminders) || !reminders.length) return [];
|
||||
if (mode === 'high_value_only') {
|
||||
return reminders.filter((item) => Boolean(item.high_value));
|
||||
}
|
||||
if (mode === 'risk_first') {
|
||||
return reminders.filter((item) =>
|
||||
item.source === 'no_trade_reason_items'
|
||||
|| item.importance_template === 'risk_first_change'
|
||||
|| item.severity === 'high'
|
||||
);
|
||||
}
|
||||
return reminders;
|
||||
}
|
||||
|
||||
function prefillOperatorNote(body) {
|
||||
revealPanel(operatorConsolePanel);
|
||||
const textarea = operatorConsolePanel.querySelector('textarea');
|
||||
@@ -465,10 +498,12 @@ function renderDecisionAssistant(summary) {
|
||||
const scanHandoffSummary = summary.scan_handoff_summary || null;
|
||||
const workflowDigest = summary.workflow_digest || [];
|
||||
const workflowRhythmSummary = summary.workflow_rhythm_summary || null;
|
||||
const workflowSessionCompletionSummary = summary.workflow_session_completion_summary || null;
|
||||
const opportunities = summary.top_opportunities || [];
|
||||
const watchlistTiers = summary.candidate_watchlist_tiers || {};
|
||||
const watchlistSummary = summary.candidate_watchlist_summary || {};
|
||||
const noTradeItems = summary.no_trade_reason_items || [];
|
||||
const evidenceChain = summary.evidence_chain || [];
|
||||
const decisionTimeline = summary.decision_timeline || [];
|
||||
const explanationDrilldown = summary.explanation_drilldown || [];
|
||||
const reasonCitations = summary.reason_citations || [];
|
||||
@@ -480,9 +515,12 @@ function renderDecisionAssistant(summary) {
|
||||
const digestHistoryGroups = summary.digest_history_groups || [];
|
||||
const digestSurface = summary.digest_surface || null;
|
||||
const digestHistoryBrowser = summary.digest_history_browser || digestSurface?.history_browser || null;
|
||||
const digestPreferenceProfile = digestSurface?.digest_preference_profile || null;
|
||||
const setupLearningRollup = summary.setup_learning_rollup || null;
|
||||
const continuousWorkspaceRail = summary.continuous_workspace_rail || [];
|
||||
const continuousWorkspaceSummary = summary.continuous_workspace_summary || null;
|
||||
const visibleReminders = filterAssistantReminders(personalizedReminders, assistantDigestPreferenceMode);
|
||||
const reminderCards = visibleReminders.length ? visibleReminders : personalizedReminders;
|
||||
const savedHandoffAge = resolveAssistantHandoffAge(assistantScanHandoffMarker, scanHandoffSummary?.default_stale_after_seconds);
|
||||
const assistantSections = [
|
||||
scanSummaryCards.length ? { title: 'Scan' } : null,
|
||||
@@ -490,6 +528,7 @@ function renderDecisionAssistant(summary) {
|
||||
digestSurface ? { title: 'Digest' } : null,
|
||||
reviewEntrypoints ? { title: 'Review Entry' } : null,
|
||||
opportunities.length ? { title: 'Opportunities' } : null,
|
||||
evidenceChain.length ? { title: 'Evidence' } : null,
|
||||
(watchlistTiers.review_now || []).length ? { title: 'Watchlist: Review Now' } : null,
|
||||
noTradeItems.length ? { title: 'No-Trade Reasons' } : null,
|
||||
setupLearningRollup ? { title: 'Setup Replay' } : null,
|
||||
@@ -763,6 +802,12 @@ function renderDecisionAssistant(summary) {
|
||||
['reminders', String(digestSurface.reminder_count ?? 0)],
|
||||
['high priority reminders', String(digestSurface.high_priority_count ?? 0)],
|
||||
['high value reminders', String(digestSurface.high_value_reminder_count ?? 0)],
|
||||
['digest preference', assistantDigestPreferenceMode || 'all_reminders'],
|
||||
['preference default', digestPreferenceProfile?.default_mode || 'n/a'],
|
||||
['preferred history kind', digestPreferenceProfile?.preferred_history_kind || 'n/a'],
|
||||
['preferred template', digestPreferenceProfile?.preferred_importance_template || 'n/a'],
|
||||
['preference reason', digestPreferenceProfile?.profile_reason || 'n/a'],
|
||||
['visible reminders', String(reminderCards.length)],
|
||||
['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'],
|
||||
@@ -835,9 +880,21 @@ function renderDecisionAssistant(summary) {
|
||||
);
|
||||
decisionAssistantPanel.appendChild(browserBar);
|
||||
}
|
||||
if (personalizedReminders.length) {
|
||||
if (digestPreferenceProfile?.available_modes?.length) {
|
||||
const preferenceBar = buildActionBar(
|
||||
digestPreferenceProfile.available_modes.map((mode) => ({
|
||||
label: mode === 'all_reminders' ? 'All Reminders' : (mode === 'high_value_only' ? 'High Value' : 'Risk First'),
|
||||
onClick: () => {
|
||||
persistAssistantDigestPreferenceMode(mode);
|
||||
renderDecisionAssistant(window.__latestDecisionAssistantSummary || summary);
|
||||
},
|
||||
}))
|
||||
);
|
||||
decisionAssistantPanel.appendChild(preferenceBar);
|
||||
}
|
||||
if (reminderCards.length) {
|
||||
const reminderGrid = createElement('div', 'cards assistant-tight-grid');
|
||||
personalizedReminders.forEach((item) => {
|
||||
reminderCards.forEach((item) => {
|
||||
reminderGrid.appendChild(
|
||||
buildItemCard(
|
||||
item.title || 'reminder',
|
||||
@@ -1159,6 +1216,21 @@ function renderDecisionAssistant(summary) {
|
||||
])
|
||||
);
|
||||
}
|
||||
if (workflowSessionCompletionSummary) {
|
||||
decisionAssistantPanel.appendChild(
|
||||
buildInfoGrid([
|
||||
['session tasks', workflowSessionCompletionSummary.status || 'idle'],
|
||||
['session', workflowSessionCompletionSummary.current_session || 'n/a'],
|
||||
['task count', String(workflowSessionCompletionSummary.task_count ?? 0)],
|
||||
['tasks complete', String(workflowSessionCompletionSummary.completed_task_count ?? 0)],
|
||||
['tasks in progress', String(workflowSessionCompletionSummary.in_progress_task_count ?? 0)],
|
||||
['tasks pending', String(workflowSessionCompletionSummary.pending_task_count ?? 0)],
|
||||
['next session task', workflowSessionCompletionSummary.next_task_label || 'n/a'],
|
||||
['next session prompt', workflowSessionCompletionSummary.next_task_prompt || 'n/a'],
|
||||
['completion ratio', formatPercent(workflowSessionCompletionSummary.completion_ratio)],
|
||||
])
|
||||
);
|
||||
}
|
||||
const workflowGrid = createElement('div', 'cards');
|
||||
workflowDigest.forEach((item) => {
|
||||
workflowGrid.appendChild(
|
||||
@@ -1171,6 +1243,9 @@ function renderDecisionAssistant(summary) {
|
||||
['rhythm status', item.rhythm_status || 'n/a'],
|
||||
['rhythm pending', String(item.rhythm_pending_count ?? 0)],
|
||||
['next rhythm action', item.next_rhythm_action || 'n/a'],
|
||||
['session task', item.session_task_label || 'n/a'],
|
||||
['session task status', item.session_task_status || 'n/a'],
|
||||
['session task prompt', item.session_task_prompt || 'n/a'],
|
||||
['checklist', formatList((item.rhythm_checklist || []).map((row) => `${row.label || row.check_id}: ${row.status}`))],
|
||||
]
|
||||
)
|
||||
@@ -1619,7 +1694,6 @@ function renderDecisionAssistant(summary) {
|
||||
decisionAssistantPanel.appendChild(historyGrid);
|
||||
}
|
||||
const items = summary.top_priority_items || [];
|
||||
const evidenceChain = summary.evidence_chain || [];
|
||||
if (!items.length) {
|
||||
decisionAssistantPanel.appendChild(createElement('div', 'empty-state', 'No priority items yet. Monitor and wait.'));
|
||||
} else {
|
||||
@@ -1642,6 +1716,7 @@ function renderDecisionAssistant(summary) {
|
||||
if (!evidenceChain.length) {
|
||||
return;
|
||||
}
|
||||
appendSectionTitle(decisionAssistantPanel, 'Evidence');
|
||||
const evidenceGrid = createElement('div', 'cards');
|
||||
evidenceChain.forEach((item) => {
|
||||
evidenceGrid.appendChild(
|
||||
|
||||
@@ -172,6 +172,10 @@ 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("workflow_session_completion_summary", summary)
|
||||
self.assertEqual(summary["workflow_session_completion_summary"]["task_count"], 3)
|
||||
self.assertIn("next_task_label", summary["workflow_session_completion_summary"])
|
||||
self.assertIn("completion_ratio", summary["workflow_session_completion_summary"])
|
||||
self.assertIn("continuous_workspace_rail", summary)
|
||||
self.assertIn("continuous_workspace_status", summary)
|
||||
self.assertIn("continuous_workspace_summary", summary)
|
||||
@@ -185,6 +189,9 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
self.assertIn("rhythm_checklist", summary["workflow_digest"][0])
|
||||
self.assertIn("rhythm_status", summary["workflow_digest"][0])
|
||||
self.assertIn("next_rhythm_action", summary["workflow_digest"][0])
|
||||
self.assertIn("session_task_label", summary["workflow_digest"][0])
|
||||
self.assertIn("session_task_status", summary["workflow_digest"][0])
|
||||
self.assertIn("session_task_prompt", summary["workflow_digest"][0])
|
||||
self.assertEqual(summary["decision_timeline"][0]["title"], "Current Packet")
|
||||
self.assertEqual(summary["evidence_chain"][0]["title"], "Decision Gate")
|
||||
self.assertEqual(summary["evidence_chain"][1]["title"], "Launch Mode")
|
||||
@@ -319,6 +326,10 @@ class OperatorWorkspaceServiceTests(unittest.TestCase):
|
||||
self.assertIn("high_value_reminder_count", summary["digest_surface"])
|
||||
self.assertIn("important_change_filter", summary["digest_surface"])
|
||||
self.assertIn("importance_templates", summary["digest_surface"])
|
||||
self.assertIn("digest_preference_profile", summary["digest_surface"])
|
||||
self.assertIn("default_mode", summary["digest_surface"]["digest_preference_profile"])
|
||||
self.assertIn("available_modes", summary["digest_surface"]["digest_preference_profile"])
|
||||
self.assertIn("profile_reason", summary["digest_surface"]["digest_preference_profile"])
|
||||
self.assertIn("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"])
|
||||
|
||||
Reference in New Issue
Block a user