From c707d6dc1563fb876f6f0767c144467777e32051 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 19 Jul 2026 06:20:27 +0800 Subject: [PATCH] fix(sre): require unique durable learning receipts --- ...rt_learning_context_post_apply_verifier.py | 145 ++++++++++++++---- ...awoooi_priority_work_order_readback_api.py | 56 ++++++- ...earning_context_post_apply_verifier_api.py | 117 +++++++++++--- ...rolled-automation-work-items.snapshot.json | 9 +- 4 files changed, 273 insertions(+), 54 deletions(-) diff --git a/apps/api/src/services/telegram_alert_learning_context_post_apply_verifier.py b/apps/api/src/services/telegram_alert_learning_context_post_apply_verifier.py index 51904a489..63eb93aa9 100644 --- a/apps/api/src/services/telegram_alert_learning_context_post_apply_verifier.py +++ b/apps/api/src/services/telegram_alert_learning_context_post_apply_verifier.py @@ -1,9 +1,9 @@ """Telegram alert learning context post-apply verifier readback. -Verifies metadata-only Telegram alert learning context receipts emitted by the -AI Loop consumer readback. This module only reads public-safe receipt metadata; -it does not write KM/RAG/PlayBook, call MCP tools, send Telegram, or touch -runtime infrastructure. +Verifies public-safe Telegram alert learning receipts and requires durable +target-write evidence for every learning target. This module remains a +read-only independent verifier; it does not write KM/RAG/PlayBook, call MCP +tools, send Telegram, or touch runtime infrastructure. """ from __future__ import annotations @@ -81,7 +81,8 @@ def build_telegram_alert_learning_context_post_apply_verifier( _verify_context_receipt(receipt) for receipt in context_receipts ] - target_count = _safe_int(telegram_context.get("target_count")) or len(_TARGETS) + expected_target_count = len(_TARGETS) + reported_target_count = _safe_int(telegram_context.get("target_count")) verified_targets = sorted( { str(item["target"]) @@ -97,6 +98,20 @@ def build_telegram_alert_learning_context_post_apply_verifier( failed_results = [ item for item in verifier_results if item["verifier_status"] != "passed" ] + verified_target_context_write_count = sum( + 1 + for item in verifier_results + if item["verified_target_context_write_performed"] is True + ) + verified_target_context_write_receipt_ids = { + str(item["target_context_write_receipt_id"]) + for item in verifier_results + if item["verified_target_context_write_performed"] is True + and item["target_context_write_receipt_id"] + } + verified_target_context_write_receipt_count = len( + verified_target_context_write_receipt_ids + ) source_ready = ( telegram_context.get("status") == "ai_loop_agent_context_receipt_ready" ) @@ -104,7 +119,11 @@ def build_telegram_alert_learning_context_post_apply_verifier( source_ready and context_receipts and not failed_results - and len(verified_targets) == target_count + and reported_target_count == expected_target_count + and len(context_receipts) == expected_target_count + and set(verified_targets) == set(_TARGETS) + and verified_target_context_write_count == expected_target_count + and verified_target_context_write_receipt_count == expected_target_count and verified_ai_agent_count > 0 ) active_blockers = _active_blockers( @@ -112,7 +131,13 @@ def build_telegram_alert_learning_context_post_apply_verifier( context_receipts=context_receipts, failed_results=failed_results, verified_targets=verified_targets, - target_count=target_count, + context_receipt_count=len(context_receipts), + reported_target_count=reported_target_count, + expected_target_count=expected_target_count, + verified_target_context_write_count=verified_target_context_write_count, + verified_target_context_write_receipt_count=( + verified_target_context_write_receipt_count + ), verified_ai_agent_count=verified_ai_agent_count, ) @@ -157,7 +182,8 @@ def build_telegram_alert_learning_context_post_apply_verifier( 1 for item in verifier_results if item["verifier_status"] == "passed" ), "failed_context_receipt_count": len(failed_results), - "target_count": target_count, + "target_count": expected_target_count, + "reported_target_count": reported_target_count, "verified_target_count": len(verified_targets), "verified_targets": verified_targets, "verified_ai_agent_context_receipt_count": verified_ai_agent_count, @@ -165,9 +191,7 @@ def build_telegram_alert_learning_context_post_apply_verifier( 1 for item in verifier_results if item["target_selector_verified"] ), "source_of_truth_diff_verified_count": sum( - 1 - for item in verifier_results - if item["source_of_truth_diff_verified"] + 1 for item in verifier_results if item["source_of_truth_diff_verified"] ), "check_mode_verified_count": sum( 1 for item in verifier_results if item["check_mode_verified"] @@ -178,10 +202,11 @@ def build_telegram_alert_learning_context_post_apply_verifier( "post_apply_verifier_ref_count": sum( len(item["post_apply_verifier_refs"]) for item in verifier_results ), - "verified_target_context_write_count": sum( - 1 - for item in verifier_results - if item["verified_target_context_write_performed"] is True + "verified_target_context_write_count": ( + verified_target_context_write_count + ), + "verified_target_context_write_receipt_count": ( + verified_target_context_write_receipt_count ), "metadata_only_receipt_count": sum( 1 for item in verifier_results if item["metadata_only"] is True @@ -198,7 +223,12 @@ def build_telegram_alert_learning_context_post_apply_verifier( "next_action": ( "surface_verified_telegram_alert_context_receipts_in_awooop_runs_work_items_alerts" if ready - else "fix_failed_telegram_alert_context_receipts_then_rerun_post_apply_verifier" + else ( + "collect_exact_six_unique_durable_consumer_write_receipts_then_rerun_post_apply_verifier" + if verified_target_context_write_count != expected_target_count + or verified_target_context_write_receipt_count != expected_target_count + else "fix_failed_telegram_alert_context_receipts_then_rerun_post_apply_verifier" + ) ), "operation_boundaries": { "metadata_read_performed": True, @@ -238,14 +268,18 @@ def _verify_context_receipt(receipt: dict[str, Any]) -> dict[str, Any]: else _ALERT_CARD_TARGET_SELECTOR_FIELDS ) required_checks = ( - _CONSUMER_REQUIRED_CHECKS - if consumer_receipt - else _ALERT_CARD_REQUIRED_CHECKS + _CONSUMER_REQUIRED_CHECKS if consumer_receipt else _ALERT_CARD_REQUIRED_CHECKS ) target_selector_missing = [ key for key in required_target_selector_fields if not target_selector.get(key) ] + target_selector_contract_verified = _target_selector_contract_verified( + receipt, + target_selector=target_selector, + target=target, + receipt_kind=receipt_kind, + ) checks = _strings(check_mode.get("checks")) missing_checks = [check for check in required_checks if check not in checks] source_diff_verified = _source_diff_verified( @@ -267,6 +301,8 @@ def _verify_context_receipt(receipt: dict[str, Any]) -> dict[str, Any]: failed_checks.append("receipt_ready_for_ai_loop_context") if target_selector_missing: failed_checks.append("target_selector_required_fields_present") + elif not target_selector_contract_verified: + failed_checks.append("target_selector_bound_to_receipt") if not source_diff_verified: failed_checks.append("source_of_truth_diff_verified") if check_mode.get("enabled") is not True or missing_checks: @@ -296,6 +332,7 @@ def _verify_context_receipt(receipt: dict[str, Any]) -> dict[str, Any]: "receipt_kind_supported", "receipt_ready_for_ai_loop_context", "target_selector_required_fields_present", + "target_selector_bound_to_receipt", "source_of_truth_diff_verified", "check_mode_required_checks_present", "rollback_ref_present", @@ -320,7 +357,9 @@ def _verify_context_receipt(receipt: dict[str, Any]) -> dict[str, Any]: "verifier_status": "passed" if not failed_checks else "failed", "passed_checks": passed_checks, "failed_checks": failed_checks, - "target_selector_verified": not target_selector_missing, + "target_selector_verified": ( + not target_selector_missing and target_selector_contract_verified + ), "target_selector_missing_fields": target_selector_missing, "source_of_truth_diff_verified": source_diff_verified, "check_mode_verified": check_mode.get("enabled") is True and not missing_checks, @@ -334,6 +373,14 @@ def _verify_context_receipt(receipt: dict[str, Any]) -> dict[str, Any]: receipt.get("target_write_performed") is True and target_write_contract_verified ), + "target_context_write_receipt_id": ( + str( + _dict(receipt.get("consumer_write_receipt")).get("consumer_receipt_id") + or "" + ) + if target_write_contract_verified and receipt_kind == _CONSUMER_RECEIPT_KIND + else "" + ), "verifier_write_performed": False, "metadata_only": receipt.get("metadata_only") is True, "raw_payload_included": receipt.get("raw_payload_included") is True, @@ -355,7 +402,11 @@ def _active_blockers( context_receipts: list[dict[str, Any]], failed_results: list[dict[str, Any]], verified_targets: list[str], - target_count: int, + context_receipt_count: int, + reported_target_count: int, + expected_target_count: int, + verified_target_context_write_count: int, + verified_target_context_write_receipt_count: int, verified_ai_agent_count: int, ) -> list[str]: blockers: list[str] = [] @@ -363,8 +414,22 @@ def _active_blockers( blockers.append("telegram_alert_learning_context_source_not_ready") if not context_receipts: blockers.append("telegram_alert_learning_context_receipts_missing") - if len(verified_targets) < target_count: - blockers.append("telegram_alert_learning_context_verified_target_count_below_target") + if reported_target_count != expected_target_count: + blockers.append("telegram_alert_learning_context_target_count_not_exact") + if context_receipt_count != expected_target_count: + blockers.append("telegram_alert_learning_context_receipt_count_not_exact") + if set(verified_targets) != set(_TARGETS): + blockers.append( + "telegram_alert_learning_context_verified_target_count_below_target" + ) + if verified_target_context_write_count != expected_target_count: + blockers.append( + "telegram_alert_learning_context_verified_target_write_count_not_exact" + ) + if verified_target_context_write_receipt_count != expected_target_count: + blockers.append( + "telegram_alert_learning_context_unique_target_write_receipt_count_not_exact" + ) if verified_ai_agent_count <= 0: blockers.append("telegram_alert_learning_context_ai_agent_receipt_not_verified") for item in failed_results: @@ -422,16 +487,44 @@ def _target_write_contract_verified( if receipt_kind != _CONSUMER_RECEIPT_KIND: return receipt.get("target_write_performed") is False consumer_write_receipt = _dict(receipt.get("consumer_write_receipt")) + target_selector = _dict(receipt.get("target_selector")) + consumer_receipt_id = str(consumer_write_receipt.get("consumer_receipt_id") or "") return bool( receipt.get("target_write_performed") is True - and consumer_write_receipt.get("consumer_receipt_id") + and consumer_receipt_id + and consumer_receipt_id == str(receipt.get("source_receipt_id") or "") + and consumer_receipt_id == str(target_selector.get("consumer_receipt_id") or "") and consumer_write_receipt.get("status") == "success" - and consumer_write_receipt.get("target_context_receipt_write_performed") - is True + and consumer_write_receipt.get("target_context_receipt_write_performed") is True and consumer_write_receipt.get("runtime_target_write_performed") is True ) +def _target_selector_contract_verified( + receipt: Mapping[str, Any], + *, + target_selector: Mapping[str, Any], + target: str, + receipt_kind: str, +) -> bool: + if target_selector.get("target") != target: + return False + if target_selector.get("source_ref") != receipt.get("source_ref"): + return False + if receipt_kind == _CONSUMER_RECEIPT_KIND: + consumer_write_receipt = _dict(receipt.get("consumer_write_receipt")) + return bool( + target_selector.get("dispatch_receipt_id") == receipt.get("source_ref") + and target_selector.get("consumer_receipt_id") + == receipt.get("source_receipt_id") + == consumer_write_receipt.get("consumer_receipt_id") + ) + return bool( + target_selector.get("run_id") == receipt.get("source_run_id") + and target_selector.get("message_id") == receipt.get("source_message_id") + ) + + def _dict(value: Any) -> dict[str, Any]: return value if isinstance(value, dict) else {} diff --git a/apps/api/tests/test_awoooi_priority_work_order_readback_api.py b/apps/api/tests/test_awoooi_priority_work_order_readback_api.py index e52f6e5e4..65f904c35 100644 --- a/apps/api/tests/test_awoooi_priority_work_order_readback_api.py +++ b/apps/api/tests/test_awoooi_priority_work_order_readback_api.py @@ -3975,6 +3975,60 @@ def _telegram_context_receipts() -> list[dict]: ] +def _durable_telegram_context_receipts() -> list[dict]: + receipts = _telegram_context_receipts() + for receipt in receipts: + target = receipt["target"] + dispatch_receipt_id = f"log_controlled_writeback_dispatched::{target}" + consumer_receipt_id = f"log_controlled_writeback_consumed::{target}" + receipt.update( + { + "receipt_kind": "log_controlled_writeback_consumer", + "receipt_id": ( + "telegram_alert_learning_context_fallback::" + f"{consumer_receipt_id}" + ), + "source_receipt_id": consumer_receipt_id, + "source_ref": dispatch_receipt_id, + "target_write_performed": True, + "verifier_write_performed": False, + "consumer_write_receipt": { + "consumer_receipt_id": consumer_receipt_id, + "status": "success", + "target_context_receipt_write_performed": True, + "runtime_target_write_performed": True, + }, + "target_selector": { + "project_id": "awoooi", + "dispatch_receipt_id": dispatch_receipt_id, + "consumer_receipt_id": consumer_receipt_id, + "target": target, + "source_ref": dispatch_receipt_id, + }, + "source_of_truth_diff": { + "current_state": ( + "telegram_alert_registry_unavailable_or_not_ready" + ), + "desired_state": "ai_loop_agent_context_receipt_available", + "delta_kind": ( + f"log_controlled_writeback_{target}_context_fallback" + ), + "raw_payload_included": False, + }, + "check_mode": { + "enabled": True, + "checks": [ + "consumer_binding_ready", + "consumer_apply_receipt_present", + "metadata_only_raw_payload_absent", + "post_apply_verifier_refs_present", + ], + }, + } + ) + return receipts + + def _consumer_readback_ready() -> dict: return { "schema_version": "ai_agent_log_controlled_writeback_consumer_readback_v1", @@ -4039,7 +4093,7 @@ def _consumer_readback_ready() -> dict: "next_action": ( "verify_telegram_alert_learning_context_receipts_with_ai_loop_post_apply_verifier" ), - "context_receipts": _telegram_context_receipts(), + "context_receipts": _durable_telegram_context_receipts(), "active_blockers": [], "operation_boundaries": { "metadata_read_performed": True, diff --git a/apps/api/tests/test_telegram_alert_learning_context_post_apply_verifier_api.py b/apps/api/tests/test_telegram_alert_learning_context_post_apply_verifier_api.py index 816108adb..42474d0b6 100644 --- a/apps/api/tests/test_telegram_alert_learning_context_post_apply_verifier_api.py +++ b/apps/api/tests/test_telegram_alert_learning_context_post_apply_verifier_api.py @@ -111,9 +111,7 @@ def _fallback_context_receipts() -> list[dict]: "metadata_only": True, "target_write_performed": True, "consumer_write_receipt": { - "consumer_receipt_id": ( - f"log_controlled_writeback_consumed::{target}" - ), + "consumer_receipt_id": (f"log_controlled_writeback_consumed::{target}"), "status": "success", "target_context_receipt_write_performed": True, "runtime_target_write_performed": True, @@ -125,20 +123,14 @@ def _fallback_context_receipts() -> list[dict]: "dispatch_receipt_id": ( f"log_controlled_writeback_dispatched::{target}" ), - "consumer_receipt_id": ( - f"log_controlled_writeback_consumed::{target}" - ), + "consumer_receipt_id": (f"log_controlled_writeback_consumed::{target}"), "target": target, "source_ref": f"log_controlled_writeback_dispatched::{target}", }, "source_of_truth_diff": { - "current_state": ( - "telegram_alert_registry_unavailable_or_not_ready" - ), + "current_state": ("telegram_alert_registry_unavailable_or_not_ready"), "desired_state": "ai_loop_agent_context_receipt_available", - "delta_kind": ( - f"log_controlled_writeback_{target}_context_fallback" - ), + "delta_kind": (f"log_controlled_writeback_{target}_context_fallback"), "raw_payload_included": False, }, "check_mode": { @@ -180,12 +172,14 @@ def _consumer_readback(*, receipts: list[dict] | None = None) -> dict: "ready_target_count": 6, "context_receipt_count": 6, "ai_agent_context_receipt_count": 1, - "context_receipts": receipts if receipts is not None else _context_receipts(), + "context_receipts": receipts + if receipts is not None + else _context_receipts(), }, } -def test_telegram_alert_learning_context_post_apply_verifier_passes_receipts(): +def test_telegram_alert_learning_context_post_apply_verifier_blocks_metadata_only_receipts(): payload = build_telegram_alert_learning_context_post_apply_verifier( consumer_readback=_consumer_readback() ) @@ -194,17 +188,23 @@ def test_telegram_alert_learning_context_post_apply_verifier_passes_receipts(): "telegram_alert_learning_context_post_apply_verifier_v1" ) assert payload["status"] == ( - "telegram_alert_learning_context_post_apply_verified" + "blocked_telegram_alert_learning_context_post_apply_verifier" + ) + assert payload["active_blockers"] == [ + "telegram_alert_learning_context_verified_target_write_count_not_exact", + "telegram_alert_learning_context_unique_target_write_receipt_count_not_exact", + ] + assert payload["next_action"] == ( + "collect_exact_six_unique_durable_consumer_write_receipts_then_rerun_post_apply_verifier" ) - assert payload["active_blockers"] == [] assert payload["controlled_verifier"]["post_apply_verifier_executed"] is True - assert payload["controlled_verifier"]["raw_payload_absence_verified"] is True + assert payload["controlled_verifier"]["raw_payload_absence_verified"] is False assert payload["operation_boundaries"]["runtime_target_write_performed"] is False assert payload["operation_boundaries"]["telegram_send_performed"] is False assert payload["operation_boundaries"]["secret_value_collection_allowed"] is False rollups = payload["rollups"] - assert rollups["telegram_alert_learning_context_post_apply_verifier_ready"] is True + assert rollups["telegram_alert_learning_context_post_apply_verifier_ready"] is False assert rollups["context_receipt_count"] == 6 assert rollups["verified_context_receipt_count"] == 6 assert rollups["verified_target_count"] == 6 @@ -233,15 +233,15 @@ def test_telegram_alert_learning_context_post_apply_verifier_passes_fallback_rec consumer_readback=_consumer_readback(receipts=_fallback_context_receipts()) ) - assert payload["status"] == ( - "telegram_alert_learning_context_post_apply_verified" - ) + assert payload["status"] == ("telegram_alert_learning_context_post_apply_verified") assert payload["active_blockers"] == [] assert payload["rollups"]["verified_context_receipt_count"] == 6 + assert payload["rollups"]["reported_target_count"] == 6 assert payload["rollups"]["verified_target_context_write_count"] == 6 - assert { - result["receipt_kind"] for result in payload["verifier_results"] - } == {"log_controlled_writeback_consumer"} + assert payload["rollups"]["verified_target_context_write_receipt_count"] == 6 + assert {result["receipt_kind"] for result in payload["verifier_results"]} == { + "log_controlled_writeback_consumer" + } assert all( result["target_write_contract_verified"] is True and result["verified_target_context_write_performed"] is True @@ -250,6 +250,73 @@ def test_telegram_alert_learning_context_post_apply_verifier_passes_fallback_rec ) +def test_telegram_alert_learning_context_post_apply_verifier_rejects_shrunk_five_of_five_contract(): + receipts = _fallback_context_receipts()[:5] + readback = _consumer_readback(receipts=receipts) + readback["telegram_alert_learning_context"]["target_count"] = 5 + readback["telegram_alert_learning_context"]["context_receipt_count"] = 5 + + payload = build_telegram_alert_learning_context_post_apply_verifier( + consumer_readback=readback + ) + + assert payload["status"] == ( + "blocked_telegram_alert_learning_context_post_apply_verifier" + ) + assert payload["rollups"]["target_count"] == 6 + assert payload["rollups"]["reported_target_count"] == 5 + assert payload["rollups"]["verified_target_context_write_count"] == 5 + assert payload["active_blockers"] == [ + "telegram_alert_learning_context_target_count_not_exact", + "telegram_alert_learning_context_receipt_count_not_exact", + "telegram_alert_learning_context_verified_target_count_below_target", + "telegram_alert_learning_context_verified_target_write_count_not_exact", + "telegram_alert_learning_context_unique_target_write_receipt_count_not_exact", + "telegram_alert_learning_context_ai_agent_receipt_not_verified", + ] + + +def test_telegram_alert_learning_context_post_apply_verifier_rejects_reused_write_receipt_id(): + receipts = _fallback_context_receipts() + reused_id = receipts[0]["source_receipt_id"] + receipts[1]["source_receipt_id"] = reused_id + receipts[1]["target_selector"]["consumer_receipt_id"] = reused_id + receipts[1]["consumer_write_receipt"]["consumer_receipt_id"] = reused_id + + payload = build_telegram_alert_learning_context_post_apply_verifier( + consumer_readback=_consumer_readback(receipts=receipts) + ) + + assert payload["rollups"]["verified_target_context_write_count"] == 6 + assert payload["rollups"]["verified_target_context_write_receipt_count"] == 5 + assert payload["status"] == ( + "blocked_telegram_alert_learning_context_post_apply_verifier" + ) + assert payload["active_blockers"] == [ + "telegram_alert_learning_context_unique_target_write_receipt_count_not_exact" + ] + + +def test_telegram_alert_learning_context_post_apply_verifier_rejects_mismatched_write_receipt_id(): + receipts = _fallback_context_receipts() + receipts[0]["consumer_write_receipt"]["consumer_receipt_id"] = "wrong-receipt" + + payload = build_telegram_alert_learning_context_post_apply_verifier( + consumer_readback=_consumer_readback(receipts=receipts) + ) + + failed = [ + result + for result in payload["verifier_results"] + if result["verifier_status"] == "failed" + ] + assert failed[0]["failed_checks"] == [ + "target_selector_bound_to_receipt", + "consumer_write_receipt_verified", + ] + assert payload["rollups"]["verified_target_context_write_count"] == 5 + + def test_telegram_alert_learning_context_post_apply_verifier_blocks_bad_fallback_receipt(): receipts = _fallback_context_receipts() receipts[0]["consumer_write_receipt"]["runtime_target_write_performed"] = False @@ -310,7 +377,7 @@ def test_telegram_alert_learning_context_post_apply_verifier_rejects_unknown_kin def test_telegram_alert_learning_context_post_apply_verifier_endpoint(monkeypatch): async def fake_loader(): return build_telegram_alert_learning_context_post_apply_verifier( - consumer_readback=_consumer_readback() + consumer_readback=_consumer_readback(receipts=_fallback_context_receipts()) ) monkeypatch.setattr( diff --git a/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json b/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json index d01997a11..bb08a961d 100644 --- a/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json +++ b/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json @@ -816,7 +816,10 @@ "apps/api/src/services/agent99_controlled_dispatch_ledger.py", "apps/api/src/services/agent99_telegram_lifecycle.py", "apps/api/src/services/agent99_public_receipts.py", - "apps/api/src/repositories/knowledge_repository.py" + "apps/api/src/repositories/knowledge_repository.py", + "apps/api/src/services/telegram_alert_learning_context_post_apply_verifier.py", + "apps/api/src/services/telegram_alert_monitoring_coverage_readback.py", + "apps/web/src/app/[locale]/awooop/alerts/page.tsx" ], "executor": "learning writeback consumer", "verifier": "durable same-run acknowledgement readback", @@ -827,7 +830,9 @@ "Backup/restore recipient-visible recovered cards name Agent99 BackupCheck as the read-only executor and backup_restore_readback_verifier as the typed independent verifier; apply remains not_applicable and the final durable closure receipt is required", "BackupCheck learning assets are reconciled idempotently as read-only DR evidence rather than controlled repair", "the Telegram learning verifier now distinguishes alert-card registry receipts from log-controlled consumer receipts, verifies the latter only with an exact successful consumer write receipt, and keeps verifier runtime writes false", - "the monitoring coverage readback no longer lets consumer candidates or alert-card metadata refs bypass the independent verifier; learning readiness requires every declared target write plus an AI Agent context receipt to be verified" + "the monitoring coverage readback no longer lets consumer candidates or alert-card metadata refs bypass the independent verifier; learning readiness requires every declared target write plus an AI Agent context receipt to be verified", + "the independent verifier now rejects metadata-only 0/6 receipts, shrunk 5/5 contracts, duplicated consumer write receipt IDs and selector/receipt ID mismatches; ready requires exactly six unique durable consumer-write receipts bound to the six canonical targets", + "the AwoooP alerts surface distinguishes verified, candidate and blocked learning closure and displays durable write, verified target, AI Agent receipt, evidence-source and blocker readbacks without claiming source evidence as production closure" ], "runtime_gaps": [ "production exposed six fallback context receipts but the deployed verifier rejected all six under the alert-card-only contract, while the deployed coverage could still treat unverified fallback metadata as ready; this source revision fixes both false-negative and false-green paths but still has no production same-run receipt",