From cb7151cc27ad7243995d4e12b3d5d14ef2958193 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 12 May 2026 14:01:03 +0800 Subject: [PATCH] fix(awooop): set shadow run defaults for mirrors --- apps/api/src/services/channel_hub.py | 8 +++- .../test_channel_hub_grouped_alert_events.py | 38 +++++++++++++++++++ docs/LOGBOOK.md | 26 +++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/apps/api/src/services/channel_hub.py b/apps/api/src/services/channel_hub.py index e1048233..e98f0eed 100644 --- a/apps/api/src/services/channel_hub.py +++ b/apps/api/src/services/channel_hub.py @@ -82,11 +82,15 @@ async def ensure_completed_shadow_run( INSERT INTO awooop_run_state ( run_id, project_id, agent_id, state, trigger_type, trigger_ref, is_shadow, - input_sha256, created_at, completed_at, timeout_at + input_sha256, + attempt_count, max_attempts, cost_usd, step_count, + created_at, completed_at, timeout_at ) VALUES ( :run_id, :project_id, :agent_id, 'completed', :trigger_type, :trigger_ref, TRUE, - :input_sha256, NOW(), NOW(), NOW() + :input_sha256, + 0, 3, 0.0000, 0, + NOW(), NOW(), NOW() ) ON CONFLICT (run_id) DO NOTHING RETURNING run_id diff --git a/apps/api/tests/test_channel_hub_grouped_alert_events.py b/apps/api/tests/test_channel_hub_grouped_alert_events.py index 685223de..eb22386c 100644 --- a/apps/api/tests/test_channel_hub_grouped_alert_events.py +++ b/apps/api/tests/test_channel_hub_grouped_alert_events.py @@ -3,6 +3,7 @@ from __future__ import annotations from src.services.channel_hub import ( build_grouped_alert_provider_event_id, build_grouped_alert_run_id, + ensure_completed_shadow_run, format_grouped_alert_digest_text, format_grouped_alert_event_content, ) @@ -67,3 +68,40 @@ def test_format_grouped_alert_digest_text_is_html_safe() -> None: assert "sentry&snuba" in content assert "7 筆同組告警" in content assert "AwoooP Run 監控" in content + + +class _FakeResult: + def fetchone(self) -> tuple[str] | None: + return ("created",) + + +class _FakeSession: + def __init__(self) -> None: + self.statement = "" + self.params = {} + + async def execute(self, statement, params): # noqa: ANN001 + self.statement = str(statement) + self.params = params + return _FakeResult() + + +async def test_completed_shadow_run_sets_run_state_not_null_defaults() -> None: + session = _FakeSession() + run_id = build_grouped_alert_run_id("awoooi", "provider-event") + + inserted = await ensure_completed_shadow_run( + session, # type: ignore[arg-type] + project_id="awoooi", + run_id=run_id, + agent_id="legacy-telegram-gateway", + trigger_type="legacy_outbound", + trigger_ref="12713", + input_payload={"message_type": "final"}, + ) + + assert inserted is True + assert "attempt_count, max_attempts, cost_usd, step_count" in session.statement + assert "0, 3, 0.0000, 0" in session.statement + assert session.params["project_id"] == "awoooi" + assert session.params["run_id"] == run_id diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 374eb2d8..1d4ef69d 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -5495,3 +5495,29 @@ DATABASE_URL='postgresql+asyncpg://test:test@127.0.0.1:5432/test' \ 發送到 SRE 群組並鏡像成 AwoooP outbound/run。 - 若 AWOOI API 不可達,workflow 會 fallback 直接 Telegram;這類 fallback 訊息仍不會進 AwoooP,後續需用外部事件補償或 runner-side outbox 收斂。 + +**追加修正**: + +- 首輪推版後,Gitea job log 證實 Code Review / CD start 已成功打進 AWOOI + `/api/v1/webhooks/alertmanager`,但 `TelegramGateway._mirror_outbound_message()` 寫 + `awooop_run_state` 時遇到: + +```text +null value in column "attempt_count" of relation "awooop_run_state" violates not-null constraint +``` + +- 根因是 `ensure_completed_shadow_run()` 依賴 DB default,但 production table 實際 + NOT NULL 欄位未吃到 default;改為 insert 時明確帶入: + - `attempt_count = 0` + - `max_attempts = 3` + - `cost_usd = 0.0000` + - `step_count = 0` +- 新增 Channel Hub 回歸測試,避免 legacy mirror run 再漏 FSM 必填欄位。 + +```text +DATABASE_URL='postgresql+asyncpg://test:test@127.0.0.1:5432/test' \ + /Users/ogt/awoooi/apps/api/.venv/bin/python -m pytest \ + apps/api/tests/test_channel_hub_grouped_alert_events.py \ + apps/api/tests/test_cicd_alertmanager_mapping.py -q +# 9 passed +```