Persist workflow archive bridge state

This commit is contained in:
Codex
2026-05-15 18:16:47 +08:00
parent 4bfdd271bf
commit b291e42ca2
7 changed files with 129 additions and 2 deletions
+3 -1
View File
@@ -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
+2 -1
View File
@@ -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
@@ -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銆佷笅鍗曟潈闄愮浉鍏冲唴瀹圭户缁繚鎸佸欢鏈燂紝涓嶅簲娣峰洖褰撳墠鍒嗘瀽鍔╂墜涓荤嚎銆?
@@ -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 {})
+87
View File
@@ -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 () => {
+2
View File
@@ -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)
@@ -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",
}