- Replace BeliefFormatter with CotJsonFormatter (single-line JSON with ts, level, trace_id, src, marker, intent, payload, error, span_id) - Migrate belief_scope to use cot_log() for structured JSON output - Update monkey-patched reason/reflect/explore to pass extra= dict - Strip 200+ inline [REASON]/[REFLECT]/[EXPLORE] markers from 50+ files - Remove belief_scope from hot-path utilities (_parse_datetime, _json_load_if_needed) to eliminate startup log noise - Register TraceContextMiddleware in app.py (was implemented but unused) - Seed trace_id in startup_event/shutdown_event for background tasks - Fix broken relative imports in translate routes (3 dots → 4 dots) - Migrate 43 translate_routes log calls to logger.reason/explore - Fix SyntaxError (positional arg after extra=) in _repo_operations_routes - Fix IndentationError in rbac_permission_catalog except-block - Add smoke test tests/test_smoke_app.py (catches import errors before run)
232 lines
9.9 KiB
Python
232 lines
9.9 KiB
Python
# #region ComplianceExecutionService [C:5] [TYPE Module] [SEMANTICS clean-release, compliance, execution, stages, immutable-evidence]
|
|
# @BRIEF Create and execute compliance runs with trusted snapshots, deterministic stages, violations and immutable report persistence.
|
|
# @LAYER: Domain
|
|
# @RELATION DEPENDS_ON -> RepositoryRelations
|
|
# @RELATION DEPENDS_ON -> PolicyResolutionService
|
|
# @RELATION DEPENDS_ON -> ComplianceStages
|
|
# @RELATION DEPENDS_ON -> ReportBuilder
|
|
# @PRE: Repository adapters, trusted policy resolution, and deterministic stage implementations are available for the run request.
|
|
# @POST: Candidate-scoped compliance runs persist stage evidence, terminal status, and immutable report artifacts when execution succeeds.
|
|
# @SIDE_EFFECT: Persists runs, stage results, violations, and reports through repository adapters and audit helpers.
|
|
# @DATA_CONTRACT: Input[candidate_id, requested_by, manifest_id?, policy snapshots, stage results] -> Output[ComplianceExecutionResult]
|
|
# @INVARIANT: A run binds to exactly one candidate/manifest/policy/registry snapshot set.
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from typing import Any, Iterable, List, Optional, cast
|
|
from uuid import uuid4
|
|
|
|
from ...core.logger import belief_scope, logger
|
|
from ...models.clean_release import (
|
|
ComplianceReport,
|
|
ComplianceRun,
|
|
ComplianceStageRun,
|
|
ComplianceViolation,
|
|
DistributionManifest,
|
|
)
|
|
from .audit_service import audit_check_run, audit_report, audit_violation
|
|
from .enums import ComplianceDecision, RunStatus
|
|
from .exceptions import ComplianceRunError, PolicyResolutionError
|
|
from .policy_resolution_service import resolve_trusted_policy_snapshots
|
|
from .report_builder import ComplianceReportBuilder
|
|
from .repository import CleanReleaseRepository
|
|
from .stages import build_default_stages, derive_final_status
|
|
from .stages.base import ComplianceStage, ComplianceStageContext, build_stage_run_record
|
|
|
|
|
|
belief_logger = cast(Any, logger)
|
|
|
|
|
|
# #region ComplianceExecutionResult [TYPE Class]
|
|
# @BRIEF Return envelope for compliance execution with run/report and persisted stage artifacts.
|
|
@dataclass
|
|
class ComplianceExecutionResult:
|
|
run: ComplianceRun
|
|
report: Optional[ComplianceReport]
|
|
stage_runs: List[ComplianceStageRun]
|
|
violations: List[ComplianceViolation]
|
|
|
|
|
|
# #endregion ComplianceExecutionResult
|
|
|
|
|
|
# #region ComplianceExecutionService [TYPE Class]
|
|
# @BRIEF Execute clean-release compliance lifecycle over trusted snapshots and immutable evidence.
|
|
# @PRE: Database session active, candidate registered
|
|
# @POST: Returns ComplianceReport with pass/fail status and violation details
|
|
# @SIDE_EFFECT: Updates compliance status in database, logs violations
|
|
# @DATA_CONTRACT: ComplianceCheckResult, ComplianceReport, Violation
|
|
class ComplianceExecutionService:
|
|
TASK_PLUGIN_ID = "clean-release-compliance"
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
repository: CleanReleaseRepository,
|
|
config_manager,
|
|
stages: Optional[Iterable[ComplianceStage]] = None,
|
|
):
|
|
self.repository = repository
|
|
self.config_manager = config_manager
|
|
self.stages = list(stages) if stages is not None else build_default_stages()
|
|
self.report_builder = ComplianceReportBuilder(repository)
|
|
|
|
# [DEF:_resolve_manifest:Function]
|
|
# @PURPOSE: Resolve explicit manifest or fallback to latest candidate manifest.
|
|
# @PRE: candidate exists.
|
|
# @POST: Returns manifest snapshot or raises ComplianceRunError.
|
|
def _resolve_manifest(
|
|
self, candidate_id: str, manifest_id: Optional[str]
|
|
) -> DistributionManifest:
|
|
with belief_scope("ComplianceExecutionService._resolve_manifest"):
|
|
manifest_id_value = cast(Optional[str], manifest_id)
|
|
if manifest_id_value is not None and manifest_id_value != "":
|
|
manifest = self.repository.get_manifest(manifest_id_value)
|
|
if manifest is None:
|
|
raise ComplianceRunError(
|
|
f"manifest '{manifest_id_value}' not found"
|
|
)
|
|
if str(getattr(manifest, "candidate_id")) != candidate_id:
|
|
raise ComplianceRunError("manifest does not belong to candidate")
|
|
return manifest
|
|
|
|
manifests = self.repository.get_manifests_by_candidate(candidate_id)
|
|
if not manifests:
|
|
raise ComplianceRunError(f"candidate '{candidate_id}' has no manifest")
|
|
return sorted(
|
|
manifests, key=lambda item: item.manifest_version, reverse=True
|
|
)[0]
|
|
|
|
# [/DEF:_resolve_manifest:Function]
|
|
|
|
# [DEF:_persist_stage_run:Function]
|
|
# @PURPOSE: Persist stage run if repository supports stage records.
|
|
# @POST: Stage run is persisted when adapter is available, otherwise no-op.
|
|
def _persist_stage_run(self, stage_run: ComplianceStageRun) -> None:
|
|
self.repository.save_stage_run(stage_run)
|
|
|
|
# [/DEF:_persist_stage_run:Function]
|
|
|
|
# [DEF:_persist_violations:Function]
|
|
# @PURPOSE: Persist stage violations via repository adapters.
|
|
# @POST: Violations are appended to repository evidence store.
|
|
def _persist_violations(self, violations: List[ComplianceViolation]) -> None:
|
|
for violation in violations:
|
|
self.repository.save_violation(violation)
|
|
|
|
# [/DEF:_persist_violations:Function]
|
|
|
|
# [DEF:execute_run:Function]
|
|
# @PURPOSE: Execute compliance run stages and finalize immutable report on terminal success.
|
|
# @PRE: candidate exists and trusted policy/registry snapshots are resolvable.
|
|
# @POST: Run and evidence are persisted; report exists for SUCCEEDED runs.
|
|
def execute_run(
|
|
self,
|
|
*,
|
|
candidate_id: str,
|
|
requested_by: str,
|
|
manifest_id: Optional[str] = None,
|
|
) -> ComplianceExecutionResult:
|
|
with belief_scope("ComplianceExecutionService.execute_run"):
|
|
belief_logger.reason(
|
|
f"Starting compliance execution candidate_id={candidate_id}"
|
|
)
|
|
|
|
candidate = self.repository.get_candidate(candidate_id)
|
|
if candidate is None:
|
|
raise ComplianceRunError(f"candidate '{candidate_id}' not found")
|
|
|
|
manifest = self._resolve_manifest(candidate_id, manifest_id)
|
|
|
|
try:
|
|
policy_snapshot, registry_snapshot = resolve_trusted_policy_snapshots(
|
|
config_manager=self.config_manager,
|
|
repository=self.repository,
|
|
)
|
|
except PolicyResolutionError as exc:
|
|
raise ComplianceRunError(str(exc)) from exc
|
|
|
|
run = ComplianceRun(
|
|
id=f"run-{uuid4()}",
|
|
candidate_id=candidate_id,
|
|
manifest_id=str(getattr(manifest, "id")),
|
|
manifest_digest=str(getattr(manifest, "manifest_digest")),
|
|
policy_snapshot_id=str(getattr(policy_snapshot, "id")),
|
|
registry_snapshot_id=str(getattr(registry_snapshot, "id")),
|
|
requested_by=requested_by,
|
|
requested_at=datetime.now(timezone.utc),
|
|
started_at=datetime.now(timezone.utc),
|
|
status=RunStatus.RUNNING.value,
|
|
)
|
|
self.repository.save_check_run(run)
|
|
|
|
stage_runs: List[ComplianceStageRun] = []
|
|
violations: List[ComplianceViolation] = []
|
|
report: Optional[ComplianceReport] = None
|
|
|
|
context = ComplianceStageContext(
|
|
run=run,
|
|
candidate=candidate,
|
|
manifest=manifest,
|
|
policy=policy_snapshot,
|
|
registry=registry_snapshot,
|
|
)
|
|
|
|
try:
|
|
for stage in self.stages:
|
|
started = datetime.now(timezone.utc)
|
|
result = stage.execute(context)
|
|
finished = datetime.now(timezone.utc)
|
|
|
|
stage_run = build_stage_run_record(
|
|
run_id=str(getattr(run, "id")),
|
|
stage_name=stage.stage_name,
|
|
result=result,
|
|
started_at=started,
|
|
finished_at=finished,
|
|
)
|
|
self._persist_stage_run(stage_run)
|
|
stage_runs.append(stage_run)
|
|
|
|
if result.violations:
|
|
self._persist_violations(result.violations)
|
|
violations.extend(result.violations)
|
|
|
|
setattr(run, "final_status", derive_final_status(stage_runs).value)
|
|
setattr(run, "status", RunStatus.SUCCEEDED.value)
|
|
setattr(run, "finished_at", datetime.now(timezone.utc))
|
|
self.repository.save_check_run(run)
|
|
|
|
report = self.report_builder.build_report_payload(run, violations)
|
|
report = self.report_builder.persist_report(report)
|
|
setattr(run, "report_id", str(getattr(report, "id")))
|
|
self.repository.save_check_run(run)
|
|
belief_logger.reflect(
|
|
f"Compliance run completed run_id={getattr(run, 'id')} final_status={getattr(run, 'final_status', None)}"
|
|
)
|
|
except Exception as exc: # noqa: BLE001
|
|
setattr(run, "status", RunStatus.FAILED.value)
|
|
setattr(run, "final_status", ComplianceDecision.ERROR.value)
|
|
setattr(run, "failure_reason", str(exc))
|
|
setattr(run, "finished_at", datetime.now(timezone.utc))
|
|
self.repository.save_check_run(run)
|
|
belief_logger.explore(
|
|
f"Compliance run failed run_id={getattr(run, 'id')}: {exc}"
|
|
)
|
|
|
|
return ComplianceExecutionResult(
|
|
run=run,
|
|
report=report,
|
|
stage_runs=stage_runs,
|
|
violations=violations,
|
|
)
|
|
|
|
# [/DEF:execute_run:Function]
|
|
|
|
|
|
# #endregion ComplianceExecutionService
|
|
|
|
# #endregion ComplianceExecutionService
|