From b291e42ca253821566ff116025aa535915a9b188 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 15 May 2026 18:16:47 +0800 Subject: [PATCH] Persist workflow archive bridge state --- SESSION.md | 4 +- TASKS.md | 3 +- ...求文档剩余功能与延期项清单_2026-05-14_v1.md | 10 +++ .../operator/operator_workspace_service.py | 16 ++++ src/web/app.js | 87 +++++++++++++++++++ tests/unit/test_api_static_server.py | 2 + tests/unit/test_operator_workspace_service.py | 9 ++ 7 files changed, 129 insertions(+), 2 deletions(-) diff --git a/SESSION.md b/SESSION.md index 10a0f2b..65c6651 100644 --- a/SESSION.md +++ b/SESSION.md @@ -718,12 +718,13 @@ Automation execution-layer PXX remains closed and its known scope stays frozen. - Latest O2 validation passed: 74 focused archive/API/static/operator tests, 732 full unit tests, 73 script static checks, `node --check src/web/app.js`, live deploy completed, remote ops smoke passed 9 checks, and remote O2 smoke confirmed persisted digest/workflow baselines, long-range trend stories, and deployed `Save Baseline` / `Save Workflow Baseline` UI strings on the live host. - Latest B7 validation passed: 74 focused archive/API/static/operator tests, 732 full unit tests, 73 script static checks, `node --check src/web/app.js`, live deploy completed, remote ops smoke passed 9 checks, and remote B7 smoke confirmed persisted archive defaults, cross-profile preset retrieval, and deployed `Save Digest Defaults` / `Use Saved Digest Defaults` / `Save Workflow Defaults` / `Use Saved Workflow Defaults` UI strings on the live host. - Latest E12 validation passed: 16 focused external-context/static tests, 732 full unit tests, 73 script static checks, `node --check src/web/app.js`, live deploy completed, remote ops smoke passed 9 checks, and remote E12 smoke confirmed screenshot readiness summary, capture-surface screenshot status fields, and deployed `Open Screenshot Surface` UI strings on the live host. +- Latest W9 validation passed: 74 focused workflow bridge/API/static/operator tests, 732 full unit tests, 73 script static checks, `node --check src/web/app.js`, live deploy completed, remote ops smoke passed 9 checks, and remote W9 smoke confirmed persisted workflow bridge state, persisted handoff/bundle starters, and deployed `Persist Bridge State` plus `bridge persistence` cockpit summary on the live host. - Refreshed `docs/AI_ICT_??????????????????????2026-05-14_v1.md` after closing Phases A-E; the active remaining mainline backlog is now archive long-range trend deepening, archive deep-retrieval deepening, external context deep visual evidence extensions, and workflow archive deep bridge extensions. - Real-live order submission remains intentionally disabled because the OKX order adapter is still not a real-live submitter. ## Exact Next Step -Use `docs/AI_ICT_???????????????????2026-05-14_v1.md` as the completed enhancement record for Phases A-E. The active backlog source is now `docs/AI_ICT_??????????????????????2026-05-14_v1.md`. `B5 archive keyword search / export starter slice`, `W7 workflow archive replay / handoff / export bridge starter slice`, `E10 external context visual evidence / provider health starter slice`, `O1 archive long-range trend / baseline pin starter slice`, `B6 archive deep retrieval / structured export follow-up starter slice`, `E11 external context deep visual evidence / capture surface follow-up starter slice`, `W8 workflow archive deep bridge extension starter slice`, `O2 archive long-range trend deepening / baseline persistence starter slice`, `B7 archive deep retrieval deepening / preset persistence starter slice`, and `E12 external context visual evidence extension / screenshot readiness starter slice` are now closed. Start from `W9 workflow archive deep bridge extension / bundle export persistence slice`, keep work on the analysis-assistant mainline only, and continue treating real-trading scope as deferred. PXX remains closed and you still must not create P25/P26 automatically or enable real trading. +Use `docs/AI_ICT_???????????????????2026-05-14_v1.md` as the completed enhancement record for Phases A-E. The active backlog source is now `docs/AI_ICT_??????????????????????2026-05-14_v1.md`. `B5 archive keyword search / export starter slice`, `W7 workflow archive replay / handoff / export bridge starter slice`, `E10 external context visual evidence / provider health starter slice`, `O1 archive long-range trend / baseline pin starter slice`, `B6 archive deep retrieval / structured export follow-up starter slice`, `E11 external context deep visual evidence / capture surface follow-up starter slice`, `W8 workflow archive deep bridge extension starter slice`, `O2 archive long-range trend deepening / baseline persistence starter slice`, `B7 archive deep retrieval deepening / preset persistence starter slice`, `E12 external context visual evidence extension / screenshot readiness starter slice`, and `W9 workflow archive deep bridge extension / bundle export persistence slice` are now closed. Start from `O3 archive long-range trend unification / operator overview slice`, keep work on the analysis-assistant mainline only, and continue treating real-trading scope as deferred. PXX remains closed and you still must not create P25/P26 automatically or enable real trading. ## Validation Commands Used Recently @@ -781,3 +782,4 @@ D:\ai-exchange\.venv\Scripts\python.exe scripts\run_automation_execution_closure + diff --git a/TASKS.md b/TASKS.md index d4e0f52..ac08fc8 100644 --- a/TASKS.md +++ b/TASKS.md @@ -113,7 +113,8 @@ - [x] O2 archive long-range trend deepening / baseline persistence starter slice - [x] B7 archive deep retrieval deepening / preset persistence starter slice - [x] E12 external context visual evidence extension / screenshot readiness starter slice -- [ ] W9 workflow archive deep bridge extension / bundle export persistence slice +- [x] W9 workflow archive deep bridge extension / bundle export persistence slice +- [ ] O3 archive long-range trend unification / operator overview slice - [x] A1 independent preferences surface / dedicated settings section - [x] A2 preference profile create / duplicate / rename / delete diff --git a/docs/AI_ICT_需求文档剩余功能与延期项清单_2026-05-14_v1.md b/docs/AI_ICT_需求文档剩余功能与延期项清单_2026-05-14_v1.md index 90ccef0..8937360 100644 --- a/docs/AI_ICT_需求文档剩余功能与延期项清单_2026-05-14_v1.md +++ b/docs/AI_ICT_需求文档剩余功能与延期项清单_2026-05-14_v1.md @@ -145,6 +145,16 @@ - 下一刀最小闭环切片改为: - `W9 workflow archive deep bridge extension / bundle export persistence slice` +## 11. 2026-05-15 W9 Addendum + +- `W9 workflow archive deep bridge extension / bundle export persistence slice` 已完成。 +- 当前已具备: + - persisted workflow bridge state + - handoff / replay / artifact bundle starter persistence + - workflow bridge persistence summary in cockpit +- 下一刀最小闭环切片改为: + - `O3 archive long-range trend unification / operator overview slice` + - A-E 璁″垝宸茬粡鍏ㄩ儴瀹屾垚锛屼笉璇ュ啀浣滀负涓?backlog 閲嶅鎺ㄨ繘銆?- 褰撳墠鐪熸鏈畬鎴愮殑锛屽凡缁忎粠鈥滃熀纭€浜у搧鍖栤€濆垏鎹㈠埌鈥渁rchive 娣卞寲銆乪xternal evidence 寮哄寲銆佽繍钀ラ暱鏈熻鍥锯€濊繖涓€绾с€?- 鐪熷疄浜ゆ槗銆乴ive adapter銆佷笅鍗曟潈闄愮浉鍏冲唴瀹圭户缁繚鎸佸欢鏈燂紝涓嶅簲娣峰洖褰撳墠鍒嗘瀽鍔╂墜涓荤嚎銆? diff --git a/src/services/operator/operator_workspace_service.py b/src/services/operator/operator_workspace_service.py index acc3dc0..0d9fddc 100644 --- a/src/services/operator/operator_workspace_service.py +++ b/src/services/operator/operator_workspace_service.py @@ -321,6 +321,7 @@ class DecisionAssistantSummaryService: ) digest_archive_compare = _build_decision_assistant_digest_archive_compare(refreshed_digest_history) workflow_archive_compare = _build_decision_assistant_workflow_archive_compare(refreshed_workflow_history) + workflow_bridge_persistence_summary = _decision_assistant_workflow_bridge_state(persisted_digest_preference) summary["digest_history_archive"] = digest_history_archive summary["digest_archive_compare"] = digest_archive_compare summary["digest_persistence_status"] = persistence_result.get("status") @@ -329,6 +330,7 @@ class DecisionAssistantSummaryService: summary["workflow_archive_operations_summary"] = workflow_history_archive.get("operations_summary") summary["workflow_archive_multi_day_drift_review"] = workflow_history_archive.get("multi_day_drift_review") summary["workflow_archive_bridge_summary"] = workflow_history_archive.get("bridge_summary") + summary["workflow_archive_bridge_persistence_summary"] = workflow_bridge_persistence_summary summary["workflow_persistence_status"] = workflow_persistence_result.get("status") digest_surface = dict(summary.get("digest_surface") or {}) digest_surface["digest_history_archive"] = digest_history_archive @@ -345,6 +347,7 @@ class DecisionAssistantSummaryService: workflow_artifact_summary["workflow_archive_operations_summary"] = workflow_history_archive.get("operations_summary") workflow_artifact_summary["workflow_archive_multi_day_drift_review"] = workflow_history_archive.get("multi_day_drift_review") workflow_artifact_summary["workflow_archive_bridge_summary"] = workflow_history_archive.get("bridge_summary") + workflow_artifact_summary["workflow_archive_bridge_persistence_summary"] = workflow_bridge_persistence_summary workflow_artifact_summary["workflow_persistence_status"] = workflow_persistence_result.get("status") summary["workflow_artifact_summary"] = workflow_artifact_summary return summary @@ -702,6 +705,19 @@ def _decision_assistant_archive_defaults(persisted_digest_preference: dict) -> d } +def _decision_assistant_workflow_bridge_state(persisted_digest_preference: dict) -> dict: + payload = _decision_assistant_preference_payload(persisted_digest_preference) + state = dict(payload.get("workflow_bridge_state") or {}) + return { + "status": "persisted" if state else "idle", + "updated_ts": state.get("updated_ts"), + "query_context": dict(state.get("query_context") or {}), + "handoff_packet_starter": dict(state.get("handoff_packet_starter") or {}), + "replay_note_bridge_summary": dict(state.get("replay_note_bridge_summary") or {}), + "artifact_bundle_summary": dict(state.get("artifact_bundle_summary") or {}), + } + + def _decision_assistant_importance_template_thresholds(*, persisted_digest_preference: dict) -> dict: payload = _decision_assistant_preference_payload(persisted_digest_preference) thresholds = dict(payload.get("importance_templates") or {}) diff --git a/src/web/app.js b/src/web/app.js index 8d70590..9eefe59 100644 --- a/src/web/app.js +++ b/src/web/app.js @@ -245,6 +245,47 @@ async function persistAssistantArchiveBaselinesServer({ profileKey, digestSnapsh }); } +async function persistWorkflowArchiveBridgeStateServer({ summary, exportPayload, profileKey }) { + const digestSurface = summary?.digest_surface || {}; + const digestProfile = digestSurface.digest_preference_profile || {}; + const saved = await persistAssistantDigestPreferenceServer({ + profileKey: profileKey || assistantDigestProfileKey || 'local_operator', + digestMode: assistantPreferenceDraft?.digest_mode || digestProfile.persisted_digest_mode || 'all_reminders', + preferredHistoryKind: assistantPreferenceDraft?.preferred_history_kind === 'all' + ? null + : (assistantPreferenceDraft?.preferred_history_kind || digestProfile.preferred_history_kind || null), + importanceThreshold: assistantPreferenceDraft?.importance_threshold || digestProfile.importance_threshold || 70, + payload: { + profile_label: assistantPreferenceDraft?.profile_label || digestProfile.profile_label || profileKey || 'local_operator', + history_window_hours: assistantPreferenceDraft?.history_window_hours || digestProfile.history_window_hours || 24, + importance_templates: { ...((assistantPreferenceDraft && assistantPreferenceDraft.importance_templates) || {}) }, + archive_defaults: { ...((assistantPreferenceDraft && assistantPreferenceDraft.archive_defaults) || digestProfile.archive_defaults || {}) }, + workflow_bridge_state: { + handoff_packet_starter: exportPayload?.handoff_packet_starter || {}, + replay_note_bridge_summary: exportPayload?.replay_note_bridge_summary || {}, + artifact_bundle_summary: exportPayload?.artifact_bundle_summary || {}, + query_context: { + profile_key: assistantWorkflowArchiveState.profile_key || profileKey || assistantDigestProfileKey || 'local_operator', + profile_scope: assistantWorkflowArchiveState.profile_scope || 'selected_profile', + query_preset: assistantWorkflowArchiveState.query_preset || '', + current_session: assistantWorkflowArchiveState.current_session || '', + next_phase: assistantWorkflowArchiveState.next_phase || '', + next_artifact_kind: assistantWorkflowArchiveState.next_artifact_kind || '', + query_text: assistantWorkflowArchiveState.query_text || '', + }, + updated_ts: new Date().toISOString(), + }, + }, + }); + if (saved) { + assistantPreferenceDraft = { + ...(assistantPreferenceDraft || {}), + archive_defaults: { ...((assistantPreferenceDraft && assistantPreferenceDraft.archive_defaults) || digestProfile.archive_defaults || {}) }, + }; + } + return saved; +} + function loadAssistantScanHandoffMarker() { try { const raw = window.localStorage?.getItem('assistantScanHandoffMarker'); @@ -1011,6 +1052,9 @@ function renderDecisionAssistant(summary) { || summary.workflow_artifact_summary?.workflow_archive_bridge_summary || summary.workflow_archive_bridge_summary || null; + const workflowArchiveBridgePersistenceSummary = summary.workflow_artifact_summary?.workflow_archive_bridge_persistence_summary + || summary.workflow_archive_bridge_persistence_summary + || null; const workflowArchiveDailyGroups = workflowArchiveData?.daily_groups || []; const workflowArchiveSessionGroups = workflowArchiveData?.session_groups || []; const workflowArchiveBridgeContext = assistantWorkflowArchiveBridgeContext; @@ -2641,6 +2685,19 @@ function renderDecisionAssistant(summary) { ]) ); } + if (workflowArchiveBridgePersistenceSummary) { + decisionAssistantPanel.appendChild( + buildInfoGrid([ + ['bridge persistence', workflowArchiveBridgePersistenceSummary.status || 'idle'], + ['bridge saved at', formatTimestamp(workflowArchiveBridgePersistenceSummary.updated_ts)], + ['bridge saved preset', workflowArchiveBridgePersistenceSummary.query_context?.query_preset || 'custom'], + ['bridge saved scope', workflowArchiveBridgePersistenceSummary.query_context?.profile_scope || 'selected_profile'], + ['bridge saved handoff', workflowArchiveBridgePersistenceSummary.handoff_packet_starter?.headline || 'n/a'], + ['bridge saved replay', workflowArchiveBridgePersistenceSummary.replay_note_bridge_summary?.note_title || 'n/a'], + ['bridge saved bundle', workflowArchiveBridgePersistenceSummary.artifact_bundle_summary?.starter_export_prompt || 'n/a'], + ]) + ); + } if (workflowArchiveBridgeContext) { decisionAssistantPanel.appendChild( buildActionBar([ @@ -3129,6 +3186,36 @@ function renderDecisionAssistant(summary) { }); }, }, + { + label: 'Persist Bridge State', + onClick: async () => { + const exportPayload = await fetchJson(`/trading/workflow-archive/export?${queryString({ + instrument_id: Number(instrumentIdInput.value || 1), + profile_key: assistantWorkflowArchiveState.profile_key || selectedProfileKey, + profile_scope: assistantWorkflowArchiveState.profile_scope || 'selected_profile', + query_preset: assistantWorkflowArchiveState.query_preset || '', + current_session: assistantWorkflowArchiveState.current_session || '', + next_phase: assistantWorkflowArchiveState.next_phase || '', + next_artifact_kind: assistantWorkflowArchiveState.next_artifact_kind || '', + query_text: assistantWorkflowArchiveState.query_text || '', + created_after: assistantWorkflowArchiveState.created_after || '', + created_before: assistantWorkflowArchiveState.created_before || '', + limit: Math.max(Number(assistantWorkflowArchiveState.limit || 12), Number(workflowArchiveData?.items?.length || 0)), + left_id: assistantWorkflowArchiveState.selected_left_id, + right_id: assistantWorkflowArchiveState.selected_right_id, + })}`); + const saved = await persistWorkflowArchiveBridgeStateServer({ + summary, + exportPayload, + profileKey: assistantWorkflowArchiveState.profile_key || selectedProfileKey, + }); + if (!saved) { + showErrorPanel('workflow_bridge_state_persist_failed'); + return; + } + await refreshDashboard(); + }, + }, { label: 'Export Artifact Bundle', onClick: async () => { diff --git a/tests/unit/test_api_static_server.py b/tests/unit/test_api_static_server.py index 9d6dd97..64bcad1 100644 --- a/tests/unit/test_api_static_server.py +++ b/tests/unit/test_api_static_server.py @@ -186,6 +186,8 @@ class ApiStaticServerTests(unittest.TestCase): "Prefill Compare Replay Note", "Export Handoff Starter", "Export Artifact Bundle", + "Persist Bridge State", + "bridge persistence", ): self.assertIn(expected, app_js) diff --git a/tests/unit/test_operator_workspace_service.py b/tests/unit/test_operator_workspace_service.py index a263bfd..0d96e04 100644 --- a/tests/unit/test_operator_workspace_service.py +++ b/tests/unit/test_operator_workspace_service.py @@ -194,6 +194,7 @@ class OperatorWorkspaceServiceTests(unittest.TestCase): self.assertIn("workflow_archive_operations_summary", summary["workflow_artifact_summary"]) self.assertIn("workflow_archive_multi_day_drift_review", summary["workflow_artifact_summary"]) self.assertIn("workflow_archive_bridge_summary", summary["workflow_artifact_summary"]) + self.assertIn("workflow_archive_bridge_persistence_summary", summary["workflow_artifact_summary"]) self.assertIn("workflow_persistence_status", summary["workflow_artifact_summary"]) self.assertEqual(summary["workflow_artifact_summary"]["workflow_archive_operations_summary"]["status"], "active") self.assertEqual( @@ -201,6 +202,7 @@ class OperatorWorkspaceServiceTests(unittest.TestCase): "insufficient_history", ) self.assertEqual(summary["workflow_artifact_summary"]["workflow_archive_bridge_summary"]["status"], "active") + self.assertEqual(summary["workflow_artifact_summary"]["workflow_archive_bridge_persistence_summary"]["status"], "persisted") self.assertIn("long_range_trend_summary", summary["workflow_artifact_summary"]["workflow_history_archive"]) self.assertIn("baseline_pin_summary", summary["workflow_artifact_summary"]["workflow_history_archive"]) self.assertEqual(summary["workflow_artifact_summary"]["workflow_history_archive"]["long_range_trend_summary"]["status"], "active") @@ -979,6 +981,13 @@ class _DecisionAssistantRepo: "workflow_snapshot_id": 10, "updated_ts": "2026-05-12T02:04:00+00:00", }, + "workflow_bridge_state": { + "handoff_packet_starter": {"headline": "Saved handoff starter"}, + "replay_note_bridge_summary": {"note_title": "Saved replay note"}, + "artifact_bundle_summary": {"starter_export_prompt": "Saved artifact bundle"}, + "query_context": {"profile_scope": "selected_profile", "query_preset": "intraday_recent"}, + "updated_ts": "2026-05-12T02:04:30+00:00", + }, }, "updated_ts": "2026-05-12T02:05:00+00:00", }