fix(agent): normalize incident terminal outcome
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m39s
CD Pipeline / build-and-deploy (push) Successful in 6m25s
CD Pipeline / post-deploy-checks (push) Successful in 2m9s

This commit is contained in:
ogt
2026-07-11 15:16:34 +08:00
parent 93f94c23be
commit f19b608005
2 changed files with 24 additions and 4 deletions

View File

@@ -2460,10 +2460,20 @@ async def _record_incident_terminal_disposition(
UPDATE incidents
SET status = CAST(:incident_status AS incidentstatus),
outcome = CAST(
coalesce(CAST(outcome AS jsonb), '{}'::jsonb)
|| jsonb_build_object(
'automation_terminal',
CAST(:terminal_payload AS jsonb)
jsonb_set(
CASE
WHEN outcome IS NULL THEN '{}'::jsonb
WHEN jsonb_typeof(CAST(outcome AS jsonb))
= 'object'
THEN CAST(outcome AS jsonb)
ELSE jsonb_build_object(
'legacy_outcome',
CAST(outcome AS jsonb)
)
END,
'{automation_terminal}',
CAST(:terminal_payload AS jsonb),
true
)
AS json
),

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import inspect
from unittest.mock import AsyncMock
import pytest
@@ -56,6 +57,15 @@ def test_runtime_stage_ids_only_accepts_durable_receipts() -> None:
assert stage_ids == {"mcp_context"}
def test_incident_terminal_writer_normalizes_legacy_non_object_outcome() -> None:
source = inspect.getsource(service._record_incident_terminal_disposition)
assert "jsonb_typeof(CAST(outcome AS jsonb))" in source
assert "jsonb_set(" in source
assert "'legacy_outcome'" in source
assert "'{automation_terminal}'" in source
@pytest.mark.asyncio
async def test_closure_refuses_to_resolve_when_any_receipt_is_missing(
monkeypatch: pytest.MonkeyPatch,