From 9aca079078374088f1a066d47809638df2a09747 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 2 Jul 2026 22:46:20 +0800 Subject: [PATCH] fix(telegram): return matrix source gaps as readback --- .../telegram_alert_ai_automation_matrix.py | 26 ++++++++-- ...telegram_alert_ai_automation_matrix_api.py | 51 +++++++++++++++++++ docs/LOGBOOK.md | 22 ++++++++ 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/apps/api/src/services/telegram_alert_ai_automation_matrix.py b/apps/api/src/services/telegram_alert_ai_automation_matrix.py index 65331d0b2..5f46c868a 100644 --- a/apps/api/src/services/telegram_alert_ai_automation_matrix.py +++ b/apps/api/src/services/telegram_alert_ai_automation_matrix.py @@ -81,6 +81,9 @@ def load_latest_telegram_alert_ai_automation_matrix( readability_summary = readability_guard["summary"] digest_rollups = digest_policy["rollups"] source_proof = _inspect_source_contract(repo_root) + source_contract_missing_markers = list( + source_proof.get("source_contract_missing_markers") or [] + ) learning_writeback_refs_present = bool( source_proof["ai_alert_card_learning_writeback_refs_present"] ) @@ -431,6 +434,8 @@ def load_latest_telegram_alert_ai_automation_matrix( "status": ( "telegram_alert_ai_automation_matrix_ready_with_direct_send_gaps" if direct_gap_count + else "blocked_telegram_alert_ai_automation_matrix_source_contract_gaps" + if source_contract_missing_markers else "telegram_alert_ai_automation_matrix_ready" ), "mode": "metadata_only_no_secret_no_telegram_send_no_runtime_write", @@ -604,7 +609,7 @@ def _require_safe_boundaries(*payloads: dict[str, Any]) -> None: ) -def _inspect_source_contract(repo_root: Path) -> dict[str, int | bool]: +def _inspect_source_contract(repo_root: Path) -> dict[str, Any]: source_checks = { "telegram_gateway_mirror_present": ( "apps/api/src/services/telegram_gateway.py", @@ -681,9 +686,13 @@ def _inspect_source_contract(repo_root: Path) -> dict[str, int | bool]: 1 if present else 0 ) - required = [key for key, value in result.items() if key.endswith("_present") and value is not True] - if required: - raise ValueError(f"Telegram matrix source contract missing markers: {required}") + required = [ + key + for key, value in result.items() + if key.endswith("_present") and value is not True + ] + result["source_contract_ready"] = not required + result["source_contract_missing_markers"] = required return result @@ -823,7 +832,7 @@ def _build_summary( migration_summary: dict[str, Any], readability_summary: dict[str, Any], digest_rollups: dict[str, Any], - source_proof: dict[str, int | bool], + source_proof: dict[str, Any], next_priority_action: dict[str, Any], ) -> dict[str, Any]: ready = lambda key, prefix: sum(1 for item in matrix if str(item[key]).startswith(prefix)) @@ -872,6 +881,13 @@ def _build_summary( "telegram_alert_post_apply_verifier_awooop_surface_count": source_proof[ "telegram_alert_post_apply_verifier_awooop_surface_count" ], + "source_contract_ready": bool(source_proof.get("source_contract_ready")), + "source_contract_missing_marker_count": len( + source_proof.get("source_contract_missing_markers") or [] + ), + "source_contract_missing_markers": list( + source_proof.get("source_contract_missing_markers") or [] + ), "next_priority_action_id": next_priority_action["action_id"], "next_priority_work_item_id": "CIR-P0-TG-001", } diff --git a/apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py b/apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py index ebe1b563b..8dba34709 100644 --- a/apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py +++ b/apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py @@ -57,6 +57,9 @@ def test_telegram_alert_ai_automation_matrix_loader_returns_gap_matrix(): assert summary["telegram_alert_ai_loop_consumer_context_readback_count"] == 1 assert summary["telegram_alert_post_apply_verifier_readback_count"] == 1 assert summary["telegram_alert_post_apply_verifier_awooop_surface_count"] == 1 + assert summary["source_contract_ready"] is True + assert summary["source_contract_missing_marker_count"] == 0 + assert summary["source_contract_missing_markers"] == [] assert summary["next_priority_action_id"] == ( "audit_all_telegram_monitoring_alerts_db_log_ai_automation_coverage" ) @@ -185,6 +188,54 @@ def test_telegram_matrix_source_contract_supports_container_layout(tmp_path): assert proof["telegram_alert_post_apply_verifier_readback_count"] == 1 assert proof["telegram_alert_post_apply_verifier_awooop_surface_present"] is True assert proof["telegram_alert_post_apply_verifier_awooop_surface_count"] == 1 + assert proof["source_contract_ready"] is True + assert proof["source_contract_missing_markers"] == [] + + +def test_telegram_matrix_source_contract_reports_missing_web_surface_without_raising( + tmp_path, +): + (tmp_path / "src/services").mkdir(parents=True) + (tmp_path / "src/db").mkdir(parents=True) + (tmp_path / "src/api/v1/platform").mkdir(parents=True) + (tmp_path / "src/services/telegram_gateway.py").write_text( + "_mirror_outbound_message\n" + "record_outbound_message\n" + "ai_automation_alert_card_mirror_v1\n", + encoding="utf-8", + ) + (tmp_path / "src/db/awooop_models.py").write_text( + '__tablename__ = "awooop_outbound_message"\n', + encoding="utf-8", + ) + (tmp_path / "src/api/v1/platform/operator_runs.py").write_text( + '"/runs/ai-alert-cards"\n' + "learning_writeback_refs\n" + "learning_writeback_ready_total\n", + encoding="utf-8", + ) + (tmp_path / "src/services/platform_operator_service.py").write_text( + "ai_alert_card_learning_registry_readback_v1\n" + "ai_agent_context_ref\n", + encoding="utf-8", + ) + (tmp_path / "src/services/ai_agent_log_controlled_writeback_consumer_readback.py").write_text( + "telegram_alert_ai_loop_consumer_context_readback_v1\n", + encoding="utf-8", + ) + (tmp_path / "src/services/telegram_alert_learning_context_post_apply_verifier.py").write_text( + "telegram_alert_learning_context_post_apply_verifier_v1\n", + encoding="utf-8", + ) + + proof = _inspect_source_contract(tmp_path) + + assert proof["source_contract_ready"] is False + assert proof["telegram_alert_post_apply_verifier_awooop_surface_present"] is False + assert proof["telegram_alert_post_apply_verifier_awooop_surface_count"] == 0 + assert proof["source_contract_missing_markers"] == [ + "telegram_alert_post_apply_verifier_awooop_surface_present" + ] def test_telegram_alert_ai_automation_matrix_endpoint_returns_readback(): diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index f35e30adb..38e6588e6 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -53402,3 +53402,25 @@ production browser smoke: **下一步**: - commit / push 到 Gitea main,讀回新 CD;deploy marker 出現後驗證 `/api/v1/agents/telegram-alert-monitoring-coverage-readback` 與 AwoooP Runs / Work Items / Alerts shared panel。 + +## 2026-07-02 — Telegram matrix production 500 source-contract 修正 + +**完成內容**: +- `#4463` CD 已成功產生 deploy marker `4fd0a4985 chore(cd): deploy 6d1edf6 [skip ci]`,tests 讀回 `AWOOOI_CD_TEST_PROFILE=controlled-runtime` 且 B5 明確跳過;API / Web / Worker rollout、API health 與 production deploy readback 均成功。 +- deploy 後正式讀回 `/api/v1/agents/telegram-alert-ai-automation-matrix` 與 `/api/v1/agents/telegram-alert-monitoring-coverage-readback` 回 500;根因是 matrix source contract 在 production API container 看不到前端 `apps/web/...` source 時直接 `raise ValueError`。 +- 已修正 matrix source contract:缺 marker 改為 `source_contract_missing_markers` 與 blocked status / summary,不再讓 readback API 500;coverage service 可繼續消費 matrix blocked payload,將缺口轉成 AI controlled action evidence。 +- 新增回歸測試,模擬 API container 只有 `src/...` layout 且缺 `apps/web/...` surface 時必須回缺口 evidence、不得 throw。 + +**本地驗證結果**: +- `DATABASE_URL=postgresql+asyncpg://test:test@localhost/test PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py apps/api/tests/test_telegram_alert_monitoring_coverage_readback_api.py -q -p no:cacheprovider`:`7 passed`。 +- `python3.11 -m pytest ops/runner/test_cd_controlled_runtime_profile.py -q -p no:cacheprovider`:`48 passed`。 +- `python3.11 -m py_compile apps/api/src/services/telegram_alert_ai_automation_matrix.py apps/api/src/services/telegram_alert_monitoring_coverage_readback.py apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py apps/api/tests/test_telegram_alert_monitoring_coverage_readback_api.py`:通過。 +- `git diff --check`:通過。 + +**仍維持**: +- 沒有讀 secret / runner token / `.runner` 內容 / `.env` / raw sessions / SQLite / auth。 +- 沒有使用 GitHub / gh / GitHub API / GitHub Actions。 +- 沒有重啟主機,沒有 Docker / Nginx / K3s / DB / firewall restart,沒有 workflow_dispatch,沒有 DROP / TRUNCATE / restore / prune。 + +**下一步**: +- commit / push 到 Gitea main;等 deploy marker 後重新讀 production matrix / coverage endpoint,再跑 AwoooP Runs / Work Items / Alerts smoke。