- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
697 lines
28 KiB
Python
697 lines
28 KiB
Python
# #region CleanReleaseModels [C:3] [TYPE Module] [SEMANTICS clean-release, models, lifecycle, compliance, evidence, immutability]
|
|
# @BRIEF Define canonical clean release domain entities and lifecycle guards.
|
|
# @LAYER: Domain
|
|
# @RELATION DEPENDS_ON -> MappingModels
|
|
# @PRE: Base mapping model and release enums are available.
|
|
# @POST: Provides SQLAlchemy and dataclass definitions for governance domain.
|
|
# @SIDE_EFFECT: None (schema definition).
|
|
# @DATA_CONTRACT: Model[ReleaseCandidate, CandidateArtifact, DistributionManifest, ComplianceRun, ComplianceReport]
|
|
# @INVARIANT: Immutable snapshots are never mutated; forbidden lifecycle transitions are rejected.
|
|
|
|
from datetime import datetime
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
from typing import List, Optional, Dict, Any
|
|
from pydantic import ConfigDict, Field, model_validator
|
|
from pydantic.dataclasses import dataclass as pydantic_dataclass
|
|
from sqlalchemy import Column, String, DateTime, JSON, ForeignKey, Integer, Boolean
|
|
from sqlalchemy.orm import relationship
|
|
from .mapping import Base
|
|
from ..services.clean_release.enums import (
|
|
CandidateStatus, RunStatus, ComplianceDecision,
|
|
ApprovalDecisionType, PublicationStatus, ClassificationType
|
|
)
|
|
from ..services.clean_release.exceptions import IllegalTransitionError
|
|
|
|
# #region ExecutionMode [TYPE Class]
|
|
# @BRIEF Backward-compatible execution mode enum for legacy TUI/orchestrator tests.
|
|
class ExecutionMode(str, Enum):
|
|
TUI = "TUI"
|
|
API = "API"
|
|
SCHEDULER = "SCHEDULER"
|
|
# #endregion ExecutionMode
|
|
|
|
# #region CheckFinalStatus [TYPE Class]
|
|
# @BRIEF Backward-compatible final status enum for legacy TUI/orchestrator tests.
|
|
class CheckFinalStatus(str, Enum):
|
|
COMPLIANT = "COMPLIANT"
|
|
BLOCKED = "BLOCKED"
|
|
FAILED = "FAILED"
|
|
RUNNING = "RUNNING"
|
|
# #endregion CheckFinalStatus
|
|
|
|
# #region CheckStageName [TYPE Class]
|
|
# @BRIEF Backward-compatible stage name enum for legacy TUI/orchestrator tests.
|
|
class CheckStageName(str, Enum):
|
|
DATA_PURITY = "DATA_PURITY"
|
|
INTERNAL_SOURCES_ONLY = "INTERNAL_SOURCES_ONLY"
|
|
NO_EXTERNAL_ENDPOINTS = "NO_EXTERNAL_ENDPOINTS"
|
|
MANIFEST_CONSISTENCY = "MANIFEST_CONSISTENCY"
|
|
# #endregion CheckStageName
|
|
|
|
# #region CheckStageStatus [TYPE Class]
|
|
# @BRIEF Backward-compatible stage status enum for legacy TUI/orchestrator tests.
|
|
class CheckStageStatus(str, Enum):
|
|
PASS = "PASS"
|
|
FAIL = "FAIL"
|
|
SKIPPED = "SKIPPED"
|
|
RUNNING = "RUNNING"
|
|
# #endregion CheckStageStatus
|
|
|
|
# #region CheckStageResult [TYPE Class]
|
|
# @BRIEF Backward-compatible stage result container for legacy TUI/orchestrator tests.
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class CheckStageResult:
|
|
stage: CheckStageName
|
|
status: CheckStageStatus
|
|
details: str = ""
|
|
# #endregion CheckStageResult
|
|
|
|
# #region ProfileType [TYPE Class]
|
|
# @BRIEF Backward-compatible profile enum for legacy TUI bootstrap logic.
|
|
class ProfileType(str, Enum):
|
|
ENTERPRISE_CLEAN = "enterprise-clean"
|
|
# #endregion ProfileType
|
|
|
|
# #region RegistryStatus [TYPE Class]
|
|
# @BRIEF Backward-compatible registry status enum for legacy TUI bootstrap logic.
|
|
class RegistryStatus(str, Enum):
|
|
ACTIVE = "ACTIVE"
|
|
INACTIVE = "INACTIVE"
|
|
# #endregion RegistryStatus
|
|
|
|
# #region ReleaseCandidateStatus [TYPE Class]
|
|
# @BRIEF Backward-compatible release candidate status enum for legacy TUI.
|
|
class ReleaseCandidateStatus(str, Enum):
|
|
DRAFT = CandidateStatus.DRAFT.value
|
|
PREPARED = CandidateStatus.PREPARED.value
|
|
MANIFEST_BUILT = CandidateStatus.MANIFEST_BUILT.value
|
|
CHECK_PENDING = CandidateStatus.CHECK_PENDING.value
|
|
CHECK_RUNNING = CandidateStatus.CHECK_RUNNING.value
|
|
CHECK_PASSED = CandidateStatus.CHECK_PASSED.value
|
|
CHECK_BLOCKED = CandidateStatus.CHECK_BLOCKED.value
|
|
BLOCKED = CandidateStatus.CHECK_BLOCKED.value
|
|
CHECK_ERROR = CandidateStatus.CHECK_ERROR.value
|
|
APPROVED = CandidateStatus.APPROVED.value
|
|
PUBLISHED = CandidateStatus.PUBLISHED.value
|
|
REVOKED = CandidateStatus.REVOKED.value
|
|
# #endregion ReleaseCandidateStatus
|
|
|
|
# #region ResourceSourceEntry [TYPE Class]
|
|
# @BRIEF Backward-compatible source entry model for legacy TUI bootstrap logic.
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class ResourceSourceEntry:
|
|
source_id: str
|
|
host: str
|
|
protocol: str
|
|
purpose: str
|
|
enabled: bool = True
|
|
# #endregion ResourceSourceEntry
|
|
|
|
# #region ResourceSourceRegistry [TYPE Class]
|
|
# @BRIEF Backward-compatible source registry model for legacy TUI bootstrap logic.
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class ResourceSourceRegistry:
|
|
registry_id: str
|
|
name: str
|
|
entries: List[ResourceSourceEntry]
|
|
updated_at: datetime
|
|
updated_by: str
|
|
status: str = "ACTIVE"
|
|
immutable: bool = True
|
|
allowed_hosts: Optional[List[str]] = None
|
|
allowed_schemes: Optional[List[str]] = None
|
|
allowed_source_types: Optional[List[str]] = None
|
|
|
|
@model_validator(mode="after")
|
|
def populate_legacy_allowlists(self):
|
|
enabled_entries = [entry for entry in self.entries if getattr(entry, "enabled", True)]
|
|
if self.allowed_hosts is None:
|
|
self.allowed_hosts = [entry.host for entry in enabled_entries]
|
|
if self.allowed_schemes is None:
|
|
self.allowed_schemes = [entry.protocol for entry in enabled_entries]
|
|
if self.allowed_source_types is None:
|
|
self.allowed_source_types = [entry.purpose for entry in enabled_entries]
|
|
return self
|
|
|
|
@property
|
|
def id(self) -> str:
|
|
return self.registry_id
|
|
# #endregion ResourceSourceRegistry
|
|
|
|
# #region CleanProfilePolicy [TYPE Class]
|
|
# @BRIEF Backward-compatible policy model for legacy TUI bootstrap logic.
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class CleanProfilePolicy:
|
|
policy_id: str
|
|
policy_version: str
|
|
profile: ProfileType
|
|
active: bool
|
|
internal_source_registry_ref: str
|
|
prohibited_artifact_categories: List[str]
|
|
effective_from: datetime
|
|
required_system_categories: Optional[List[str]] = None
|
|
external_source_forbidden: bool = True
|
|
immutable: bool = True
|
|
content_json: Optional[Dict[str, Any]] = None
|
|
|
|
@model_validator(mode="after")
|
|
def validate_enterprise_policy(self):
|
|
if self.profile == ProfileType.ENTERPRISE_CLEAN:
|
|
if not self.prohibited_artifact_categories:
|
|
raise ValueError("enterprise-clean policy requires prohibited_artifact_categories")
|
|
if self.external_source_forbidden is not True:
|
|
raise ValueError("enterprise-clean policy requires external_source_forbidden=true")
|
|
if self.content_json is None:
|
|
self.content_json = {
|
|
"profile": self.profile.value,
|
|
"prohibited_artifact_categories": list(self.prohibited_artifact_categories or []),
|
|
"required_system_categories": list(self.required_system_categories or []),
|
|
"external_source_forbidden": self.external_source_forbidden,
|
|
}
|
|
return self
|
|
|
|
@property
|
|
def id(self) -> str:
|
|
return self.policy_id
|
|
|
|
@property
|
|
def registry_snapshot_id(self) -> str:
|
|
return self.internal_source_registry_ref
|
|
# #endregion CleanProfilePolicy
|
|
|
|
# #region ComplianceCheckRun [TYPE Class]
|
|
# @BRIEF Backward-compatible run model for legacy TUI typing/import compatibility.
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class ComplianceCheckRun:
|
|
check_run_id: str
|
|
candidate_id: str
|
|
policy_id: str
|
|
started_at: datetime
|
|
triggered_by: str
|
|
execution_mode: ExecutionMode
|
|
final_status: CheckFinalStatus
|
|
checks: List[CheckStageResult]
|
|
finished_at: Optional[datetime] = None
|
|
|
|
@model_validator(mode="after")
|
|
def validate_final_status_alignment(self):
|
|
mandatory_stages = {
|
|
CheckStageName.DATA_PURITY,
|
|
CheckStageName.INTERNAL_SOURCES_ONLY,
|
|
CheckStageName.NO_EXTERNAL_ENDPOINTS,
|
|
CheckStageName.MANIFEST_CONSISTENCY,
|
|
}
|
|
if self.final_status == CheckFinalStatus.COMPLIANT:
|
|
observed_stages = {check.stage for check in self.checks}
|
|
if observed_stages != mandatory_stages:
|
|
raise ValueError("compliant run requires all mandatory stages")
|
|
if any(check.status != CheckStageStatus.PASS for check in self.checks):
|
|
raise ValueError("compliant run requires PASS on all mandatory stages")
|
|
return self
|
|
|
|
@property
|
|
def id(self) -> str:
|
|
return self.check_run_id
|
|
|
|
@property
|
|
def run_id(self) -> str:
|
|
return self.check_run_id
|
|
|
|
@property
|
|
def status(self) -> RunStatus:
|
|
if self.final_status == CheckFinalStatus.RUNNING:
|
|
return RunStatus.RUNNING
|
|
if self.final_status == CheckFinalStatus.BLOCKED:
|
|
return RunStatus.FAILED
|
|
return RunStatus.SUCCEEDED
|
|
# #endregion ComplianceCheckRun
|
|
|
|
# #region ReleaseCandidate [TYPE Class]
|
|
# @BRIEF Represents the release unit being prepared and governed.
|
|
# @PRE: id, version, source_snapshot_ref are non-empty.
|
|
# @POST: status advances only through legal transitions.
|
|
class ReleaseCandidate(Base):
|
|
__tablename__ = "clean_release_candidates"
|
|
|
|
id = Column(String, primary_key=True)
|
|
name = Column(String, nullable=True) # Added back for backward compatibility with some legacy DTOs
|
|
version = Column(String, nullable=False)
|
|
source_snapshot_ref = Column(String, nullable=False)
|
|
build_id = Column(String, nullable=True)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
created_by = Column(String, nullable=False)
|
|
status = Column(String, default=CandidateStatus.DRAFT)
|
|
|
|
def __init__(self, **kwargs):
|
|
if "candidate_id" in kwargs:
|
|
kwargs["id"] = kwargs.pop("candidate_id")
|
|
if "profile" in kwargs:
|
|
kwargs.pop("profile")
|
|
status = kwargs.get("status")
|
|
if status is None:
|
|
kwargs["status"] = CandidateStatus.DRAFT.value
|
|
elif isinstance(status, ReleaseCandidateStatus):
|
|
kwargs["status"] = status.value
|
|
elif isinstance(status, CandidateStatus):
|
|
kwargs["status"] = status.value
|
|
if not str(kwargs.get("id", "")).strip():
|
|
raise ValueError("candidate_id must be non-empty")
|
|
super().__init__(**kwargs)
|
|
|
|
@property
|
|
def candidate_id(self) -> str:
|
|
return self.id
|
|
|
|
def transition_to(self, new_status: CandidateStatus):
|
|
"""
|
|
@PURPOSE: Enforce legal state transitions.
|
|
@PRE: Transition must be allowed by lifecycle rules.
|
|
"""
|
|
allowed = {
|
|
CandidateStatus.DRAFT: [CandidateStatus.PREPARED],
|
|
CandidateStatus.PREPARED: [CandidateStatus.MANIFEST_BUILT],
|
|
CandidateStatus.MANIFEST_BUILT: [CandidateStatus.CHECK_PENDING],
|
|
CandidateStatus.CHECK_PENDING: [CandidateStatus.CHECK_RUNNING],
|
|
CandidateStatus.CHECK_RUNNING: [
|
|
CandidateStatus.CHECK_PASSED,
|
|
CandidateStatus.CHECK_BLOCKED,
|
|
CandidateStatus.CHECK_ERROR
|
|
],
|
|
CandidateStatus.CHECK_PASSED: [CandidateStatus.APPROVED, CandidateStatus.CHECK_PENDING],
|
|
CandidateStatus.CHECK_BLOCKED: [CandidateStatus.CHECK_PENDING],
|
|
CandidateStatus.CHECK_ERROR: [CandidateStatus.CHECK_PENDING],
|
|
CandidateStatus.APPROVED: [CandidateStatus.PUBLISHED],
|
|
CandidateStatus.PUBLISHED: [CandidateStatus.REVOKED],
|
|
CandidateStatus.REVOKED: []
|
|
}
|
|
current_status = CandidateStatus(self.status)
|
|
if new_status not in allowed.get(current_status, []):
|
|
raise IllegalTransitionError(f"Forbidden transition from {current_status} to {new_status}")
|
|
self.status = new_status.value
|
|
# #endregion ReleaseCandidate
|
|
|
|
# #region CandidateArtifact [TYPE Class]
|
|
# @BRIEF Represents one artifact associated with a release candidate.
|
|
class CandidateArtifact(Base):
|
|
__tablename__ = "clean_release_artifacts"
|
|
|
|
id = Column(String, primary_key=True)
|
|
candidate_id = Column(String, ForeignKey("clean_release_candidates.id"), nullable=False)
|
|
path = Column(String, nullable=False)
|
|
sha256 = Column(String, nullable=False)
|
|
size = Column(Integer, nullable=False)
|
|
detected_category = Column(String, nullable=True)
|
|
declared_category = Column(String, nullable=True)
|
|
source_uri = Column(String, nullable=True)
|
|
source_host = Column(String, nullable=True)
|
|
metadata_json = Column(JSON, default=dict)
|
|
# #endregion CandidateArtifact
|
|
|
|
# #region ManifestItem [TYPE Class]
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class ManifestItem:
|
|
path: str
|
|
category: str
|
|
classification: ClassificationType
|
|
reason: str
|
|
checksum: Optional[str] = None
|
|
# #endregion ManifestItem
|
|
|
|
# #region ManifestSummary [TYPE Class]
|
|
@pydantic_dataclass(config=ConfigDict(validate_assignment=True))
|
|
class ManifestSummary:
|
|
included_count: int
|
|
excluded_count: int
|
|
prohibited_detected_count: int
|
|
# #endregion ManifestSummary
|
|
|
|
# #region DistributionManifest [TYPE Class]
|
|
# @BRIEF Immutable snapshot of the candidate payload.
|
|
# @INVARIANT: Immutable after creation.
|
|
class DistributionManifest(Base):
|
|
__tablename__ = "clean_release_manifests"
|
|
|
|
id = Column(String, primary_key=True)
|
|
candidate_id = Column(String, ForeignKey("clean_release_candidates.id"), nullable=False)
|
|
manifest_version = Column(Integer, nullable=False)
|
|
manifest_digest = Column(String, nullable=False)
|
|
artifacts_digest = Column(String, nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
created_by = Column(String, nullable=False)
|
|
source_snapshot_ref = Column(String, nullable=False)
|
|
content_json = Column(JSON, nullable=False)
|
|
immutable = Column(Boolean, default=True)
|
|
|
|
# Redesign compatibility fields (not persisted directly but used by builder/facade)
|
|
def __init__(self, **kwargs):
|
|
items = kwargs.pop("items", None)
|
|
summary = kwargs.pop("summary", None)
|
|
|
|
# Handle fields from manifest_builder.py
|
|
if "manifest_id" in kwargs:
|
|
kwargs["id"] = kwargs.pop("manifest_id")
|
|
if "generated_at" in kwargs:
|
|
kwargs["created_at"] = kwargs.pop("generated_at")
|
|
if "generated_by" in kwargs:
|
|
kwargs["created_by"] = kwargs.pop("generated_by")
|
|
if "deterministic_hash" in kwargs:
|
|
kwargs["manifest_digest"] = kwargs.pop("deterministic_hash")
|
|
if "policy_id" in kwargs:
|
|
kwargs.pop("policy_id")
|
|
|
|
if items is not None and summary is not None:
|
|
expected_count = int(summary.included_count) + int(summary.excluded_count)
|
|
if expected_count != len(items):
|
|
raise ValueError("manifest summary counts must match items size")
|
|
|
|
# Ensure required DB fields have defaults if missing
|
|
if "manifest_version" not in kwargs:
|
|
kwargs["manifest_version"] = 1
|
|
if "artifacts_digest" not in kwargs:
|
|
kwargs["artifacts_digest"] = kwargs.get("manifest_digest", "pending")
|
|
if "source_snapshot_ref" not in kwargs:
|
|
kwargs["source_snapshot_ref"] = "pending"
|
|
|
|
# Pack items and summary into content_json if provided
|
|
if items is not None or summary is not None:
|
|
content = dict(kwargs.get("content_json") or {})
|
|
if items is not None:
|
|
content["items"] = [
|
|
{
|
|
"path": i.path,
|
|
"category": i.category,
|
|
"classification": i.classification.value,
|
|
"reason": i.reason,
|
|
"checksum": i.checksum
|
|
} for i in items
|
|
]
|
|
if summary is not None:
|
|
content["summary"] = {
|
|
"included_count": summary.included_count,
|
|
"excluded_count": summary.excluded_count,
|
|
"prohibited_detected_count": summary.prohibited_detected_count
|
|
}
|
|
kwargs["content_json"] = content
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
@property
|
|
def manifest_id(self) -> str:
|
|
return self.id
|
|
|
|
@property
|
|
def deterministic_hash(self) -> str:
|
|
return self.manifest_digest
|
|
|
|
@property
|
|
def summary(self) -> ManifestSummary:
|
|
payload = (self.content_json or {}).get("summary", {})
|
|
return ManifestSummary(
|
|
included_count=int(payload.get("included_count", 0)),
|
|
excluded_count=int(payload.get("excluded_count", 0)),
|
|
prohibited_detected_count=int(payload.get("prohibited_detected_count", 0)),
|
|
)
|
|
# #endregion DistributionManifest
|
|
|
|
# #region SourceRegistrySnapshot [TYPE Class]
|
|
# @BRIEF Immutable registry snapshot for allowed sources.
|
|
class SourceRegistrySnapshot(Base):
|
|
__tablename__ = "clean_release_registry_snapshots"
|
|
|
|
id = Column(String, primary_key=True)
|
|
registry_id = Column(String, nullable=False)
|
|
registry_version = Column(String, nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
allowed_hosts = Column(JSON, nullable=False) # List[str]
|
|
allowed_schemes = Column(JSON, nullable=False) # List[str]
|
|
allowed_source_types = Column(JSON, nullable=False) # List[str]
|
|
immutable = Column(Boolean, default=True)
|
|
# #endregion SourceRegistrySnapshot
|
|
|
|
# #region CleanPolicySnapshot [TYPE Class]
|
|
# @BRIEF Immutable policy snapshot used to evaluate a run.
|
|
class CleanPolicySnapshot(Base):
|
|
__tablename__ = "clean_release_policy_snapshots"
|
|
|
|
id = Column(String, primary_key=True)
|
|
policy_id = Column(String, nullable=False)
|
|
policy_version = Column(String, nullable=False)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
content_json = Column(JSON, nullable=False)
|
|
registry_snapshot_id = Column(String, ForeignKey("clean_release_registry_snapshots.id"), nullable=False)
|
|
immutable = Column(Boolean, default=True)
|
|
# #endregion CleanPolicySnapshot
|
|
|
|
# #region ComplianceRun [TYPE Class]
|
|
# @BRIEF Operational record for one compliance execution.
|
|
class ComplianceRun(Base):
|
|
__tablename__ = "clean_release_compliance_runs"
|
|
|
|
id = Column(String, primary_key=True)
|
|
candidate_id = Column(String, ForeignKey("clean_release_candidates.id"), nullable=False)
|
|
manifest_id = Column(String, ForeignKey("clean_release_manifests.id"), nullable=False)
|
|
manifest_digest = Column(String, nullable=False)
|
|
policy_snapshot_id = Column(String, ForeignKey("clean_release_policy_snapshots.id"), nullable=False)
|
|
registry_snapshot_id = Column(String, ForeignKey("clean_release_registry_snapshots.id"), nullable=False)
|
|
requested_by = Column(String, nullable=False)
|
|
requested_at = Column(DateTime, default=datetime.utcnow)
|
|
started_at = Column(DateTime, nullable=True)
|
|
finished_at = Column(DateTime, nullable=True)
|
|
status = Column(String, default=RunStatus.PENDING)
|
|
final_status = Column(String, nullable=True) # ComplianceDecision
|
|
failure_reason = Column(String, nullable=True)
|
|
task_id = Column(String, nullable=True)
|
|
|
|
@property
|
|
def check_run_id(self) -> str:
|
|
return self.id
|
|
# #endregion ComplianceRun
|
|
|
|
# #region ComplianceStageRun [TYPE Class]
|
|
# @BRIEF Stage-level execution record inside a run.
|
|
class ComplianceStageRun(Base):
|
|
__tablename__ = "clean_release_compliance_stage_runs"
|
|
|
|
id = Column(String, primary_key=True)
|
|
run_id = Column(String, ForeignKey("clean_release_compliance_runs.id"), nullable=False)
|
|
stage_name = Column(String, nullable=False)
|
|
status = Column(String, nullable=False)
|
|
started_at = Column(DateTime, nullable=True)
|
|
finished_at = Column(DateTime, nullable=True)
|
|
decision = Column(String, nullable=True) # ComplianceDecision
|
|
details_json = Column(JSON, default=dict)
|
|
# #endregion ComplianceStageRun
|
|
|
|
# #region ViolationSeverity [TYPE Class]
|
|
# @BRIEF Backward-compatible violation severity enum for legacy clean-release tests.
|
|
class ViolationSeverity(str, Enum):
|
|
CRITICAL = "CRITICAL"
|
|
MAJOR = "MAJOR"
|
|
MINOR = "MINOR"
|
|
# #endregion ViolationSeverity
|
|
|
|
# #region ViolationCategory [TYPE Class]
|
|
# @BRIEF Backward-compatible violation category enum for legacy clean-release tests.
|
|
class ViolationCategory(str, Enum):
|
|
DATA_PURITY = "DATA_PURITY"
|
|
EXTERNAL_SOURCE = "EXTERNAL_SOURCE"
|
|
SOURCE_ISOLATION = "SOURCE_ISOLATION"
|
|
MANIFEST_CONSISTENCY = "MANIFEST_CONSISTENCY"
|
|
EXTERNAL_ENDPOINT = "EXTERNAL_ENDPOINT"
|
|
# #endregion ViolationCategory
|
|
|
|
# #region ComplianceViolation [TYPE Class]
|
|
# @BRIEF Violation produced by a stage.
|
|
class ComplianceViolation(Base):
|
|
__tablename__ = "clean_release_compliance_violations"
|
|
|
|
id = Column(String, primary_key=True)
|
|
run_id = Column(String, ForeignKey("clean_release_compliance_runs.id"), nullable=False)
|
|
stage_name = Column(String, nullable=False)
|
|
code = Column(String, nullable=False)
|
|
severity = Column(String, nullable=False)
|
|
artifact_path = Column(String, nullable=True)
|
|
artifact_sha256 = Column(String, nullable=True)
|
|
message = Column(String, nullable=False)
|
|
evidence_json = Column(JSON, default=dict)
|
|
|
|
def __init__(self, **kwargs):
|
|
if "violation_id" in kwargs:
|
|
kwargs["id"] = kwargs.pop("violation_id")
|
|
if "check_run_id" in kwargs:
|
|
kwargs["run_id"] = kwargs.pop("check_run_id")
|
|
if "category" in kwargs:
|
|
category = kwargs.pop("category")
|
|
kwargs["stage_name"] = category.value if isinstance(category, ViolationCategory) else str(category)
|
|
if "location" in kwargs:
|
|
kwargs["artifact_path"] = kwargs.pop("location")
|
|
if "remediation" in kwargs:
|
|
remediation = kwargs.pop("remediation")
|
|
evidence = dict(kwargs.get("evidence_json") or {})
|
|
evidence["remediation"] = remediation
|
|
kwargs["evidence_json"] = evidence
|
|
if "blocked_release" in kwargs:
|
|
blocked_release = kwargs.pop("blocked_release")
|
|
evidence = dict(kwargs.get("evidence_json") or {})
|
|
evidence["blocked_release"] = blocked_release
|
|
kwargs["evidence_json"] = evidence
|
|
if "detected_at" in kwargs:
|
|
kwargs.pop("detected_at")
|
|
if "code" not in kwargs:
|
|
kwargs["code"] = "LEGACY_VIOLATION"
|
|
if "message" not in kwargs:
|
|
kwargs["message"] = kwargs.get("stage_name", "LEGACY_VIOLATION")
|
|
super().__init__(**kwargs)
|
|
|
|
@property
|
|
def violation_id(self) -> str:
|
|
return self.id
|
|
|
|
@violation_id.setter
|
|
def violation_id(self, value: str) -> None:
|
|
self.id = value
|
|
|
|
@property
|
|
def check_run_id(self) -> str:
|
|
return self.run_id
|
|
|
|
@property
|
|
def category(self) -> ViolationCategory:
|
|
return ViolationCategory(self.stage_name)
|
|
|
|
@category.setter
|
|
def category(self, value: ViolationCategory) -> None:
|
|
self.stage_name = value.value if isinstance(value, ViolationCategory) else str(value)
|
|
|
|
@property
|
|
def location(self) -> Optional[str]:
|
|
return self.artifact_path
|
|
|
|
@property
|
|
def remediation(self) -> Optional[str]:
|
|
return (self.evidence_json or {}).get("remediation")
|
|
|
|
@property
|
|
def blocked_release(self) -> bool:
|
|
return bool((self.evidence_json or {}).get("blocked_release", False))
|
|
# #endregion ComplianceViolation
|
|
|
|
# #region ComplianceReport [TYPE Class]
|
|
# @BRIEF Immutable result derived from a completed run.
|
|
# @INVARIANT: Immutable after creation.
|
|
class ComplianceReport(Base):
|
|
__tablename__ = "clean_release_compliance_reports"
|
|
|
|
id = Column(String, primary_key=True)
|
|
run_id = Column(String, ForeignKey("clean_release_compliance_runs.id"), nullable=False)
|
|
candidate_id = Column(String, ForeignKey("clean_release_candidates.id"), nullable=False)
|
|
final_status = Column(String, nullable=False) # ComplianceDecision
|
|
summary_json = Column(JSON, nullable=False)
|
|
generated_at = Column(DateTime, default=datetime.utcnow)
|
|
immutable = Column(Boolean, default=True)
|
|
|
|
def __init__(self, **kwargs):
|
|
if "report_id" in kwargs:
|
|
kwargs["id"] = kwargs.pop("report_id")
|
|
if "check_run_id" in kwargs:
|
|
kwargs["run_id"] = kwargs.pop("check_run_id")
|
|
operator_summary = kwargs.pop("operator_summary", None)
|
|
structured_payload_ref = kwargs.pop("structured_payload_ref", None)
|
|
violations_count = kwargs.pop("violations_count", None)
|
|
blocking_violations_count = kwargs.pop("blocking_violations_count", None)
|
|
|
|
final_status = kwargs.get("final_status")
|
|
final_status_value = getattr(final_status, "value", final_status)
|
|
|
|
if (
|
|
final_status_value in {CheckFinalStatus.BLOCKED.value, ComplianceDecision.BLOCKED.value}
|
|
and blocking_violations_count is not None
|
|
and int(blocking_violations_count) <= 0
|
|
):
|
|
raise ValueError("blocked report requires blocking violations")
|
|
|
|
if (
|
|
operator_summary is not None
|
|
or structured_payload_ref is not None
|
|
or violations_count is not None
|
|
or blocking_violations_count is not None
|
|
):
|
|
kwargs["summary_json"] = {
|
|
"operator_summary": operator_summary or "",
|
|
"structured_payload_ref": structured_payload_ref,
|
|
"violations_count": int(violations_count or 0),
|
|
"blocking_violations_count": int(blocking_violations_count or 0),
|
|
}
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
@property
|
|
def report_id(self) -> str:
|
|
return self.id
|
|
|
|
@property
|
|
def check_run_id(self) -> str:
|
|
return self.run_id
|
|
|
|
@property
|
|
def operator_summary(self) -> str:
|
|
return (self.summary_json or {}).get("operator_summary", "")
|
|
|
|
@property
|
|
def structured_payload_ref(self) -> Optional[str]:
|
|
return (self.summary_json or {}).get("structured_payload_ref")
|
|
|
|
@property
|
|
def violations_count(self) -> int:
|
|
return int((self.summary_json or {}).get("violations_count", 0))
|
|
|
|
@property
|
|
def blocking_violations_count(self) -> int:
|
|
return int((self.summary_json or {}).get("blocking_violations_count", 0))
|
|
# #endregion ComplianceReport
|
|
|
|
# #region ApprovalDecision [TYPE Class]
|
|
# @BRIEF Approval or rejection bound to a candidate and report.
|
|
class ApprovalDecision(Base):
|
|
__tablename__ = "clean_release_approval_decisions"
|
|
|
|
id = Column(String, primary_key=True)
|
|
candidate_id = Column(String, ForeignKey("clean_release_candidates.id"), nullable=False)
|
|
report_id = Column(String, ForeignKey("clean_release_compliance_reports.id"), nullable=False)
|
|
decision = Column(String, nullable=False) # ApprovalDecisionType
|
|
decided_by = Column(String, nullable=False)
|
|
decided_at = Column(DateTime, default=datetime.utcnow)
|
|
comment = Column(String, nullable=True)
|
|
# #endregion ApprovalDecision
|
|
|
|
# #region PublicationRecord [TYPE Class]
|
|
# @BRIEF Publication or revocation record.
|
|
class PublicationRecord(Base):
|
|
__tablename__ = "clean_release_publication_records"
|
|
|
|
id = Column(String, primary_key=True)
|
|
candidate_id = Column(String, ForeignKey("clean_release_candidates.id"), nullable=False)
|
|
report_id = Column(String, ForeignKey("clean_release_compliance_reports.id"), nullable=False)
|
|
published_by = Column(String, nullable=False)
|
|
published_at = Column(DateTime, default=datetime.utcnow)
|
|
target_channel = Column(String, nullable=False)
|
|
publication_ref = Column(String, nullable=True)
|
|
status = Column(String, default=PublicationStatus.ACTIVE)
|
|
# #endregion PublicationRecord
|
|
|
|
# #region CleanReleaseAuditLog [TYPE Class]
|
|
# @BRIEF Represents a persistent audit log entry for clean release actions.
|
|
import uuid
|
|
class CleanReleaseAuditLog(Base):
|
|
__tablename__ = "clean_release_audit_logs"
|
|
|
|
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
|
|
candidate_id = Column(String, index=True, nullable=True)
|
|
action = Column(String, nullable=False) # e.g. "TRANSITION", "APPROVE", "PUBLISH"
|
|
actor = Column(String, nullable=False)
|
|
timestamp = Column(DateTime, default=datetime.utcnow)
|
|
details_json = Column(JSON, default=dict)
|
|
# #endregion CleanReleaseAuditLog
|
|
|
|
# #endregion CleanReleaseModels |