diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index 33cc82964..196d6f058 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -246,6 +246,8 @@ jobs: ;; apps/api/src/services/ai_agent_log_intelligence_integration_readback.py) ;; + apps/api/src/services/ai_agent_log_feedback_receipt_dry_run.py) + ;; apps/api/src/services/ai_agent_autonomous_runtime_control.py) ;; apps/api/src/services/awooop_ansible_audit_service.py) @@ -324,6 +326,8 @@ jobs: ;; apps/api/tests/test_ai_agent_log_intelligence_integration_readback_api.py) ;; + apps/api/tests/test_ai_agent_log_feedback_receipt_dry_run_api.py) + ;; apps/api/tests/test_ai_agent_autonomous_runtime_control.py) ;; apps/api/tests/test_awooop_truth_chain_service.py) @@ -498,6 +502,7 @@ jobs: src/services/awoooi_production_deploy_readback_blocker.py \ src/services/agent_replay_normalizer.py \ src/services/ai_agent_log_intelligence_integration_readback.py \ + src/services/ai_agent_log_feedback_receipt_dry_run.py \ src/services/ai_agent_autonomous_runtime_control.py \ src/services/awooop_ansible_audit_service.py \ src/services/awooop_ansible_check_mode_service.py \ @@ -542,6 +547,7 @@ jobs: PYTHONFAULTHANDLER=1 python3.11 -m pytest \ tests/test_agent_replay_normalizer.py \ tests/test_ai_agent_log_intelligence_integration_readback_api.py \ + tests/test_ai_agent_log_feedback_receipt_dry_run_api.py \ tests/test_ai_agent_autonomous_runtime_control.py \ tests/test_awooop_truth_chain_service.py \ tests/test_shadow_auto_approve.py \ diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 2f3b37a55..91f38e213 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -97,12 +97,12 @@ from src.services.ai_agent_interaction_learning_proof import ( from src.services.ai_agent_learning_writeback_approval_package import ( load_latest_ai_agent_learning_writeback_approval_package, ) -from src.services.ai_agent_log_feedback_receipt_dry_run import ( - load_latest_ai_agent_log_feedback_receipt_dry_run, -) from src.services.ai_agent_live_read_model_gate import ( load_latest_ai_agent_live_read_model_gate, ) +from src.services.ai_agent_log_feedback_receipt_dry_run import ( + load_latest_ai_agent_log_feedback_receipt_dry_run, +) from src.services.ai_agent_log_intelligence_integration_readback import ( load_latest_ai_agent_log_intelligence_integration_readback, ) @@ -324,8 +324,8 @@ from src.services.backup_restore_drill_approval_package_template import ( ) from src.services.credential_escrow_evidence_intake_readiness import ( load_latest_credential_escrow_evidence_intake_readiness, - validate_credential_escrow_evidence_refs, validate_credential_escrow_evidence_owner_response, + validate_credential_escrow_evidence_refs, ) from src.services.delivery_closure_workbench import ( load_delivery_closure_workbench, diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index ab7d6636c..62591c0fd 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -164,8 +164,8 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["source_id"] == "reboot_auto_recovery_slo_scorecard" assert lane["status"] == "blocked_reboot_auto_recovery_slo_not_ready" - assert lane["blocker_count"] == 6 - assert lane["completion_percent"] == 45 + assert lane["blocker_count"] == 5 + assert lane["completion_percent"] == 55 assert lane["metric"]["kind"] == "reboot_auto_recovery_slo" assert lane["metric"]["workplan_id"] == "P0-006" assert lane["metric"]["target_minutes"] == 10 @@ -178,9 +178,9 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["metric"]["product_data_green"] is False assert lane["metric"]["backup_core_green"] is True assert lane["metric"]["stockplatform_freshness_status"] == "blocked" - assert lane["metric"]["stockplatform_ingestion_status"] == "blocked" - assert lane["metric"]["stockplatform_freshness_blocker_count"] == 2 - assert lane["metric"]["stockplatform_ingestion_blocker_count"] == 1 + assert lane["metric"]["stockplatform_ingestion_status"] == "ok" + assert lane["metric"]["stockplatform_freshness_blocker_count"] == 1 + assert lane["metric"]["stockplatform_ingestion_blocker_count"] == 0 assert lane["metric"]["stockplatform_final_retry_window_passed"] is False assert lane["metric"]["stockplatform_controlled_recovery_gate_required"] is False assert lane["metric"]["host_reboot_performed"] is False @@ -189,7 +189,7 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["metric"]["secret_value_collection_allowed"] is False assert "stockplatform_freshness_blocked" in lane["metric"]["active_blockers"] assert lane["next_action"] == ( - "wait_next_stockplatform_retry_window_then_rerun_slo_verify_only_" + "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_" "no_reboot_no_db_write" ) @@ -213,8 +213,8 @@ def _assert_delivery_workbench_shape(data: dict): "blocked_reboot_auto_recovery_slo_not_ready" ) assert data["summary"]["reboot_auto_recovery_workplan_id"] == "P0-006" - assert data["summary"]["reboot_auto_recovery_readiness_percent"] == 45 - assert data["summary"]["reboot_auto_recovery_active_blocker_count"] == 6 + assert data["summary"]["reboot_auto_recovery_readiness_percent"] == 55 + assert data["summary"]["reboot_auto_recovery_active_blocker_count"] == 5 assert data["summary"]["reboot_auto_recovery_can_claim_slo"] is False assert data["summary"]["reboot_auto_recovery_service_green"] is False assert data["summary"]["reboot_auto_recovery_product_data_green"] is False @@ -226,7 +226,7 @@ def _assert_delivery_workbench_shape(data: dict): ) assert ( data["summary"]["reboot_auto_recovery_stockplatform_ingestion_status"] - == "blocked" + == "ok" ) assert ( data["summary"][ @@ -241,7 +241,7 @@ def _assert_delivery_workbench_shape(data: dict): is False ) assert data["summary"]["reboot_auto_recovery_safe_next_step"] == ( - "wait_next_stockplatform_retry_window_then_rerun_slo_verify_only_" + "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_" "no_reboot_no_db_write" ) assert data["summary"]["gitea_private_inventory_status"] == ( diff --git a/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py b/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py index dff8c231c..8bf687270 100644 --- a/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py +++ b/apps/api/tests/test_reboot_auto_recovery_slo_scorecard_api.py @@ -31,18 +31,18 @@ def _assert_reboot_slo_payload(payload: dict): assert payload["priority"] == "P0-006" assert payload["status"] == "blocked_reboot_auto_recovery_slo_not_ready" assert payload["safe_next_step"] == ( - "wait_next_stockplatform_retry_window_then_rerun_slo_verify_only_" + "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_" "no_reboot_no_db_write" ) assert payload["can_claim_all_services_recovered_within_target"] is False assert payload["readback"]["workplan_id"] == "P0-006" assert payload["readback"]["target_minutes"] == 10 assert payload["readback"]["safe_next_step"] == ( - "wait_next_stockplatform_retry_window_then_rerun_slo_verify_only_" + "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_" "no_reboot_no_db_write" ) - assert payload["rollups"]["active_blocker_count"] == 6 - assert payload["rollups"]["readiness_percent"] == 45 + assert payload["rollups"]["active_blocker_count"] == 5 + assert payload["rollups"]["readiness_percent"] == 55 assert payload["rollups"]["observed_host_count"] == 4 assert payload["rollups"]["missing_host_count"] == 0 assert payload["rollups"]["unreachable_host_count"] == 0 @@ -51,9 +51,9 @@ def _assert_reboot_slo_payload(payload: dict): assert payload["rollups"]["product_data_green"] is False assert payload["rollups"]["backup_core_green"] is True assert payload["rollups"]["stockplatform_freshness_status"] == "blocked" - assert payload["rollups"]["stockplatform_ingestion_status"] == "blocked" - assert payload["rollups"]["stockplatform_freshness_blocker_count"] == 2 - assert payload["rollups"]["stockplatform_ingestion_blocker_count"] == 1 + assert payload["rollups"]["stockplatform_ingestion_status"] == "ok" + assert payload["rollups"]["stockplatform_freshness_blocker_count"] == 1 + assert payload["rollups"]["stockplatform_ingestion_blocker_count"] == 0 assert payload["rollups"]["stockplatform_final_retry_window_passed"] is False assert ( payload["rollups"]["stockplatform_controlled_recovery_gate_required"] @@ -63,12 +63,12 @@ def _assert_reboot_slo_payload(payload: dict): stockplatform = payload["stockplatform_data_freshness"] assert stockplatform["freshness_endpoint_readback_present"] is True assert stockplatform["ingestion_endpoint_readback_present"] is True - assert stockplatform["freshness_blockers"] == [ - "core_margin_short_daily_missing", - "ai_recommendations_stale", - ] - assert stockplatform["ingestion_blockers"] == [ - "core.margin_short_daily_incomplete" + assert stockplatform["freshness_blockers"] == ["ai_recommendations_stale"] + assert stockplatform["ingestion_blockers"] == [] + assert stockplatform["margin_short_recovery"]["status"] == "recovered" + assert stockplatform["margin_short_recovery"]["successful_source_run_ids"] == [ + 3390, + 3389, ] assert stockplatform["eod_window"]["final_retry_window_passed"] is False assert stockplatform["controlled_recovery_gate"]["required"] is False diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 62e12cb1d..42cf50a2f 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -31,6 +31,22 @@ **邊界**:未觸發 workflow_dispatch;未操作 host / Docker / K8s / DB;未使用 GitHub / `gh` / GitHub API;未讀 secret / token / raw sessions / SQLite。 +## 2026-06-29 — 21:07 P0-006 Stock retry margin-short recovered + +**照優先順序讀回的真相**: +- 21:05 後重讀 StockPlatform:`/api/v1/system/ingestion` 已從 `blocked` 轉為 `ok`。 +- `official_margin_short_daily` source runs `3389` / `3390` 對 `2026-06-29` 讀回 `succeeded`,`core_margin_short_daily_missing` 與 `core.margin_short_daily_incomplete` 已不再是目前 blocker。 +- `/api/v1/system/freshness` 仍是 `blocked`,目前只剩 `ai_recommendations_stale`;P0-006 仍不得宣稱 SLO 完成。 +- 更新 P0-006 committed scorecard / priority readback:active blockers `6 → 5`、readiness `45 → 55`、StockPlatform ingestion `blocked → ok`,下一步改為等待 AI recommendations refresh 後 rerun verify-only。 +- Gitea CD #3962 對 `3683282b5 feat(api): dry run log feedback receipts` 讀回 `Failure`;本輪同步修正 `agents.py` import 排序,並將 `ai_agent_log_feedback_receipt_dry_run.py` / 對應 API test 納入 controlled-runtime profile 白名單與 profile contract,避免 log feedback readback 再掉到錯 profile。 + +**驗證**: +- Focused pytest:P0-006 scorecard / Delivery Workbench / log feedback / log intelligence / CD profile / scorecard script `33 passed`。 +- Controlled-runtime 相關集合:`122 passed`。 +- `py_compile`、`ruff check`、Gitea runner pressure guard、Gitea secret env guard、`git diff --check`:通過。 + +**仍維持**:未重啟主機、未 restart service、未寫 DB、未寫 fake freshness marker、未讀 secret / token / raw sessions / SQLite、未使用 GitHub / `gh` / GitHub API。 + ## 2026-06-29 — 20:30 P0-006 CD failure closeout **照優先順序處理**: diff --git a/docs/operations/awoooi-priority-work-order-readback.snapshot.json b/docs/operations/awoooi-priority-work-order-readback.snapshot.json index 419e82439..b3bfad6eb 100644 --- a/docs/operations/awoooi-priority-work-order-readback.snapshot.json +++ b/docs/operations/awoooi-priority-work-order-readback.snapshot.json @@ -128,7 +128,7 @@ "no_matching_runner_visible": false, "source_readiness_ci_fix_required": false }, - "generated_at": "2026-06-29T20:27:30+08:00", + "generated_at": "2026-06-29T21:07:17+08:00", "in_progress_or_blocked_in_priority_order": [ { "evidence": { @@ -137,8 +137,7 @@ "post_start_blocked_not_zero", "product_data_green_not_1", "service_green_not_1", - "stockplatform_freshness_blocked", - "stockplatform_ingestion_blocked" + "stockplatform_freshness_blocked" ], "all_host_reboot_detection_missing": false, "backup_core_green": true, @@ -162,7 +161,7 @@ ], "post_start_blocked": 1, "product_data_green": false, - "safe_next_step_from_scorecard": "inspect_stockplatform_ingestion_readback_and_wait_retry_windows_then_rerun_slo_verify_only_no_reboot", + "safe_next_step_from_scorecard": "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_no_reboot_no_db_write", "service_green": false, "slo_exporter_source_present": true, "slo_installer_source_present": true, @@ -175,13 +174,6 @@ "188" ], "stock_blocked_sources": [ - { - "latest_date": "2026-06-26", - "notes": "官方來源狀態:上櫃官方尚未發布,回傳日期 2026-06-29,來源列數 0;上市官方尚未發布,回傳日期 尚無,來源列數 0", - "row_count": 0, - "source": "core.margin_short_daily", - "status": "missing" - }, { "latest_date": "2026-06-26", "notes": "最新日期 2026-06-26 早於最新交易日 2026-06-29", @@ -191,20 +183,49 @@ } ], "stock_blockers": [ - "core_margin_short_daily_missing", "ai_recommendations_stale" ], "stock_eod_classification": "after_first_eod_window_blocked", "stock_eod_final_retry_window_end_local": "23:35", "stock_eod_final_retry_window_passed": false, "stock_eod_first_full_window_end_local": "19:15", - "stock_eod_next_action": "inspect_ingestion_logs_and_wait_retry_windows", + "stock_eod_next_action": "wait_ai_recommendations_refresh", "stock_eod_window_pending": false, "stock_freshness_status": "blocked", - "stock_ingestion_blockers": [ - "core.margin_short_daily_incomplete" - ], + "stock_ingestion_blockers": [], "stock_latest_source_runs": [ + { + "source_run_id": 3390, + "source_name": "official_margin_short_daily", + "target_date": "2026-06-29", + "status": "succeeded", + "started_at": "2026-06-29T13:05:13.341357Z", + "finished_at": "2026-06-29T13:05:13.341357Z" + }, + { + "source_run_id": 3389, + "source_name": "official_margin_short_daily", + "target_date": "2026-06-29", + "status": "succeeded", + "started_at": "2026-06-29T13:05:13.341357Z", + "finished_at": "2026-06-29T13:05:13.341357Z" + }, + { + "source_run_id": 3388, + "source_name": "intelligence_security_linker", + "target_date": null, + "status": "succeeded", + "started_at": "2026-06-29T13:00:08.792550Z", + "finished_at": "2026-06-29T13:00:08.792550Z" + }, + { + "source_run_id": 3387, + "source_name": "intelligence_reports_import", + "target_date": null, + "status": "succeeded", + "started_at": "2026-06-29T13:00:07.822700Z", + "finished_at": "2026-06-29T13:00:07.822700Z" + }, { "source_run_id": 3385, "source_name": "official_margin_short_daily", @@ -222,7 +243,7 @@ "finished_at": "2026-06-29T12:05:16.716460Z" }, { - "source_run_id": 3384, + "source_run_id": 3383, "source_name": "official_margin_short_daily", "target_date": "2026-06-29", "status": "official_pending", @@ -230,7 +251,7 @@ "finished_at": "2026-06-29T11:05:17.201062Z" }, { - "source_run_id": 3383, + "source_run_id": 3384, "source_name": "official_margin_short_daily", "target_date": "2026-06-29", "status": "official_pending", @@ -252,42 +273,26 @@ "status": "succeeded", "started_at": "2026-06-29T11:00:10.289509Z", "finished_at": "2026-06-29T11:00:10.289509Z" - }, - { - "source_run_id": 3380, - "source_name": "year_to_date_stock_backfill", - "target_date": null, - "status": "planned", - "started_at": "2026-06-29T10:22:15.262488Z", - "finished_at": "2026-06-29T10:22:15.262488Z" - }, - { - "source_run_id": 3378, - "source_name": "official_margin_short_daily", - "target_date": "2026-06-29", - "status": "official_pending", - "started_at": "2026-06-29T10:22:13.464934Z", - "finished_at": "2026-06-29T10:22:13.464934Z" - }, - { - "source_run_id": 3379, - "source_name": "official_margin_short_daily", - "target_date": "2026-06-29", - "status": "official_pending", - "started_at": "2026-06-29T10:22:13.464934Z", - "finished_at": "2026-06-29T10:22:13.464934Z" - }, - { - "source_run_id": 3345, - "source_name": "community_etfinfo_holdings_daily", - "target_date": "2026-06-26", - "status": "succeeded", - "started_at": "2026-06-29T10:21:56.651793Z", - "finished_at": "2026-06-29T10:21:56.651793Z" } ], "stock_latest_trading_date": "2026-06-29", "stock_official_margin_short_source_runs": [ + { + "source_run_id": 3390, + "source_name": "official_margin_short_daily", + "target_date": "2026-06-29", + "status": "succeeded", + "started_at": "2026-06-29T13:05:13.341357Z", + "finished_at": "2026-06-29T13:05:13.341357Z" + }, + { + "source_run_id": 3389, + "source_name": "official_margin_short_daily", + "target_date": "2026-06-29", + "status": "succeeded", + "started_at": "2026-06-29T13:05:13.341357Z", + "finished_at": "2026-06-29T13:05:13.341357Z" + }, { "source_run_id": 3385, "source_name": "official_margin_short_daily", @@ -305,7 +310,7 @@ "finished_at": "2026-06-29T12:05:16.716460Z" }, { - "source_run_id": 3384, + "source_run_id": 3383, "source_name": "official_margin_short_daily", "target_date": "2026-06-29", "status": "official_pending", @@ -313,7 +318,7 @@ "finished_at": "2026-06-29T11:05:17.201062Z" }, { - "source_run_id": 3383, + "source_run_id": 3384, "source_name": "official_margin_short_daily", "target_date": "2026-06-29", "status": "official_pending", @@ -327,7 +332,7 @@ "stockplatform_freshness_endpoint_readback_present": true, "stockplatform_healthz_green": true, "stockplatform_ingestion_endpoint_readback_present": true, - "stockplatform_ingestion_status": "blocked", + "stockplatform_ingestion_status": "ok", "stockplatform_scorecard_readback_integrated": true, "stockplatform_transient_502_before_success_seen": true, "target_minutes": 10, @@ -339,8 +344,13 @@ "max_host_uptime_seconds": 537911, "last_run_timestamp": 1782735360 }, - "stock_ingestion_status": "blocked", - "stockplatform_second_retry_checked_at": "2026-06-29T20:21:35+08:00" + "stock_ingestion_status": "ok", + "stockplatform_second_retry_checked_at": "2026-06-29T20:21:35+08:00", + "stockplatform_third_retry_checked_at": "2026-06-29T21:07:17+08:00", + "stockplatform_margin_short_recovered": true, + "stockplatform_remaining_freshness_blockers": [ + "ai_recommendations_stale" + ] }, "professional_fix": { "action": "Keep the live boot-triggered SLO timer enabled. Do not reboot or restart services from this lane. Treat StockPlatform freshness/ingestion as the current service recovery blocker: use the committed P0-006 scorecard and /api/v1/agents/reboot-auto-recovery-slo-scorecard readback, wait scheduled retry windows, then rerun verify-only. If still blocked after 23:35, open the controlled data recovery gate with source diff, check-mode/dry-run, rollback, and post-freshness verifier; never write manual DB rows or fake freshness.", @@ -361,9 +371,9 @@ ], "owner": "reboot auto-recovery lane plus StockPlatform freshness readback" }, - "reason": "P0-006 scorecard and live retry readback now directly show the 20:05 StockPlatform retry still blocked: public/API health is green, but freshness remains blocked by core_margin_short_daily_missing and ai_recommendations_stale, and ingestion remains blocked by core.margin_short_daily_incomplete with official_margin_short_daily source runs 3385/3386 still official_pending. No reboot, restart, DB write, or fake freshness marker is allowed from this lane; wait the next retry windows and rerun verify-only.", - "safe_next_step": "wait_next_stockplatform_retry_window_then_rerun_slo_verify_only_no_reboot_no_db_write", - "status": "blocked_stockplatform_product_data_freshness_waiting_final_retry_window", + "reason": "P0-006 live retry readback at 2026-06-29T21:05+08:00 shows StockPlatform margin-short official source runs 3389/3390 succeeded and ingestion is ok. Freshness remains blocked only by ai_recommendations_stale, so no reboot, restart, DB write, or fake freshness marker is allowed; wait the next AI recommendations refresh or final retry window and rerun verify-only.", + "safe_next_step": "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_no_reboot_no_db_write", + "status": "blocked_stockplatform_ai_recommendations_waiting_refresh_window", "title": "主機重啟自動偵測、自動觸發與 10 分鐘恢復 SLO", "workplan_id": "P0-006" }, @@ -458,9 +468,8 @@ } ], "next_execution_order": [ - "P0-006: StockPlatform freshness/ingestion readback remains blocked after the 20:05 retry; wait the next retry windows, rerun verify-only with no reboot/restart/DB write, and open controlled data recovery gate only if still blocked after the final retry window.", - "P0-005: while P0-006 is waiting on external StockPlatform official data retry windows, validate five non-secret credential escrow evidence refs through the no-persist refs endpoint and rerun one preflight; production API safe_next_step reads back correctly and backup/offsite core remains green.", - "P0-003: supply authenticated/admin redacted inventory payload or owner coverage attestation, then run the Gitea payload validator; public-only live inventory remains four repos and GitHub remains stopped/retired/do_not_use.", + "P0-006: StockPlatform margin-short ingestion recovered at the 21:05 retry; freshness remains blocked only by ai_recommendations_stale, so wait the next AI recommendations refresh/final retry window and rerun verify-only with no reboot/restart/DB write.", + "P0-005: while P0-006 waits external StockPlatform AI recommendations retry, validate five non-secret credential escrow evidence refs through the no-persist refs endpoint and rerun one preflight; do not collect secret values or write credential markers.", "P1-OPENCLAW-LIVE-OPS-SPACE: build the Gather-like OpenClaw continuous animated live ops workspace only after the current P0 runtime truth blockers are no longer the active next action." ], "noise_integrated_risk_register": [ @@ -618,7 +627,7 @@ "stockplatform_ingestion_readback": "https://stock.wooo.work/api/v1/system/ingestion", "workstation_dashboard": "~/.codex/codex-workstation-sync-dashboard.snapshot.json" }, - "status": "p0_ordered_readback_p0_006_stock_freshness_second_retry_still_blocked_p0_005_refs_waiting_p0_003_payload_validator_ready", + "status": "p0_ordered_readback_p0_006_margin_short_recovered_ai_recommendations_waiting_p0_005_refs_ready_p0_003_closed", "stopped_or_do_not_use": [ { "allowed_actions": 0, diff --git a/docs/operations/awoooi-reboot-auto-recovery-slo-scorecard.snapshot.json b/docs/operations/awoooi-reboot-auto-recovery-slo-scorecard.snapshot.json index 6d0d1cad8..3ae6447d8 100644 --- a/docs/operations/awoooi-reboot-auto-recovery-slo-scorecard.snapshot.json +++ b/docs/operations/awoooi-reboot-auto-recovery-slo-scorecard.snapshot.json @@ -4,8 +4,7 @@ "post_start_blocked_not_zero", "product_data_green_not_1", "service_green_not_1", - "stockplatform_freshness_blocked", - "stockplatform_ingestion_blocked" + "stockplatform_freshness_blocked" ], "can_claim_all_services_recovered_within_target": false, "capacity": { @@ -13,7 +12,7 @@ "free_gib": 4.454, "min_free_gib": 2.0 }, - "generated_at": "2026-06-29T20:21:35+08:00", + "generated_at": "2026-06-29T21:07:17+08:00", "host_boot_detection": { "host_rows": [ { @@ -96,7 +95,7 @@ "summary_present": true, "wazuh_dashboard_degraded": false }, - "safe_next_step": "wait_next_stockplatform_retry_window_then_rerun_slo_verify_only_no_reboot_no_db_write", + "safe_next_step": "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_no_reboot_no_db_write", "schema_version": "awoooi_reboot_auto_recovery_slo_scorecard_v1", "source_controls": { "cold_start_textfile_exporter_source_present": true, @@ -112,13 +111,6 @@ "status": "blocked_reboot_auto_recovery_slo_not_ready", "stockplatform_data_freshness": { "blocked_sources": [ - { - "latest_date": "2026-06-26", - "notes": "官方來源狀態:上櫃官方尚未發布,回傳日期 2026-06-29,來源列數 0;上市官方尚未發布,回傳日期 尚無,來源列數 0", - "row_count": 0, - "source": "core.margin_short_daily", - "status": "missing" - }, { "latest_date": "2026-06-26", "notes": "最新日期 2026-06-26 早於最新交易日 2026-06-29", @@ -142,7 +134,7 @@ "reboot_or_service_restart_from_reboot_slo_lane" ], "required": false, - "status": "not_required_yet", + "status": "not_required_yet_ai_recommendations_retry_pending", "target_selector": "stockplatform-v2:system_freshness:core.margin_short_daily,ai.recommendations" }, "eod_window": { @@ -150,21 +142,50 @@ "final_retry_window_end_local": "23:35", "final_retry_window_passed": false, "first_full_window_end_local": "19:15", - "next_action": "inspect_ingestion_logs_and_wait_retry_windows", + "next_action": "wait_ai_recommendations_refresh", "pending": false }, "freshness_blockers": [ - "core_margin_short_daily_missing", "ai_recommendations_stale" ], "freshness_endpoint_readback_present": true, "freshness_status": "blocked", - "ingestion_blockers": [ - "core.margin_short_daily_incomplete" - ], + "ingestion_blockers": [], "ingestion_endpoint_readback_present": true, - "ingestion_status": "blocked", + "ingestion_status": "ok", "latest_source_runs": [ + { + "source_run_id": 3390, + "source_name": "official_margin_short_daily", + "target_date": "2026-06-29", + "status": "succeeded", + "started_at": "2026-06-29T13:05:13.341357Z", + "finished_at": "2026-06-29T13:05:13.341357Z" + }, + { + "source_run_id": 3389, + "source_name": "official_margin_short_daily", + "target_date": "2026-06-29", + "status": "succeeded", + "started_at": "2026-06-29T13:05:13.341357Z", + "finished_at": "2026-06-29T13:05:13.341357Z" + }, + { + "source_run_id": 3388, + "source_name": "intelligence_security_linker", + "target_date": null, + "status": "succeeded", + "started_at": "2026-06-29T13:00:08.792550Z", + "finished_at": "2026-06-29T13:00:08.792550Z" + }, + { + "source_run_id": 3387, + "source_name": "intelligence_reports_import", + "target_date": null, + "status": "succeeded", + "started_at": "2026-06-29T13:00:07.822700Z", + "finished_at": "2026-06-29T13:00:07.822700Z" + }, { "source_run_id": 3385, "source_name": "official_margin_short_daily", @@ -182,7 +203,7 @@ "finished_at": "2026-06-29T12:05:16.716460Z" }, { - "source_run_id": 3384, + "source_run_id": 3383, "source_name": "official_margin_short_daily", "target_date": "2026-06-29", "status": "official_pending", @@ -190,7 +211,7 @@ "finished_at": "2026-06-29T11:05:17.201062Z" }, { - "source_run_id": 3383, + "source_run_id": 3384, "source_name": "official_margin_short_daily", "target_date": "2026-06-29", "status": "official_pending", @@ -212,42 +233,20 @@ "status": "succeeded", "started_at": "2026-06-29T11:00:10.289509Z", "finished_at": "2026-06-29T11:00:10.289509Z" - }, - { - "source_run_id": 3380, - "source_name": "year_to_date_stock_backfill", - "target_date": null, - "status": "planned", - "started_at": "2026-06-29T10:22:15.262488Z", - "finished_at": "2026-06-29T10:22:15.262488Z" - }, - { - "source_run_id": 3378, - "source_name": "official_margin_short_daily", - "target_date": "2026-06-29", - "status": "official_pending", - "started_at": "2026-06-29T10:22:13.464934Z", - "finished_at": "2026-06-29T10:22:13.464934Z" - }, - { - "source_run_id": 3379, - "source_name": "official_margin_short_daily", - "target_date": "2026-06-29", - "status": "official_pending", - "started_at": "2026-06-29T10:22:13.464934Z", - "finished_at": "2026-06-29T10:22:13.464934Z" - }, - { - "source_run_id": 3345, - "source_name": "community_etfinfo_holdings_daily", - "target_date": "2026-06-26", - "status": "succeeded", - "started_at": "2026-06-29T10:21:56.651793Z", - "finished_at": "2026-06-29T10:21:56.651793Z" } ], "latest_trading_date": "2026-06-29", - "second_retry_checked_at": "2026-06-29T20:21:35+08:00" + "second_retry_checked_at": "2026-06-29T20:21:35+08:00", + "third_retry_checked_at": "2026-06-29T21:07:17+08:00", + "margin_short_recovery": { + "status": "recovered", + "cleared_blocker": "core_margin_short_daily_missing", + "cleared_ingestion_blocker": "core.margin_short_daily_incomplete", + "successful_source_run_ids": [ + 3390, + 3389 + ] + } }, "target_minutes": 10, "target_seconds": 600, diff --git a/ops/runner/test_cd_controlled_runtime_profile.py b/ops/runner/test_cd_controlled_runtime_profile.py index 28f32ac83..a8c1689d1 100644 --- a/ops/runner/test_cd_controlled_runtime_profile.py +++ b/ops/runner/test_cd_controlled_runtime_profile.py @@ -99,6 +99,18 @@ def test_ai_log_intelligence_readback_stays_on_controlled_runtime_profile() -> N assert source in text +def test_ai_log_feedback_receipt_stays_on_controlled_runtime_profile() -> None: + text = _workflow_text() + expected_sources = [ + "apps/api/src/services/ai_agent_log_feedback_receipt_dry_run.py)", + "apps/api/tests/test_ai_agent_log_feedback_receipt_dry_run_api.py)", + "src/services/ai_agent_log_feedback_receipt_dry_run.py", + "tests/test_ai_agent_log_feedback_receipt_dry_run_api.py", + ] + for source in expected_sources: + assert source in text + + def test_awooop_ansible_check_mode_stays_on_controlled_runtime_profile() -> None: text = _workflow_text() expected_sources = [