feat(records): govern lifecycle recovery and transfer evidence

This commit is contained in:
2026-08-06 05:36:18 +02:00
parent f316523a1d
commit df51bb1787
22 changed files with 4954 additions and 66 deletions
+124
View File
@@ -148,6 +148,99 @@ class RecordItemCreateRequest(StrictModel):
metadata: dict[str, Any] = Field(default_factory=dict)
class RecordLifecycleActionRequest(StrictModel):
expected_revision: int = Field(ge=1)
purpose: str = Field(min_length=1, max_length=255)
reason: str = Field(min_length=1, max_length=2_000)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordCloseRequest(RecordLifecycleActionRequest):
retention_trigger_at: datetime | None = None
restart_retention: bool = False
class RecordAppraisalRequest(RecordLifecycleActionRequest):
outcome: Literal["retain", "transfer", "destroy", "reclassify"]
policy_refs: list[str] = Field(default_factory=list, max_length=100)
override_retention_not_due: bool = False
class RecordHoldCreateRequest(StrictModel):
hold_id: str | None = Field(default=None, max_length=255)
expected_record_revision: int = Field(ge=1)
reason: str = Field(min_length=1, max_length=10_000)
authority: str = Field(min_length=1, max_length=500)
purpose: str = Field(min_length=1, max_length=255)
scope: dict[str, Any] = Field(default_factory=dict)
effective_from: datetime | None = None
effective_to: datetime | None = None
policy_refs: list[str] = Field(default_factory=list, max_length=100)
institutional_context: dict[str, Any] = Field(default_factory=dict)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
@model_validator(mode="after")
def validate_interval(self):
if (
self.effective_from
and self.effective_to
and self.effective_to <= self.effective_from
):
raise ValueError("effective_to must be after effective_from")
return self
class RecordHoldReleaseRequest(StrictModel):
expected_hold_revision: int = Field(ge=1)
reason: str = Field(min_length=1, max_length=2_000)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordDispositionCreateRequest(StrictModel):
disposition_id: str | None = Field(default=None, max_length=255)
expected_record_revision: int = Field(ge=1)
action: Literal["retain", "transfer", "destroy", "reclassify"]
reason: str = Field(min_length=1, max_length=10_000)
purpose: str = Field(min_length=1, max_length=255)
policy_refs: list[str] = Field(default_factory=list, max_length=100)
institutional_context: dict[str, Any] = Field(default_factory=dict)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordDispositionFinalizeRequest(StrictModel):
expected_disposition_revision: int = Field(ge=1)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordDispositionWithdrawRequest(RecordDispositionFinalizeRequest):
reason: str = Field(min_length=1, max_length=2_000)
class RecordTransferPackageCreateRequest(StrictModel):
package_id: str | None = Field(default=None, max_length=255)
disposition_id: str = Field(min_length=1, max_length=255)
expected_record_revision: int = Field(ge=1)
provider_id: str = Field(min_length=1, max_length=100)
profile: str = Field(min_length=1, max_length=255)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordTransferDispatchRequest(StrictModel):
expected_package_revision: int = Field(ge=1)
purpose: str = Field(min_length=1, max_length=255)
recorded_at: datetime
idempotency_key: str = Field(min_length=1, max_length=255)
class RecordListResponse(StrictModel):
records: list[dict[str, Any]]
total: int
@@ -165,6 +258,9 @@ class RecordDetailResponse(StrictModel):
volumes: list[dict[str, Any]]
items: list[dict[str, Any]]
chronology: list[dict[str, Any]]
holds: list[dict[str, Any]]
dispositions: list[dict[str, Any]]
transfer_packages: list[dict[str, Any]]
access_explanation: dict[str, Any]
@@ -172,15 +268,43 @@ class RecordSourceProviderResponse(StrictModel):
providers: list[dict[str, Any]]
class RecordArchiveProviderResponse(StrictModel):
providers: list[dict[str, Any]]
class RecordRecoveryStatusResponse(StrictModel):
record_id: str
record_revision: int
evidence_sha256: str
healthy: bool
source_checks: list[dict[str, Any]]
package_checks: list[dict[str, Any]]
recovery_operations: list[dict[str, Any]]
failure_count: int
limitations: list[str]
__all__ = [
"FilePlanNodeWriteRequest",
"RecordAppraisalRequest",
"RecordArchiveProviderResponse",
"RecordCatalogResponse",
"RecordClassWriteRequest",
"RecordCloseRequest",
"RecordCreateRequest",
"RecordDetailResponse",
"RecordDispositionCreateRequest",
"RecordDispositionFinalizeRequest",
"RecordDispositionWithdrawRequest",
"RecordHoldCreateRequest",
"RecordHoldReleaseRequest",
"RecordItemCreateRequest",
"RecordLifecycleActionRequest",
"RecordListResponse",
"RecordRecoveryStatusResponse",
"RecordSourceProviderResponse",
"RecordTransferDispatchRequest",
"RecordTransferPackageCreateRequest",
"RecordUpdateRequest",
"RecordVolumeCreateRequest",
]