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 fc7c55f2d..2363d3b14 100644 --- a/apps/api/src/services/awooop_ansible_check_mode_service.py +++ b/apps/api/src/services/awooop_ansible_check_mode_service.py @@ -3608,6 +3608,21 @@ async def _read_verified_apply_closure_prerequisites( AND outbound.source_envelope #>> '{notification_policy,disposition}' = 'suppressed' + AND nullif( + outbound.source_envelope #>> + '{notification_policy,policy_version}', + '' + ) IS NOT NULL + AND outbound.source_envelope #>> + '{notification_policy,provider_delivery}' + IN ( + 'shadow_only', + 'state_transition', + 'digest' + ) + AND outbound.source_envelope #>> + '{notification_policy,provider_send_performed}' + = 'false' AND coalesce( outbound.source_envelope #>> '{callback_reply,execution_kind}', @@ -4631,7 +4646,6 @@ async def _retry_telegram_receipt_acknowledged( project_id: str, ) -> bool: automation_run_id = _automation_run_id_for_claim(claim) - telegram_shadow_allowed = _claim_is_no_write_observation(claim) try: async with get_db_context(project_id) as db: result = await db.execute( @@ -4647,18 +4661,31 @@ async def _retry_telegram_receipt_acknowledged( AND outbound.provider_message_id IS NOT NULL ) OR ( - :telegram_shadow_allowed - AND outbound.send_status = 'shadow' + outbound.send_status = 'shadow' AND outbound.provider_message_id IS NULL AND outbound.source_envelope #>> '{notification_policy,disposition}' = 'suppressed' + AND nullif( + outbound.source_envelope #>> + '{notification_policy,policy_version}', + '' + ) IS NOT NULL + AND outbound.source_envelope #>> + '{notification_policy,provider_delivery}' + IN ('shadow_only', 'digest') + AND outbound.source_envelope #>> + '{notification_policy,provider_send_performed}' + = 'false' AND coalesce( outbound.source_envelope #>> '{callback_reply,execution_kind}', outbound.source_envelope ->> 'execution_kind' - ) = 'no_write_posture_readback' + ) IN ( + 'no_write_posture_readback', + 'no_write_replay' + ) ) ) AND outbound.source_envelope #>> @@ -4687,7 +4714,6 @@ async def _retry_telegram_receipt_acknowledged( "automation_run_id": automation_run_id, "incident_id": claim.incident_id, "apply_op_id": apply_op_id, - "telegram_shadow_allowed": telegram_shadow_allowed, }, ) return result.scalar() is True @@ -4759,8 +4785,38 @@ async def _record_retry_runtime_stage_receipt( FROM awooop_outbound_message WHERE project_id = :project_id AND channel_type = 'telegram' - AND send_status = 'sent' - AND provider_message_id IS NOT NULL + AND ( + ( + send_status = 'sent' + AND provider_message_id IS NOT NULL + ) + OR ( + send_status = 'shadow' + AND provider_message_id IS NULL + AND source_envelope #>> + '{notification_policy,disposition}' + = 'suppressed' + AND nullif( + source_envelope #>> + '{notification_policy,policy_version}', + '' + ) IS NOT NULL + AND source_envelope #>> + '{notification_policy,provider_delivery}' + IN ('shadow_only', 'digest') + AND source_envelope #>> + '{notification_policy,provider_send_performed}' + = 'false' + AND coalesce( + source_envelope #>> + '{callback_reply,execution_kind}', + source_envelope ->> 'execution_kind' + ) IN ( + 'no_write_posture_readback', + 'no_write_replay' + ) + ) + ) AND source_envelope #>> '{callback_reply,action}' = 'controlled_apply_result' AND COALESCE( @@ -8305,6 +8361,7 @@ async def backfill_missing_retry_terminal_projections_once( "learning": False, }, project_id=project_id, + execution_kind="no_write_replay", ) if receipt_sent: telegram_acknowledged = ( diff --git a/apps/api/tests/test_ansible_verified_closure.py b/apps/api/tests/test_ansible_verified_closure.py index cb00407dd..347b176e8 100644 --- a/apps/api/tests/test_ansible_verified_closure.py +++ b/apps/api/tests/test_ansible_verified_closure.py @@ -2232,6 +2232,8 @@ async def test_retry_telegram_readback_binds_exact_apply_operation( assert acknowledged is True assert "{callback_reply,apply_op_id}" in db.statements[0] + assert "no_write_replay" in db.statements[0] + assert "provider_send_performed" in db.statements[0] assert db.parameters[0]["apply_op_id"] == apply_op_id @@ -2542,6 +2544,9 @@ async def test_retry_terminal_projection_backfill_writes_and_verifies_no_apply( assert lifecycle_writer.await_args.args[1] == "EXECUTION_COMPLETED" assert lifecycle_writer.await_args.kwargs["success"] is False telegram_sender.assert_awaited_once() + assert telegram_sender.await_args.kwargs["execution_kind"] == ( + "no_write_replay" + ) telegram_readback.assert_awaited_once()