fix(automation): enforce durable alert closure
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m32s
CD Pipeline / build-and-deploy (push) Successful in 8m2s
CD Pipeline / post-deploy-checks (push) Successful in 2m22s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m32s
CD Pipeline / build-and-deploy (push) Successful in 8m2s
CD Pipeline / post-deploy-checks (push) Successful in 2m22s
Restore D037 durable incident readback without weakening database failure semantics. Fence Agent99 dispatch and terminal learning to canonical identities, durable leases, verifier receipts, and reconciler-owned checkpoints. Drain backup and restore legacy receipts in bounded cohorts with strict transport, claim, projection, and no-false-green controls. Keep SSH service refusal visible while separating it from broker network-policy reachability.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from src.services.agent99_controlled_dispatch_ledger import (
|
||||
PostgresAgent99DispatchLedger,
|
||||
build_agent99_dispatch_identity,
|
||||
build_agent99_dispatch_receipt_envelope,
|
||||
parse_agent99_dispatch_identity,
|
||||
)
|
||||
|
||||
|
||||
def test_dispatch_identity_parser_rejects_cross_run_projection() -> None:
|
||||
identity = build_agent99_dispatch_identity(
|
||||
project_id="awoooi",
|
||||
incident_id="INC-20260711-11C751",
|
||||
source_fingerprint="fp-cold-start",
|
||||
route_id="agent99:host_recovery:Recover",
|
||||
)
|
||||
public = identity.public_dict()
|
||||
|
||||
assert parse_agent99_dispatch_identity(public) == identity
|
||||
public["trace_id"] = "00-00000000000000000000000000000000-0000000000000000-01"
|
||||
with pytest.raises(ValueError, match="identity_mismatch:trace_id"):
|
||||
parse_agent99_dispatch_identity(public)
|
||||
|
||||
|
||||
def test_accepted_without_inbox_trigger_is_delivery_unknown_not_authorized() -> None:
|
||||
identity = build_agent99_dispatch_identity(
|
||||
project_id="awoooi",
|
||||
incident_id="INC-20260711-11C751",
|
||||
source_fingerprint="fp-cold-start",
|
||||
route_id="agent99:host_recovery:Recover",
|
||||
)
|
||||
|
||||
envelope = build_agent99_dispatch_receipt_envelope(
|
||||
identity=identity,
|
||||
dispatch_receipt={
|
||||
"status": "accepted_inbox_pending",
|
||||
"accepted": True,
|
||||
"inbox_triggered": False,
|
||||
"delivery_certainty": "unknown",
|
||||
},
|
||||
controlled_apply_authorized=True,
|
||||
)
|
||||
|
||||
assert envelope["status"] == "dispatch_delivery_unknown_reconcile_only"
|
||||
assert envelope["dispatch_promoted"] is False
|
||||
assert envelope["controlled_apply_authorized"] is False
|
||||
assert envelope["retry_policy"]["reconcile_only"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backup_verifier_requires_all_six_dr_evidence_refs() -> None:
|
||||
identity = build_agent99_dispatch_identity(
|
||||
project_id="awoooi",
|
||||
incident_id="INC-20260711-BACKUP",
|
||||
source_fingerprint="fp-backup",
|
||||
route_id="agent99:backup_health:BackupCheck",
|
||||
)
|
||||
ledger = PostgresAgent99DispatchLedger()
|
||||
|
||||
result = await ledger.record_verifier(
|
||||
identity=identity,
|
||||
outcome_receipt={
|
||||
"identity": identity.public_dict(),
|
||||
"automationRunId": str(identity.run_id),
|
||||
"traceId": identity.trace_id,
|
||||
"workItemId": identity.work_item_id,
|
||||
"mode": "BackupCheck",
|
||||
"outcome": {
|
||||
"identity": identity.public_dict(),
|
||||
"schemaVersion": "agent99_outcome_contract_v1",
|
||||
"state": "resolved",
|
||||
"transportOk": True,
|
||||
"verifierPassed": True,
|
||||
"sourceEventResolved": True,
|
||||
},
|
||||
},
|
||||
evidence_refs={
|
||||
"agent99_outcome_receipt_id": "agent99:outcome:1",
|
||||
"post_verifier_evidence_ref": "agent99:BackupCheck:verified",
|
||||
"source_event_evidence_ref": "incident:source-resolved",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["status"] == "verifier_evidence_missing_fail_closed"
|
||||
assert set(result["missing_evidence_refs"]) == {
|
||||
"backup_status_evidence_ref",
|
||||
"freshness_evidence_ref",
|
||||
"offsite_verify_evidence_ref",
|
||||
"escrow_evidence_ref",
|
||||
"restore_drill_evidence_ref",
|
||||
"source_resolution_receipt_ref",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_backup_learning_requires_dr_scorecard_ack() -> None:
|
||||
identity = build_agent99_dispatch_identity(
|
||||
project_id="awoooi",
|
||||
incident_id="INC-20260711-BACKUP",
|
||||
source_fingerprint="fp-backup",
|
||||
route_id="agent99:backup_health:BackupCheck",
|
||||
)
|
||||
ledger = PostgresAgent99DispatchLedger()
|
||||
|
||||
result = await ledger.record_learning_writeback(
|
||||
identity=identity,
|
||||
receipt_refs={
|
||||
"incident_closure_receipt_id": "incident:1",
|
||||
"telegram_lifecycle_receipt_id": "telegram:1",
|
||||
"km_writeback_ack_id": "km:1",
|
||||
"playbook_trust_writeback_ack_id": "playbook:1",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["status"] == "learning_writeback_incomplete_fail_closed"
|
||||
assert result["missing_receipts"] == ["dr_scorecard_writeback_ack_id"]
|
||||
Reference in New Issue
Block a user