fix(awooop): strengthen outbound truth references
All checks were successful
Code Review / ai-code-review (push) Successful in 10s
CD Pipeline / tests (push) Successful in 1m12s
CD Pipeline / build-and-deploy (push) Successful in 3m33s
CD Pipeline / post-deploy-checks (push) Successful in 1m15s

This commit is contained in:
Your Name
2026-05-13 12:04:26 +08:00
parent 7d506b785d
commit 7fa9f743dd
6 changed files with 102 additions and 2 deletions

View File

@@ -785,6 +785,8 @@ async def fetch_truth_chain(source_id: str, project_id: str = "awoooi") -> dict[
AND (
run_id::text = :source_id
OR content_preview ILIKE :needle
OR coalesce(source_envelope #> '{source_refs,incident_ids}', '[]'::jsonb) ? :source_id
OR coalesce(source_envelope #> '{source_refs,code_refs}', '[]'::jsonb) ? :source_id
)
ORDER BY queued_at DESC
LIMIT :limit

View File

@@ -514,7 +514,7 @@ async def record_outbound_message(
content_hash, content_preview, content_redacted,
redaction_version, source_envelope,
provider_message_id,
send_status, queued_at,
send_status, queued_at, sent_at,
triggered_by_state, waiting_since
) VALUES (
:project_id, :run_id, :conversation_event_id,
@@ -523,6 +523,7 @@ async def record_outbound_message(
:redaction_version, CAST(:source_envelope AS jsonb),
:provider_message_id,
:send_status, NOW(),
CASE WHEN :send_status = 'sent' THEN NOW() ELSE NULL END,
:triggered_by_state, :waiting_since
)
RETURNING message_id

View File

@@ -69,6 +69,7 @@ POLLING_LEADER_WATCH = 30 # seconds - 非 Leader Pod 每 30s 嘗試接管
logger = structlog.get_logger(__name__)
_TELEGRAM_BOT_URL_RE = re.compile(r"(api\.telegram\.org/bot)[^/\s]+")
_INCIDENT_ID_RE = re.compile(r"\bINC-\d{8}-[A-Z0-9]{4,}\b")
_CODE_REF_RE = re.compile(r"<code>([0-9a-f]{7,12})</code>", re.IGNORECASE)
def _top_gateway_bucket(
@@ -213,6 +214,9 @@ def _reply_markup_summary(payload: dict) -> dict[str, object]:
def _outbound_source_envelope(method: str, payload: dict) -> dict[str, object]:
"""Build a redaction-friendly source envelope for Channel Hub replay."""
text = str(payload.get("text") or payload.get("caption") or "")
incident_ids = sorted(set(_INCIDENT_ID_RE.findall(text)))
code_refs = sorted(set(match.group(1) for match in _CODE_REF_RE.finditer(text)))
return {
"adapter": "legacy_telegram_gateway",
"method": method,
@@ -222,6 +226,10 @@ def _outbound_source_envelope(method: str, payload: dict) -> dict[str, object]:
"disable_web_page_preview": payload.get("disable_web_page_preview"),
"has_reply_context": _has_reply_context(payload),
"reply_markup": _reply_markup_summary(payload),
"source_refs": {
"incident_ids": incident_ids[:20],
"code_refs": code_refs[:20],
},
}
# 2026-04-27 Claude Sonnet 4.6: B3 — LLM 動態 Telegram 按鈕 Feature Flag

View File

@@ -6,6 +6,7 @@ from src.services.channel_hub import (
ensure_completed_shadow_run,
format_grouped_alert_digest_text,
format_grouped_alert_event_content,
record_outbound_message,
)
@@ -79,10 +80,14 @@ class _FakeSession:
def __init__(self) -> None:
self.statement = ""
self.params = {}
self.statements = []
self.param_sets = []
async def execute(self, statement, params): # noqa: ANN001
self.statement = str(statement)
self.params = params
self.statements.append(str(statement))
self.param_sets.append(params)
return _FakeResult()
@@ -105,3 +110,27 @@ async def test_completed_shadow_run_sets_run_state_not_null_defaults() -> None:
assert "0, 3, 0.0000, 0" in session.statement
assert session.params["project_id"] == "awoooi"
assert session.params["run_id"] == run_id
async def test_record_outbound_message_sets_sent_at_for_sent_messages() -> None:
session = _FakeSession()
run_id = build_grouped_alert_run_id("awoooi", "telegram-message-13152")
await record_outbound_message(
session, # type: ignore[arg-type]
project_id="awoooi",
run_id=run_id,
channel_type="telegram",
channel_chat_id="-100123",
message_type="approval_request",
content="ACTION REQUIRED INC-20260513-9B082D",
provider_message_id="13152",
send_status="sent",
triggered_by_state="legacy_gateway",
is_shadow=False,
)
insert_statement = session.statements[-1]
assert "sent_at" in insert_statement
assert "CASE WHEN :send_status = 'sent' THEN NOW() ELSE NULL END" in insert_statement
assert session.param_sets[-1]["send_status"] == "sent"

View File

@@ -18,7 +18,11 @@ def test_telegram_gateway_sanitizes_bot_token_url() -> None:
def test_outbound_source_envelope_keeps_replay_context_without_raw_payload() -> None:
payload = {
"chat_id": "-100123",
"text": "ACTION REQUIRED token 1234567890:abcdefghijklmnopqrstuvwxyzABCDEFGH",
"text": (
"ACTION REQUIRED INC-20260513-9B082D "
"<code>7f858956</code> token "
"1234567890:abcdefghijklmnopqrstuvwxyzABCDEFGH"
),
"parse_mode": "HTML",
"reply_markup": {
"inline_keyboard": [
@@ -40,6 +44,8 @@ def test_outbound_source_envelope_keeps_replay_context_without_raw_payload() ->
assert envelope["reply_markup"]["button_count"] == 2
assert envelope["reply_markup"]["buttons"][0]["callback_prefix"] == "approve"
assert envelope["reply_markup"]["buttons"][1]["callback_prefix"] == "details"
assert envelope["source_refs"]["incident_ids"] == ["INC-20260513-9B082D"]
assert envelope["source_refs"]["code_refs"] == ["7f858956"]
assert "approval-id-secret" not in str(envelope)
assert "1234567890:" not in str(envelope)
assert "ACTION REQUIRED" not in str(envelope)

View File

@@ -7251,3 +7251,57 @@ scope=write
- T11 已部署Operator Run Detail 與 Telegram detail formatter 都已能呈現 MCP Gateway 摘要。
- 這次沒有發送真實 Telegram 測試訊息,避免洗版;改以 production pod 直接呼叫 detail formatter 驗證。
- 目前整體進度更新:約 68%。
### 2026-05-13 — AwoooP truth-chain T12aTelegram outbound 可回放關聯強化local green
**production live audit 摘要**
```text
24h:
incidents=150
approval_records=102
alert_operation_log=682
timeline_events=154
incident_evidence=130
auto_repair_executions=10
knowledge_entries=42
legacy_mcp_audit_log=1265
awooop_mcp_gateway_audit=1365
awooop_outbound_message=420
outbound_quality:
total=420 redacted=226 envelope=420 envelope_schema=226 with_run=420 sent_at=0
incident_join_quality:
total=150 with_aol=100 with_evidence=102 with_legacy_mcp=102 with_timeline=102 with_approval=102 with_auto_repair=10
Sentry / SignOz durable event tables:
none found by information_schema table_name ILIKE '%sentry%' OR '%signoz%'
```
**判讀**
- MCP / SignOz 能力已有實際使用,且 legacy MCP bridge 已寫入 AwoooP Gateway audit。
- 仍不能宣稱完整 AI 自動修復24h incident 150 筆中只有 10 筆有 `auto_repair_executions`
- Telegram outbound mirror 有資料,但 `sent_at=0` 是真缺口source envelope 也缺少 structured source refstruth-chain 只能靠 `content_preview ILIKE` 猜關聯。
**變更**
- `record_outbound_message()``send_status='sent'` 時寫入 `sent_at=NOW()`
- Telegram outbound `source_envelope` 新增 `source_refs.incident_ids``source_refs.code_refs`,保留 redaction-friendly 關聯錨點。
- truth-chain outbound 查詢支援 structured `source_refs`,不只靠 preview 文字搜尋。
**local verification**
```text
DATABASE_URL=postgresql+asyncpg://u:p@localhost:5432/db python -m pytest tests/test_telegram_gateway_error_sanitizer.py tests/test_channel_hub_grouped_alert_events.py tests/test_awooop_truth_chain_service.py tests/test_telegram_adr050.py -q
51 passed
python -m ruff check --select F821 src/services/channel_hub.py src/services/telegram_gateway.py src/services/awooop_truth_chain_service.py tests/test_telegram_gateway_error_sanitizer.py tests/test_channel_hub_grouped_alert_events.py tests/test_telegram_adr050.py
All checks passed
python -m py_compile src/services/channel_hub.py src/services/telegram_gateway.py src/services/awooop_truth_chain_service.py tests/test_telegram_gateway_error_sanitizer.py tests/test_channel_hub_grouped_alert_events.py tests/test_telegram_adr050.py
OK
```
**目前整體進度**:約 69%。