Files
X-Financial/server/tests/test_agent_asset_release_telemetry.py
caoxiaozhu 787bc3a481 feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
2026-07-17 14:14:08 +08:00

872 lines
28 KiB
Python

from __future__ import annotations
from collections.abc import Generator
import pytest
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool
from app.core.agent_enums import AgentAssetDomain, AgentAssetStatus, AgentAssetType
from app.db.base import Base
from app.models.agent_asset import AgentAsset
from app.models.agent_asset_release_telemetry import (
AgentAssetReleaseAuditSample,
AgentAssetReleaseLabel,
AgentAssetReleaseObservation,
)
from app.models.risk_disposition import RiskDisposition, RiskDispositionEvent
from app.models.risk_observation import RiskObservation
from app.services.agent_asset_release_disposition_labels import (
AgentAssetReleaseDispositionLabelService,
)
from app.services.agent_asset_release_review import AgentAssetReleaseReviewService
from app.services.agent_asset_release_telemetry import (
AgentAssetReleaseTelemetryService,
ReleaseObservationInput,
ReleaseTelemetryCollecting,
ReleaseTelemetryIdempotencyConflict,
ReleaseTelemetryStaleRelease,
)
@pytest.fixture
def db() -> Generator[Session, None, None]:
engine = create_engine(
"sqlite+pysqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
Base.metadata.create_all(
engine,
tables=[
AgentAsset.__table__,
RiskObservation.__table__,
RiskDisposition.__table__,
RiskDispositionEvent.__table__,
AgentAssetReleaseObservation.__table__,
AgentAssetReleaseLabel.__table__,
AgentAssetReleaseAuditSample.__table__,
],
)
factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
with factory() as session:
yield session
engine.dispose()
def _seed_asset(
db: Session,
*,
stage: str = "shadow",
tenant_id: str = "tenant-a",
release_id: str = "release-1",
reviewer_quorum: int = 1,
recall_gate_enabled: bool = False,
negative_sample_percent: int = 20,
negative_min_reviewed: int = 5,
min_recall: float = 0.95,
) -> AgentAsset:
asset = AgentAsset(
id="release-asset",
asset_type=AgentAssetType.RULE.value,
code="risk.release.telemetry",
name="发布遥测规则",
description="",
domain=AgentAssetDomain.EXPENSE.value,
scenario_json=["travel"],
owner="finance",
status=AgentAssetStatus.ACTIVE.value,
current_version="v1",
published_version="v1",
working_version="v2",
config_json={
"tenant_id": tenant_id,
"detail_mode": "json_risk",
"release_guard": {
"release_id": release_id,
"stage": stage,
"candidate_version": "v2",
"previous_version": "v1",
"started_by": "publisher",
"policy": {
"reviewer_quorum": reviewer_quorum,
"recall_gate_enabled": recall_gate_enabled,
"negative_sample_percent": negative_sample_percent,
"negative_min_reviewed": negative_min_reviewed,
"min_recall": min_recall,
"recall_confidence_level": 0.95,
},
},
},
)
db.add(asset)
db.commit()
return asset
def _shadow_result(*, candidate_hit: bool, baseline_hit: bool) -> dict:
flags = []
if baseline_hit:
flags.append(
{
"rule_code": "risk.release.telemetry",
"rule_version": "v1",
"release_stage": "shadow",
"release_mode": "enforced",
}
)
return {
"flags": flags,
"shadow_evaluations": [
{
"asset_id": "release-asset",
"rule_code": "risk.release.telemetry",
"rule_version": "v2",
"release_stage": "shadow",
"hit": candidate_hit,
"severity": "high" if candidate_hit else "none",
}
],
}
def _record_shadow(
db: Session,
*,
claim_id: str,
candidate_hit: bool = True,
baseline_hit: bool = True,
) -> AgentAssetReleaseObservation:
return AgentAssetReleaseTelemetryService(db).record_expense_risk_result(
tenant_id="tenant-a",
claim_id=claim_id,
result=_shadow_result(
candidate_hit=candidate_hit,
baseline_hit=baseline_hit,
),
)[0]
def test_shadow_runtime_samples_and_labels_build_conservative_release_input(
db: Session,
) -> None:
_seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
candidate = _record_shadow(db, claim_id="claim-secret-001")
baseline_only = _record_shadow(
db,
claim_id="claim-secret-002",
candidate_hit=False,
baseline_hit=True,
)
replay = _record_shadow(db, claim_id="claim-secret-001")
db.commit()
assert replay.id == candidate.id
assert candidate.candidate_hit is True
assert candidate.baseline_hit is True
assert baseline_only.candidate_hit is False
stored_text = str(
{
column.name: getattr(candidate, column.name)
for column in AgentAssetReleaseObservation.__table__.columns
}
)
assert "claim-secret" not in stored_text
collecting = service.aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
assert collecting.status == "collecting"
assert collecting.precision is None
assert collecting.recall is None
assert collecting.false_negative_count is None
assert collecting.negative_ground_truth_status == "collecting_candidate_labels"
with pytest.raises(ReleaseTelemetryCollecting):
collecting.to_release_evaluation_input()
candidate_label = service.record_review_label(
tenant_id="tenant-a",
observation_id=candidate.id,
label="confirmed",
request_id="review-001",
actor_id="auditor-secret-account",
)
service.record_review_label(
tenant_id="tenant-a",
observation_id=baseline_only.id,
label="false_positive",
request_id="review-002",
actor_id="auditor-secret-account",
)
service.record_review_label(
tenant_id="tenant-a",
observation_id=baseline_only.id,
label="false_positive",
request_id="review-002-second",
actor_id="second-auditor-secret-account",
)
db.commit()
assert candidate_label.actor_fingerprint != "auditor-secret-account"
assert "auditor-secret-account" not in str(candidate_label.__dict__)
ready = service.aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
assert ready.status == "ready"
assert ready.observed_count == 2
assert ready.candidate_hit_count == 1
assert ready.candidate_labeled_count == 1
assert ready.precision == 1.0
assert ready.baseline_hit_count == 2
assert ready.baseline_labeled_count == 2
assert ready.baseline_precision == 0.5
evaluation = ready.to_release_evaluation_input()
assert evaluation.total == 2
assert evaluation.failure_count == 0
assert evaluation.precision == 1.0
assert evaluation.baseline_precision == 0.5
assert evaluation.details["metric_source"] == "release_runtime_telemetry"
assert evaluation.details["recall"] == 1.0
assert evaluation.details["recall_lower_bound"] == 1.0
def test_unlabeled_candidate_hit_never_counts_as_release_success(db: Session) -> None:
_seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
first = _record_shadow(db, claim_id="claim-1")
_record_shadow(db, claim_id="claim-2")
service.record_review_label(
tenant_id="tenant-a",
observation_id=first.id,
label="confirmed",
request_id="review-first",
actor_id="auditor",
)
db.commit()
aggregate = service.aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
assert aggregate.status == "collecting"
assert aggregate.candidate_hit_count == 2
assert aggregate.candidate_labeled_count == 1
assert aggregate.candidate_pending_label_count == 1
assert aggregate.baseline_precision is None
assert "candidate_labels_pending" in aggregate.reasons
with pytest.raises(ReleaseTelemetryCollecting):
aggregate.to_release_evaluation_input()
def test_observation_and_label_idempotency_reject_changed_payload(db: Session) -> None:
_seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
observation = _record_shadow(db, claim_id="claim-1")
with pytest.raises(ReleaseTelemetryIdempotencyConflict):
service.record_observation(
ReleaseObservationInput(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
rule_code="risk.release.telemetry",
source_key="claim-1",
candidate_hit=False,
baseline_hit=True,
)
)
service.record_review_label(
tenant_id="tenant-a",
observation_id=observation.id,
label="confirmed",
request_id="same-review-request",
actor_id="auditor",
)
with pytest.raises(ReleaseTelemetryIdempotencyConflict):
service.record_review_label(
tenant_id="tenant-a",
observation_id=observation.id,
label="false_positive",
request_id="same-review-request",
actor_id="auditor",
)
def test_release_label_values_cannot_cross_verification_sources(db: Session) -> None:
_seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
observation = _record_shadow(db, claim_id="claim-label-semantics")
with pytest.raises(ValueError, match="release reviews require confirmed"):
service.record_review_label(
tenant_id="tenant-a",
observation_id=observation.id,
label="risk_present", # type: ignore[arg-type]
request_id="wrong-release-review-semantics",
actor_id="auditor",
)
with pytest.raises(ValueError, match="Blind release review requires"):
service.record_blind_review_label(
tenant_id="tenant-a",
observation_id=observation.id,
ground_truth="confirmed", # type: ignore[arg-type]
request_id="wrong-blind-review-semantics",
actor_id="auditor",
)
def test_observation_rolls_back_when_blind_sample_cannot_be_protected(
db: Session,
monkeypatch: pytest.MonkeyPatch,
) -> None:
_seed_asset(db)
def fail_encryption(_value: str) -> str:
raise ValueError("sample encryption unavailable")
monkeypatch.setattr(
"app.services.agent_asset_release_sampling.encrypt_secret",
fail_encryption,
)
with pytest.raises(ValueError, match="sample encryption unavailable"):
_record_shadow(db, claim_id="claim-must-not-leak")
assert db.query(AgentAssetReleaseObservation).count() == 0
assert db.query(AgentAssetReleaseAuditSample).count() == 0
def test_cross_tenant_and_stale_release_labels_are_rejected(db: Session) -> None:
asset = _seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
observation = _record_shadow(db, claim_id="claim-1")
db.commit()
with pytest.raises(LookupError):
service.record_review_label(
tenant_id="tenant-b",
observation_id=observation.id,
label="confirmed",
request_id="foreign-review",
actor_id="foreign-auditor",
)
config = dict(asset.config_json or {})
state = dict(config["release_guard"])
state["release_id"] = "release-2"
config["release_guard"] = state
asset.config_json = config
db.add(asset)
db.commit()
with pytest.raises(ReleaseTelemetryStaleRelease):
service.record_review_label(
tenant_id="tenant-a",
observation_id=observation.id,
label="confirmed",
request_id="stale-review",
actor_id="auditor",
)
def test_typed_risk_disposition_event_is_a_trusted_release_label(db: Session) -> None:
_seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
telemetry = _record_shadow(db, claim_id="claim-typed")
risk_observation = RiskObservation(
id="risk-observation-1",
tenant_id="tenant-a",
observation_key="risk:claim-typed:platform:risk.release.telemetry",
subject_type="expense_claim",
subject_key="claim:claim-typed",
claim_id="claim-typed",
claim_no="REDACTED",
risk_type="policy",
risk_signal="risk.release.telemetry",
risk_score=82,
risk_level="high",
algorithm_version="v1",
decision_trace_json={"rule_code": "risk.release.telemetry"},
)
disposition = RiskDisposition(
id="disposition-1",
tenant_id="tenant-a",
observation_id=risk_observation.id,
adjudication="confirmed",
lifecycle_status="open",
version=1,
)
event = RiskDispositionEvent(
id="disposition-event-1",
tenant_id="tenant-a",
disposition_id=disposition.id,
observation_id=risk_observation.id,
version=1,
action="confirm",
actor_id="real-auditor-secret",
actor_name="真实审计员",
request_id="typed-action-1",
payload_fingerprint="f" * 64,
before_json={"adjudication": "unreviewed"},
after_json={"adjudication": "confirmed"},
)
db.add_all([risk_observation, disposition, event])
db.commit()
label = service.record_risk_disposition_label(
tenant_id="tenant-a",
observation_id=telemetry.id,
disposition_event_id=event.id,
)
db.commit()
assert label.label == "confirmed"
assert label.verification_source == "typed_risk_disposition"
assert "real-auditor-secret" not in str(label.__dict__)
aggregate = service.aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
assert aggregate.status == "ready"
assert aggregate.precision == 1.0
assert aggregate.baseline_precision == 1.0
def test_typed_disposition_resolves_current_release_sample_without_client_observation_id(
db: Session,
) -> None:
_seed_asset(db)
telemetry = _record_shadow(db, claim_id="claim-current-label")
risk_observation = RiskObservation(
id="risk-observation-current",
tenant_id="tenant-a",
observation_key="risk:claim-current-label:platform:risk.release.telemetry",
subject_type="expense_claim",
subject_key="claim:claim-current-label",
claim_id="claim-current-label",
claim_no="REDACTED",
risk_type="policy",
risk_signal="risk.release.telemetry",
risk_score=82,
risk_level="high",
control_stage="reimbursement",
algorithm_version="v1",
decision_trace_json={"rule_code": "risk.release.telemetry"},
)
disposition = RiskDisposition(
id="disposition-current",
tenant_id="tenant-a",
observation_id=risk_observation.id,
adjudication="false_positive",
lifecycle_status="open",
version=1,
)
event = RiskDispositionEvent(
id="disposition-event-current",
tenant_id="tenant-a",
disposition_id=disposition.id,
observation_id=risk_observation.id,
version=1,
action="false_positive",
actor_id="auditor-secret",
actor_name="审计员",
request_id="typed-current-action",
payload_fingerprint="e" * 64,
before_json={"adjudication": "unreviewed"},
after_json={"adjudication": "false_positive"},
)
db.add_all([risk_observation, disposition, event])
db.commit()
labels = AgentAssetReleaseDispositionLabelService(db).record_current_labels(
tenant_id="tenant-a",
disposition_event_id=event.id,
)
db.commit()
assert [item.observation_id for item in labels] == [telemetry.id]
assert labels[0].label == "false_positive"
assert "auditor-secret" not in str(labels[0].__dict__)
def test_current_disposition_does_not_label_stale_release_sample(db: Session) -> None:
asset = _seed_asset(db)
_record_shadow(db, claim_id="claim-stale-label")
risk_observation = RiskObservation(
id="risk-observation-stale-current",
tenant_id="tenant-a",
observation_key="risk:claim-stale-label:platform:risk.release.telemetry",
subject_type="expense_claim",
subject_key="claim:claim-stale-label",
claim_id="claim-stale-label",
claim_no="REDACTED",
risk_type="policy",
risk_signal="risk.release.telemetry",
risk_score=82,
risk_level="high",
control_stage="reimbursement",
algorithm_version="v1",
decision_trace_json={"rule_code": "risk.release.telemetry"},
)
disposition = RiskDisposition(
id="disposition-stale-current",
tenant_id="tenant-a",
observation_id=risk_observation.id,
adjudication="confirmed",
lifecycle_status="open",
version=1,
)
event = RiskDispositionEvent(
id="disposition-event-stale-current",
tenant_id="tenant-a",
disposition_id=disposition.id,
observation_id=risk_observation.id,
version=1,
action="confirm",
actor_id="auditor",
actor_name="审计员",
request_id="typed-stale-current-action",
payload_fingerprint="d" * 64,
before_json={"adjudication": "unreviewed"},
after_json={"adjudication": "confirmed"},
)
db.add_all([risk_observation, disposition, event])
db.commit()
config = dict(asset.config_json or {})
state = dict(config["release_guard"])
state["release_id"] = "release-2"
config["release_guard"] = state
asset.config_json = config
db.add(asset)
db.commit()
labels = AgentAssetReleaseDispositionLabelService(db).record_current_labels(
tenant_id="tenant-a",
disposition_event_id=event.id,
)
assert labels == []
def test_canary_hits_and_structured_runtime_failures_are_real_samples(db: Session) -> None:
_seed_asset(db, stage="canary")
service = AgentAssetReleaseTelemetryService(db)
records = service.record_expense_risk_result(
tenant_id="tenant-a",
claim_id="canary-claim-1",
result={
"shadow_evaluations": [],
"flags": [
{
"rule_code": "risk.release.telemetry",
"rule_version": "v2",
"release_stage": "canary",
"release_mode": "enforced",
}
],
},
)
miss = service.record_manifest_evaluation(
tenant_id="tenant-a",
claim_id="canary-claim-miss",
manifest={
"_rule_asset_id": "release-asset",
"_release_stage": "canary",
"_release_mode": "enforced",
"_rule_version": "v2",
"rule_code": "risk.release.telemetry",
},
hit=False,
)
failure = service.record_observation(
ReleaseObservationInput(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="canary",
version="v2",
rule_code="risk.release.telemetry",
source_key="canary-claim-2",
candidate_hit=False,
baseline_hit=None,
runtime_status="failed",
failure_code="evaluator_error",
)
)
service.record_review_label(
tenant_id="tenant-a",
observation_id=records[0].id,
label="false_positive",
request_id="canary-review",
actor_id="auditor",
)
with pytest.raises(ValueError, match="independent ground truth"):
service.record_review_label(
tenant_id="tenant-a",
observation_id=miss.id,
label="confirmed",
request_id="unsupported-false-negative-label",
actor_id="auditor",
)
db.commit()
aggregate = service.aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="canary",
version="v2",
)
evaluation = aggregate.to_release_evaluation_input()
assert records[0].candidate_hit is True
assert miss.candidate_hit is False
assert failure.runtime_status == "failed"
assert aggregate.status == "ready"
assert aggregate.runtime_failure_count == 1
assert aggregate.precision == 0.0
assert aggregate.baseline_precision is None
assert evaluation.total == 3
assert evaluation.failure_count == 1
def test_dedicated_review_queue_is_tenant_safe_and_separates_publisher(
db: Session,
) -> None:
_seed_asset(db)
observation = _record_shadow(db, claim_id="claim-review-queue")
service = AgentAssetReleaseReviewService(db)
with pytest.raises(LookupError):
service.list_pending(tenant_id="tenant-b", asset_id="release-asset")
queue = service.list_pending(tenant_id="tenant-a", asset_id="release-asset")
assert queue["pending_total"] == 1
assert queue["items"][0]["observation_id"] == observation.id
assert "source_fingerprint" not in queue["items"][0]
with pytest.raises(PermissionError, match="发布发起人"):
service.record_label(
tenant_id="tenant-a",
asset_id="release-asset",
observation_id=observation.id,
label="confirmed",
actor_id="publisher",
request_id="publisher-self-review",
)
label = service.record_label(
tenant_id="tenant-a",
asset_id="release-asset",
observation_id=observation.id,
label="confirmed",
actor_id="independent-reviewer",
request_id="independent-review",
)
db.commit()
assert label.label == "risk_present"
assert service.list_pending(tenant_id="tenant-a", asset_id="release-asset")[
"pending_total"
] == 0
def test_release_review_quorum_requires_distinct_reviewers(db: Session) -> None:
_seed_asset(db, reviewer_quorum=2)
observation = _record_shadow(db, claim_id="claim-two-reviewers")
service = AgentAssetReleaseReviewService(db)
service.record_label(
tenant_id="tenant-a",
asset_id="release-asset",
observation_id=observation.id,
label="confirmed",
actor_id="reviewer-one",
request_id="review-one",
)
db.commit()
first_queue = service.list_pending(tenant_id="tenant-a", asset_id="release-asset")
first_aggregate = AgentAssetReleaseTelemetryService(db).aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
assert first_queue["pending_total"] == 1
assert first_queue["items"][0]["reviewer_count"] == 1
assert first_queue["items"][0]["required_reviewers"] == 2
assert first_aggregate.status == "collecting"
service.record_label(
tenant_id="tenant-a",
asset_id="release-asset",
observation_id=observation.id,
label="confirmed",
actor_id="reviewer-two",
request_id="review-two",
)
db.commit()
second_aggregate = AgentAssetReleaseTelemetryService(db).aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
assert service.list_pending(tenant_id="tenant-a", asset_id="release-asset")[
"pending_total"
] == 0
assert second_aggregate.status == "ready"
assert second_aggregate.precision == 1.0
def test_blind_negative_audit_builds_real_false_negative_and_recall_evidence(
db: Session,
) -> None:
_seed_asset(
db,
recall_gate_enabled=True,
negative_sample_percent=100,
negative_min_reviewed=2,
min_recall=0.3,
)
positive = _record_shadow(
db,
claim_id="claim-blind-positive",
candidate_hit=True,
baseline_hit=False,
)
false_negative = _record_shadow(
db,
claim_id="claim-blind-missed-risk",
candidate_hit=False,
baseline_hit=False,
)
true_negative = _record_shadow(
db,
claim_id="claim-blind-no-risk",
candidate_hit=False,
baseline_hit=False,
)
db.commit()
review = AgentAssetReleaseReviewService(db)
queue = review.list_pending(tenant_id="tenant-a", asset_id="release-asset")
assert queue["pending_total"] == 3
assert {item["source_document_id"] for item in queue["items"]} == {
"claim-blind-positive",
"claim-blind-missed-risk",
"claim-blind-no-risk",
}
assert all(item["prediction_blinded"] is True for item in queue["items"])
assert all("candidate_hit" not in item for item in queue["items"])
assert all("baseline_hit" not in item for item in queue["items"])
quorum_by_source = {
item["source_document_id"]: item["required_reviewers"]
for item in queue["items"]
}
assert quorum_by_source["claim-blind-positive"] == 1
assert quorum_by_source["claim-blind-missed-risk"] == 2
assert quorum_by_source["claim-blind-no-risk"] == 2
stored_samples = list(db.scalars(select(AgentAssetReleaseAuditSample)).all())
assert len(stored_samples) == 3
assert "claim-blind" not in str(
[item.source_reference_encrypted for item in stored_samples]
)
for observation, label in (
(positive, "confirmed"),
(false_negative, "confirmed"),
(true_negative, "false_positive"),
):
review.record_label(
tenant_id="tenant-a",
asset_id="release-asset",
observation_id=observation.id,
label=label,
actor_id="independent-auditor",
request_id=f"blind-review:{observation.id}",
)
if not observation.candidate_hit:
review.record_label(
tenant_id="tenant-a",
asset_id="release-asset",
observation_id=observation.id,
label=label,
actor_id="second-independent-auditor",
request_id=f"blind-review-second:{observation.id}",
)
db.commit()
aggregate = AgentAssetReleaseTelemetryService(db).aggregate(
tenant_id="tenant-a",
asset_id="release-asset",
release_id="release-1",
stage="shadow",
version="v2",
)
evaluation = aggregate.to_release_evaluation_input()
assert aggregate.status == "ready"
assert aggregate.false_negative_count == 1
assert aggregate.random_negative_population_count == 2
assert aggregate.random_negative_labeled_count == 2
assert aggregate.recall == 0.5
assert aggregate.recall_lower_bound is not None
assert aggregate.recall_lower_bound < aggregate.recall
assert (
aggregate.negative_ground_truth_status
== "available_stratified_random_audit"
)
assert evaluation.details is not None
assert evaluation.details["recall"] == 0.5
assert evaluation.details["false_negative_count"] == 1
def test_telemetry_rows_are_append_only(db: Session) -> None:
_seed_asset(db)
service = AgentAssetReleaseTelemetryService(db)
observation = _record_shadow(db, claim_id="claim-immutable")
label = service.record_review_label(
tenant_id="tenant-a",
observation_id=observation.id,
label="confirmed",
request_id="immutable-review",
actor_id="auditor",
)
db.commit()
observation.candidate_hit = False
with pytest.raises(ValueError, match="append-only"):
db.flush()
db.rollback()
persisted_label = db.scalar(
select(AgentAssetReleaseLabel).where(AgentAssetReleaseLabel.id == label.id)
)
assert persisted_label is not None
db.delete(persisted_label)
with pytest.raises(ValueError, match="append-only"):
db.flush()