fix(agent): project no-write lifecycle prefixes

This commit is contained in:
ogt
2026-07-14 16:32:59 +08:00
parent 138432ecaa
commit 7625cb9b0c
2 changed files with 113 additions and 18 deletions

View File

@@ -4088,22 +4088,47 @@ async def _load_missing_stdin_boundary_terminal_projection_rows(
'{detail,repository_readback_verified}' = 'true'
AND receipt.value ->> 'durable_receipt' = 'true'
)
AND NOT EXISTS (
SELECT 1
FROM jsonb_array_elements(
coalesce(
terminal.input -> 'runtime_stage_receipts',
'[]'::jsonb
)
) receipt(value)
WHERE receipt.value ->> 'stage_id' = 'incident_closure'
AND receipt.value ->> 'automation_run_id'
= terminal.input ->> 'automation_run_id'
AND receipt.value #>>
'{detail,terminal_op_id}' = terminal.op_id::text
AND receipt.value #>>
'{detail,projection_readback_verified}' = 'true'
AND receipt.value ->> 'durable_receipt' = 'true'
AND (
NOT EXISTS (
SELECT 1
FROM jsonb_array_elements(
coalesce(
terminal.input -> 'runtime_stage_receipts',
'[]'::jsonb
)
) receipt(value)
WHERE receipt.value ->> 'stage_id'
= 'incident_closure'
AND receipt.value ->> 'automation_run_id'
= terminal.input ->> 'automation_run_id'
AND receipt.value #>>
'{detail,terminal_op_id}' = terminal.op_id::text
AND receipt.value #>>
'{detail,projection_readback_verified}' = 'true'
AND receipt.value ->> 'durable_receipt' = 'true'
)
OR NOT EXISTS (
SELECT 1
FROM alert_operation_log lifecycle
WHERE lifecycle.incident_id = incident.incident_id
AND lifecycle.event_type::text
= 'AUTO_REPAIR_TRIGGERED'
AND lifecycle.context ->> 'automation_run_id'
= terminal.input ->> 'automation_run_id'
AND lifecycle.context ->> 'apply_op_id'
= terminal.op_id::text
)
OR NOT EXISTS (
SELECT 1
FROM alert_operation_log lifecycle
WHERE lifecycle.incident_id = incident.incident_id
AND lifecycle.event_type::text
= 'EXECUTION_STARTED'
AND lifecycle.context ->> 'automation_run_id'
= terminal.input ->> 'automation_run_id'
AND lifecycle.context ->> 'apply_op_id'
= terminal.op_id::text
)
)
ORDER BY terminal.created_at ASC, terminal.op_id ASC
LIMIT :limit
@@ -4504,6 +4529,30 @@ async def _verify_stdin_boundary_terminal_projection_readback(
'{callback_reply,apply_op_id}'
= CAST(:terminal_op_id AS text)
)
AND EXISTS (
SELECT 1
FROM alert_operation_log lifecycle
WHERE lifecycle.incident_id = :incident_id
AND lifecycle.event_type::text
= 'AUTO_REPAIR_TRIGGERED'
AND lifecycle.context
->> 'automation_run_id'
= :automation_run_id
AND lifecycle.context ->> 'apply_op_id'
= CAST(:terminal_op_id AS text)
)
AND EXISTS (
SELECT 1
FROM alert_operation_log lifecycle
WHERE lifecycle.incident_id = :incident_id
AND lifecycle.event_type::text
= 'EXECUTION_STARTED'
AND lifecycle.context
->> 'automation_run_id'
= :automation_run_id
AND lifecycle.context ->> 'apply_op_id'
= CAST(:terminal_op_id AS text)
)
AND EXISTS (
SELECT 1
FROM alert_operation_log lifecycle
@@ -4660,6 +4709,28 @@ async def backfill_missing_stdin_boundary_terminal_projections_once(
continue
stats["telegram_receipt_acknowledged"] += 1
trigger_lifecycle = await _append_alert_lifecycle_receipt(
claim,
"AUTO_REPAIR_TRIGGERED",
apply_op_id=terminal_op_id,
success=True,
action_detail=(
"stdin_boundary_no_write_replay_triggered"
),
project_id=project_id,
post_verifier_passed=None,
)
started_lifecycle = await _append_alert_lifecycle_receipt(
claim,
"EXECUTION_STARTED",
apply_op_id=terminal_op_id,
success=True,
action_detail=(
"stdin_boundary_no_write_replay_started"
),
project_id=project_id,
post_verifier_passed=None,
)
execution_lifecycle = await _append_alert_lifecycle_receipt(
claim,
"EXECUTION_COMPLETED",
@@ -4683,7 +4754,12 @@ async def backfill_missing_stdin_boundary_terminal_projections_once(
project_id=project_id,
post_verifier_passed=False,
)
if not execution_lifecycle or not telegram_lifecycle:
if not all((
trigger_lifecycle,
started_lifecycle,
execution_lifecycle,
telegram_lifecycle,
)):
stats["blockers"].append("lifecycle_projection_pending")
continue
stats["lifecycle_written"] += 1

View File

@@ -2145,12 +2145,16 @@ def test_stdin_boundary_terminal_projection_closes_only_from_durable_receipts(
assert "replay_of_check_mode_op_id" in source
assert "retry_of_check_mode_op_id" in source
assert "TELEGRAM_RESULT_SENT" in verifier_source
assert "AUTO_REPAIR_TRIGGERED" in loader_source
assert "EXECUTION_STARTED" in loader_source
assert "AUTO_REPAIR_TRIGGERED" in verifier_source
assert "EXECUTION_STARTED" in verifier_source
assert "km_playbook_writeback" in verifier_source
assert "playbook_trust" in verifier_source
assert "runtime_apply_executed" in verifier_source
assert verifier_source.count(
"CAST(:terminal_op_id AS text)"
) == 4
) == 6
@pytest.mark.asyncio
@@ -2307,6 +2311,14 @@ async def test_stdin_boundary_terminal_projection_runs_no_write_pipeline(
assert result["runtime_apply_executed"] is False
assert result["blockers"] == []
assert ("telegram_send", "no_write_replay") in calls
assert (
"lifecycle",
("AUTO_REPAIR_TRIGGERED", terminal_op_id, True),
) in calls
assert (
"lifecycle",
("EXECUTION_STARTED", terminal_op_id, True),
) in calls
assert (
"lifecycle",
("EXECUTION_COMPLETED", terminal_op_id, False),
@@ -2315,6 +2327,13 @@ async def test_stdin_boundary_terminal_projection_runs_no_write_pipeline(
"lifecycle",
("TELEGRAM_RESULT_SENT", terminal_op_id, False),
) in calls
lifecycle_calls = [value for name, value in calls if name == "lifecycle"]
assert lifecycle_calls == [
("AUTO_REPAIR_TRIGGERED", terminal_op_id, True),
("EXECUTION_STARTED", terminal_op_id, True),
("EXECUTION_COMPLETED", terminal_op_id, False),
("TELEGRAM_RESULT_SENT", terminal_op_id, False),
]
append_calls = [value for name, value in calls if name == "append"]
assert len(append_calls) == 3
assert all(item[0] == terminal_op_id for item in append_calls)