From 207f00522a4a6bf4ac0c48ca05a69533803d98ad Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 19 Jul 2026 01:12:32 +0800 Subject: [PATCH] fix(sre): verify DR scorecard RAG writeback --- ...nt99_controlled_dispatch_reconciler_job.py | 28 ++++++++- ...nt99_controlled_dispatch_reconciler_job.py | 60 +++++++++++++++++++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/apps/api/src/jobs/agent99_controlled_dispatch_reconciler_job.py b/apps/api/src/jobs/agent99_controlled_dispatch_reconciler_job.py index 0a1f4d9ac..cf20cd968 100644 --- a/apps/api/src/jobs/agent99_controlled_dispatch_reconciler_job.py +++ b/apps/api/src/jobs/agent99_controlled_dispatch_reconciler_job.py @@ -31,6 +31,8 @@ from src.services.agent99_controlled_dispatch_ledger import ( validate_agent99_outcome_identity, ) from src.services.agent99_public_receipts import ( + AGENT99_BACKUP_REQUIRED_VERIFIER_EVIDENCE_REFS, + AGENT99_REQUIRED_VERIFIER_EVIDENCE_REFS, extract_agent99_outcome_evidence_refs, sanitize_agent99_public_receipt_refs, ) @@ -749,8 +751,19 @@ async def _ensure_dr_scorecard( ) -> str | None: if "backup_health" not in identity.route_id: return "not_required" + required_refs = ( + AGENT99_REQUIRED_VERIFIER_EVIDENCE_REFS + | AGENT99_BACKUP_REQUIRED_VERIFIER_EVIDENCE_REFS + ) + safe_refs = sanitize_agent99_public_receipt_refs( + evidence_refs, + allowed_keys=required_refs, + ) + if not required_refs.issubset(safe_refs): + return None try: - entry = await get_knowledge_service().create_entry( + knowledge = get_knowledge_service() + entry = await knowledge.create_entry( KnowledgeEntryCreate( title=f"DR scorecard {identity.incident_id}", content=( @@ -758,7 +771,7 @@ async def _ensure_dr_scorecard( "restore-drill, and source-resolution receipts. " + "; ".join( f"{key}={value}" - for key, value in sorted(evidence_refs.items()) + for key, value in sorted(safe_refs.items()) ) ), entry_type=EntryType.BEST_PRACTICE, @@ -777,8 +790,17 @@ async def _ensure_dr_scorecard( created_by="agent99_controlled_dispatch_reconciler", ), project_id=identity.project_id, + schedule_embedding=False, ) - return f"knowledge_entries:{entry.id}:dr_scorecard" + embedded = await knowledge.ensure_entry_embedding( + entry.id, + entry.title, + entry.content, + project_id=identity.project_id, + ) + if not embedded: + return None + return f"knowledge_entries:{entry.id}:dr_scorecard_embedding_verified" except Exception as exc: logger.warning( "agent99_dr_scorecard_writeback_failed", diff --git a/apps/api/tests/test_agent99_controlled_dispatch_reconciler_job.py b/apps/api/tests/test_agent99_controlled_dispatch_reconciler_job.py index cca55b282..1ab7961a1 100644 --- a/apps/api/tests/test_agent99_controlled_dispatch_reconciler_job.py +++ b/apps/api/tests/test_agent99_controlled_dispatch_reconciler_job.py @@ -189,6 +189,66 @@ async def test_backup_mcp_ack_requires_dr_scorecard_binding() -> None: assert receipt is None +@pytest.mark.asyncio +async def test_dr_scorecard_ack_requires_complete_safe_refs_and_rag_embedding( + monkeypatch, +) -> None: + identity = build_agent99_dispatch_identity( + project_id="awoooi", + incident_id="INC-20260719-DR-RAG", + source_fingerprint="backup-dr-rag-fingerprint", + route_id="agent99:backup_health:BackupCheck", + ) + created = [] + + class Knowledge: + async def create_entry(self, request, **kwargs): # type: ignore[no-untyped-def] + created.append((request, kwargs)) + return SimpleNamespace( + id="00000000-0000-0000-0000-0000000000dr", + title=request.title, + content=request.content, + ) + + async def ensure_entry_embedding( + self, *_args, **_kwargs + ): # type: ignore[no-untyped-def] + return True + + monkeypatch.setattr(job, "get_knowledge_service", lambda: Knowledge()) + complete_refs = { + "agent99_outcome_receipt_id": "agent99:outcome:1", + "post_verifier_evidence_ref": "verifier:1", + "source_event_evidence_ref": "source:1", + "backup_status_evidence_ref": "backup:1", + "freshness_evidence_ref": "freshness:1", + "offsite_verify_evidence_ref": "offsite:1", + "escrow_evidence_ref": "escrow:1", + "restore_drill_evidence_ref": "restore:1", + "source_resolution_receipt_ref": "resolution:1", + } + + missing = await job._ensure_dr_scorecard( + identity, + evidence_refs={"backup_status_evidence_ref": "backup:1"}, + ) + receipt = await job._ensure_dr_scorecard( + identity, + evidence_refs={**complete_refs, "unsafe": "secret=value"}, + ) + + assert missing is None + assert receipt == ( + "knowledge_entries:00000000-0000-0000-0000-0000000000dr:" + "dr_scorecard_embedding_verified" + ) + assert len(created) == 1 + request, kwargs = created[0] + assert kwargs["schedule_embedding"] is False + assert "unsafe" not in request.content + assert all(value in request.content for value in complete_refs.values()) + + @pytest.mark.asyncio async def test_backup_telegram_ack_is_no_write_lifecycle_not_apply( monkeypatch,