From 7625cb9b0c09e8a56d9db1d235bb84e7ced776f2 Mon Sep 17 00:00:00 2001 From: ogt Date: Tue, 14 Jul 2026 16:32:59 +0800 Subject: [PATCH] fix(agent): project no-write lifecycle prefixes --- .../awooop_ansible_check_mode_service.py | 110 +++++++++++++++--- .../tests/test_awooop_truth_chain_service.py | 21 +++- 2 files changed, 113 insertions(+), 18 deletions(-) diff --git a/apps/api/src/services/awooop_ansible_check_mode_service.py b/apps/api/src/services/awooop_ansible_check_mode_service.py index 5b7b40c0f..c7f23a1f6 100644 --- a/apps/api/src/services/awooop_ansible_check_mode_service.py +++ b/apps/api/src/services/awooop_ansible_check_mode_service.py @@ -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 diff --git a/apps/api/tests/test_awooop_truth_chain_service.py b/apps/api/tests/test_awooop_truth_chain_service.py index 70acdea55..42e3acf01 100644 --- a/apps/api/tests/test_awooop_truth_chain_service.py +++ b/apps/api/tests/test_awooop_truth_chain_service.py @@ -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)