From 3f6c62dd03534e745251fc7a250216e0a8818399 Mon Sep 17 00:00:00 2001 From: ogt Date: Fri, 10 Jul 2026 00:15:27 +0800 Subject: [PATCH 1/2] fix(gitea): classify alert receipt chain gaps --- ...gitea_cicd_alert_receipt_chain_readback.py | 87 ++++++++++++++++--- ...a_cicd_alert_receipt_chain_readback_api.py | 46 +++++++++- 2 files changed, 122 insertions(+), 11 deletions(-) diff --git a/apps/api/src/services/gitea_cicd_alert_receipt_chain_readback.py b/apps/api/src/services/gitea_cicd_alert_receipt_chain_readback.py index f92170e67..e6ca4f7c1 100644 --- a/apps/api/src/services/gitea_cicd_alert_receipt_chain_readback.py +++ b/apps/api/src/services/gitea_cicd_alert_receipt_chain_readback.py @@ -60,11 +60,28 @@ def build_gitea_cicd_alert_receipt_chain_readback( monitoring_gap_count = _int(coverage_summary.get("monitoring_live_receipt_gap_count")) full_dr_gap_count = _int(full_server_backup_dr_receipts.get("active_gap_count")) + full_dr_gap_ids = _list(full_server_backup_dr_receipts.get("active_gap_ids")) + full_dr_restore_drill_only = ( + full_dr_gap_ids == ["restore_drill_approval_gate"] + and _int(full_dr_summary.get("source_ready_component_count")) >= 6 + and _int(full_dr_summary.get("receipt_required_component_count")) == 0 + ) quiet_contract_count = len(_list(backup_rollups.get("suppressed_success_rule_ids"))) immediate_escalation_count = len( _list(backup_rollups.get("immediate_escalation_rule_ids")) ) notify_workflow_count = _int(workflow_rollups.get("workflows_with_notify_bridge")) + ai_context_source_ready = ( + coverage_summary.get("source_contract_ready") is True + and _int(coverage_summary.get("alert_receipt_pipeline_ready_count")) > 0 + ) + ai_context_verified_ready = ( + ( + coverage_summary.get("ai_loop_context_ready") is True + and _int(coverage_summary.get("ai_loop_ai_agent_context_receipt_count")) > 0 + ) + or _int(coverage_summary.get("verified_ai_agent_context_receipt_count")) > 0 + ) receipt_rows = [ { @@ -95,17 +112,30 @@ def build_gitea_cicd_alert_receipt_chain_readback( { "chain_id": "gitea_full_server_dr_receipts", "domain": "backup_dr", - "status": "action_required" if full_dr_gap_count else "verified_ready", - "receipt_state": "component_receipts_pending" - if full_dr_gap_count - else "component_receipts_ready", + "status": "verified_ready" + if not full_dr_gap_count + else "controlled_restore_drill_pending" + if full_dr_restore_drill_only + else "action_required", + "receipt_state": "component_receipts_ready" + if not full_dr_gap_count + else "source_contract_ready_restore_drill_pending" + if full_dr_restore_drill_only + else "component_receipts_pending", "evidence_refs": ["GET /api/v1/agents/gitea-full-server-backup-dr-receipts"], - "next_action": "close full-server component receipt rows before DR complete", + "next_action": "run a controlled restore-drill verifier before DR complete" + if full_dr_restore_drill_only + else "close full-server component receipt rows before DR complete", "metrics": { "full_server_active_gap_count": full_dr_gap_count, + "full_server_active_gap_ids": full_dr_gap_ids, "full_server_verified_ready_component_count": _int( full_dr_summary.get("verified_ready_component_count") ), + "full_server_source_ready_component_count": _int( + full_dr_summary.get("source_ready_component_count") + ), + "restore_drill_only_gap": full_dr_restore_drill_only, }, }, { @@ -132,14 +162,40 @@ def build_gitea_cicd_alert_receipt_chain_readback( { "chain_id": "ai_agent_context_receipts", "domain": "ai_context", - "status": "source_ready" - if _int(coverage_summary.get("verified_ai_agent_context_receipt_count")) > 0 + "status": "verified_ready" + if ai_context_verified_ready + else "source_ready" + if ai_context_source_ready else "action_required", - "receipt_state": "ai_context_receipts_projected", + "receipt_state": "ai_context_receipts_verified" + if ai_context_verified_ready + else "source_backed_ai_context_pipeline_ready" + if ai_context_source_ready + else "ai_context_receipts_projected", "evidence_refs": [ "GET /api/v1/agents/telegram-alert-monitoring-coverage-readback" ], - "next_action": "keep KM/RAG/MCP/PlayBook context receipts metadata-only", + "next_action": "keep KM/RAG/MCP/PlayBook context receipts metadata-only" + if ai_context_source_ready + else "write metadata-only KM/RAG/MCP/PlayBook context receipts", + "metrics": { + "source_contract_ready": coverage_summary.get("source_contract_ready") + is True, + "ai_loop_context_ready": coverage_summary.get("ai_loop_context_ready") + is True, + "ai_loop_context_source": str( + coverage_summary.get("ai_loop_context_source") or "" + ), + "ai_loop_ai_agent_context_receipt_count": _int( + coverage_summary.get("ai_loop_ai_agent_context_receipt_count") + ), + "verified_ai_agent_context_receipt_count": _int( + coverage_summary.get("verified_ai_agent_context_receipt_count") + ), + "alert_receipt_pipeline_ready_count": _int( + coverage_summary.get("alert_receipt_pipeline_ready_count") + ), + }, }, ] @@ -181,9 +237,20 @@ def build_gitea_cicd_alert_receipt_chain_readback( "source_or_policy_ready_chain_count": sum( 1 for row in receipt_rows - if row["status"] in {"source_ready", "policy_ready", "verified_ready"} + if row["status"] + in { + "source_ready", + "policy_ready", + "verified_ready", + "controlled_restore_drill_pending", + } ), "action_required_chain_count": len(active_gaps), + "controlled_restore_drill_pending_chain_count": sum( + 1 + for row in receipt_rows + if row["status"] == "controlled_restore_drill_pending" + ), "monitoring_live_receipt_gap_count": monitoring_gap_count, "full_server_dr_active_gap_count": full_dr_gap_count, "workflow_notify_bridge_count": notify_workflow_count, diff --git a/apps/api/tests/test_gitea_cicd_alert_receipt_chain_readback_api.py b/apps/api/tests/test_gitea_cicd_alert_receipt_chain_readback_api.py index 435009b82..56c6a1f79 100644 --- a/apps/api/tests/test_gitea_cicd_alert_receipt_chain_readback_api.py +++ b/apps/api/tests/test_gitea_cicd_alert_receipt_chain_readback_api.py @@ -73,7 +73,51 @@ def test_gitea_cicd_alert_receipt_chain_readback_surfaces_receipt_gaps() -> None assert by_id["backup_notification_policy"]["status"] == "policy_ready" assert by_id["gitea_full_server_dr_receipts"]["status"] == "action_required" assert by_id["telegram_monitoring_live_receipts"]["status"] == "action_required" - assert by_id["ai_agent_context_receipts"]["status"] == "source_ready" + assert by_id["ai_agent_context_receipts"]["status"] == "verified_ready" + + +def test_gitea_cicd_alert_receipt_chain_keeps_restore_drill_pending_separate() -> None: + full_dr = { + "schema_version": "gitea_full_server_backup_dr_receipts_v1", + "status": "gitea_full_server_backup_dr_receipts_gaps_present", + "active_gap_count": 1, + "active_gap_ids": ["restore_drill_approval_gate"], + "summary": { + "verified_ready_component_count": 3, + "source_ready_component_count": 6, + "receipt_required_component_count": 0, + }, + } + telegram_coverage = { + **_telegram_coverage(), + "summary": { + **_telegram_coverage()["summary"], + "source_contract_ready": True, + "ai_loop_context_ready": False, + "ai_loop_ai_agent_context_receipt_count": 0, + }, + } + + payload = build_gitea_cicd_alert_receipt_chain_readback( + telegram_coverage=telegram_coverage, + backup_notification_policy=load_latest_backup_notification_policy(), + workflow_runner_health=load_latest_gitea_workflow_runner_health(), + full_server_backup_dr_receipts=full_dr, + generated_at="2026-07-10T00:21:00+08:00", + ) + + assert payload["summary"]["action_required_chain_count"] == 1 + assert payload["summary"]["controlled_restore_drill_pending_chain_count"] == 1 + assert payload["active_gap_ids"] == ["telegram_monitoring_live_receipts"] + + by_id = {row["chain_id"]: row for row in payload["receipt_chain_rows"]} + assert by_id["gitea_full_server_dr_receipts"]["status"] == ( + "controlled_restore_drill_pending" + ) + assert by_id["gitea_full_server_dr_receipts"]["metrics"][ + "restore_drill_only_gap" + ] is True + assert by_id["ai_agent_context_receipts"]["status"] == "verified_ready" @pytest.mark.asyncio From 5b1ccabcee620478a72e8e3aa80436b90f371d2e Mon Sep 17 00:00:00 2001 From: AWOOOI CD Date: Fri, 10 Jul 2026 00:33:57 +0800 Subject: [PATCH 2/2] chore(cd): deploy e2cfbaa [skip ci] --- k8s/awoooi-prod/06-deployment-api.yaml | 4 ++-- k8s/awoooi-prod/kustomization.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/k8s/awoooi-prod/06-deployment-api.yaml b/k8s/awoooi-prod/06-deployment-api.yaml index f226006e2..fd6bf4388 100644 --- a/k8s/awoooi-prod/06-deployment-api.yaml +++ b/k8s/awoooi-prod/06-deployment-api.yaml @@ -84,12 +84,12 @@ spec: - name: AWOOOI_BUILD_COMMIT_SHA # 2026-06-29 Codex: CD rewrites this to the deployed image tag so # production deploy readback does not rely on a stale static snapshot. - value: "e150a85a6d6d7b422a23aedc2dcb434755969dfd" + value: "e2cfbaabc8dd50cbd2605caf39c7d518b633f361" - name: AWOOOI_DESIRED_API_IMAGE_TAG # 2026-06-30 Codex: CD rewrites this alongside AWOOOI_BUILD_COMMIT_SHA. # Production readback compares runtime image truth against this # GitOps desired tag instead of doing a slow Gitea raw fetch. - value: "e150a85a6d6d7b422a23aedc2dcb434755969dfd" + value: "e2cfbaabc8dd50cbd2605caf39c7d518b633f361" - name: DATABASE_POOL_SIZE # 2026-07-01 Codex: production role `awoooi` currently has a low # connection limit. Keep API pool conservative until DB role diff --git a/k8s/awoooi-prod/kustomization.yaml b/k8s/awoooi-prod/kustomization.yaml index cce9ed669..ba0fe4833 100644 --- a/k8s/awoooi-prod/kustomization.yaml +++ b/k8s/awoooi-prod/kustomization.yaml @@ -41,7 +41,7 @@ resources: images: - name: 192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/api - newTag: e150a85a6d6d7b422a23aedc2dcb434755969dfd + newTag: e2cfbaabc8dd50cbd2605caf39c7d518b633f361 - name: 192.168.0.110:5000/library/web:IMAGE_TAG_PLACEHOLDER newName: 192.168.0.110:5000/awoooi/web - newTag: e150a85a6d6d7b422a23aedc2dcb434755969dfd + newTag: e2cfbaabc8dd50cbd2605caf39c7d518b633f361