274 lines
16 KiB
Markdown
274 lines
16 KiB
Markdown
# Module Contracts: Clean Repository Enterprise Preparation
|
||
|
||
## Contract Scope
|
||
|
||
Этот документ фиксирует семантические контракты новых модулей для enterprise clean-подготовки.
|
||
Контракты синхронизированы с UX состояниями из [`ux_reference.md`](../ux_reference.md) и моделью данных из [`data-model.md`](../data-model.md).
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.services.clean_release.policy_engine:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: clean-release, policy, classification, compliance, enterprise
|
||
# @PURPOSE: Evaluate artifact and source policies for enterprise clean profile with deterministic outcomes.
|
||
# @LAYER: Domain
|
||
# @RELATION: DEPENDS_ON -> backend.src.models.clean_release.CleanProfilePolicy
|
||
# @RELATION: DEPENDS_ON -> backend.src.models.clean_release.ResourceSourceRegistry
|
||
# @INVARIANT: For enterprise-clean profile, external resource sources are always forbidden.
|
||
# @TEST_CONTRACT: CleanPolicyEvaluation -> PolicyDecision
|
||
# @TEST_FIXTURE: policy_enterprise_clean -> {"external_source_forbidden": true, "prohibited_artifact_categories": ["test-data","demo-data"]}
|
||
# @TEST_EDGE: conflicting_rules -> same artifact matches allowed and prohibited patterns, must resolve to prohibited
|
||
# @TEST_EDGE: missing_registry -> enterprise-clean policy without internal_source_registry_ref must fail validation
|
||
# @TEST_EDGE: empty_prohibited_categories -> must fail policy activation for enterprise-clean
|
||
# @TEST_INVARIANT: deterministic_policy_result -> VERIFIED_BY: [policy_enterprise_clean, conflicting_rules]
|
||
class CleanPolicyEngine:
|
||
# @PURPOSE: Initialize policy engine with active policy and internal source registry.
|
||
# @PRE: Active policy exists and is internally consistent.
|
||
# @POST: Engine is ready for deterministic classification.
|
||
def __init__(self): ...
|
||
|
||
# @PURPOSE: Validate policy consistency before any release operation.
|
||
# @PRE: Policy payload is provided.
|
||
# @POST: Returns validation result with blocking reasons if inconsistent.
|
||
def validate_policy(self): ...
|
||
|
||
# @PURPOSE: Classify one artifact against policy categories.
|
||
# @PRE: Artifact metadata is provided.
|
||
# @POST: Returns one of [required-system, allowed, excluded-prohibited].
|
||
def classify_artifact(self): ...
|
||
|
||
# @PURPOSE: Validate that resource source belongs to internal registry.
|
||
# @PRE: Source endpoint is provided.
|
||
# @POST: Returns pass/fail and violation payload for non-internal sources.
|
||
def validate_resource_source(self): ...
|
||
|
||
# @PURPOSE: Evaluate full candidate policy compliance input.
|
||
# @PRE: Candidate artifacts and sources are provided.
|
||
# @POST: Returns deterministic policy decision and violations list.
|
||
def evaluate_candidate(self): ...
|
||
# [/DEF:backend.src.services.clean_release.policy_engine:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.services.clean_release.compliance_orchestrator:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: clean-release, compliance, orchestrator, release-gate, audit
|
||
# @PURPOSE: Orchestrate multi-stage compliance checks and produce final COMPLIANT/BLOCKED decision.
|
||
# @LAYER: Domain
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.policy_engine
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.report_builder
|
||
# @RELATION: DEPENDS_ON -> backend.src.models.clean_release.ComplianceCheckRun
|
||
# @INVARIANT: A check run cannot end with COMPLIANT if any mandatory stage fails.
|
||
# @TEST_CONTRACT: ComplianceRunInput -> ComplianceRunResult
|
||
# @TEST_FIXTURE: compliant_candidate -> {"stages":{"data_purity":"pass","internal_sources_only":"pass","no_external_endpoints":"pass","manifest_consistency":"pass"}}
|
||
# @TEST_EDGE: stage_failure_blocks_release -> one mandatory stage fail must force BLOCKED
|
||
# @TEST_EDGE: missing_stage_result -> incomplete stage set must fail run as invalid
|
||
# @TEST_EDGE: report_generation_error -> run must be marked failed, never falsely compliant
|
||
# @TEST_INVARIANT: blocking_gate_integrity -> VERIFIED_BY: [compliant_candidate, stage_failure_blocks_release]
|
||
class CleanComplianceOrchestrator:
|
||
# @PURPOSE: Start new compliance check run for a release candidate.
|
||
# @PRE: Candidate exists and policy is active.
|
||
# @POST: Check run created with status RUNNING.
|
||
def start_check_run(self): ...
|
||
|
||
# @PURPOSE: Execute mandatory stage sequence deterministically.
|
||
# @PRE: Check run is in RUNNING state.
|
||
# @POST: Stage statuses recorded for all mandatory checks.
|
||
def execute_stages(self): ...
|
||
|
||
# @PURPOSE: Finalize run status and trigger report build.
|
||
# @PRE: Stage execution completed or failed.
|
||
# @POST: Final status set to COMPLIANT/BLOCKED/FAILED with immutable result summary.
|
||
def finalize_run(self): ...
|
||
# [/DEF:backend.src.services.clean_release.compliance_orchestrator:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.services.clean_release.report_builder:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: compliance-report, audit, release, violations, summary
|
||
# @PURPOSE: Build operator-readable and machine-readable compliance reports for release audit.
|
||
# @LAYER: Domain
|
||
# @RELATION: DEPENDS_ON -> backend.src.models.clean_release.ComplianceReport
|
||
# @RELATION: DEPENDS_ON -> backend.src.models.clean_release.ComplianceViolation
|
||
# @INVARIANT: Report counters must match underlying violations set.
|
||
# @TEST_CONTRACT: ComplianceRunResult -> ComplianceReport
|
||
# @TEST_FIXTURE: blocked_with_two_violations -> {"violations":[{"category":"data-purity","blocked_release":true},{"category":"external-source","blocked_release":true}]}
|
||
# @TEST_EDGE: empty_violations_for_blocked -> blocked run without violations must fail report validation
|
||
# @TEST_EDGE: counter_mismatch -> blocking_violations_count cannot exceed violations_count
|
||
# @TEST_EDGE: missing_operator_summary -> report invalid without human-readable summary
|
||
# @TEST_INVARIANT: report_counter_consistency -> VERIFIED_BY: [blocked_with_two_violations, counter_mismatch]
|
||
class ComplianceReportBuilder:
|
||
# @PURPOSE: Build normalized report payload from run result.
|
||
# @PRE: Run has terminal status.
|
||
# @POST: Report payload includes required machine and operator fields.
|
||
def build_report_payload(self): ...
|
||
|
||
# @PURPOSE: Persist report and return report reference.
|
||
# @PRE: Report payload validated.
|
||
# @POST: Report saved with stable report_id.
|
||
def persist_report(self): ...
|
||
# [/DEF:backend.src.services.clean_release.report_builder:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.scripts.clean_release_tui:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: tui, ncurses, enterprise-clean, compliance, operator-flow
|
||
# @PURPOSE: Provide interactive ncurses-compatible TUI flow for clean release validation and report export.
|
||
# @LAYER: UI
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.compliance_orchestrator
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.report_builder
|
||
# @INVARIANT: TUI must always expose explicit terminal state READY/RUNNING/COMPLIANT/BLOCKED.
|
||
# @UX_STATE: READY -> Candidate and profile are visible; operator can trigger check via hotkey.
|
||
# @UX_STATE: RUNNING -> Stage progress is visible with current active check.
|
||
# @UX_STATE: COMPLIANT -> Final pass state with report id and export action.
|
||
# @UX_STATE: BLOCKED -> Final blocked state with violation table and remediation hints.
|
||
# @UX_FEEDBACK: On violation, blocked status is rendered with category/location/remediation.
|
||
# @UX_RECOVERY: Operator can fix inputs/config and rerun check from same screen without restarting tool.
|
||
# @TEST_CONTRACT: TuiOperatorAction -> TuiStateTransition
|
||
# @TEST_FIXTURE: ready_to_running -> {"initial":"READY","action":"F5","expected":"RUNNING"}
|
||
# @TEST_EDGE: blocked_due_to_external_source -> {"initial":"RUNNING","result":"external-source violation","expected":"BLOCKED"}
|
||
# @TEST_EDGE: internal_registry_unavailable -> {"initial":"RUNNING","error":"internal source unavailable","expected":"BLOCKED"}
|
||
# @TEST_EDGE: report_export_without_terminal_state -> export denied before COMPLIANT/BLOCKED
|
||
# @TEST_INVARIANT: state_machine_integrity -> VERIFIED_BY: [ready_to_running, blocked_due_to_external_source]
|
||
module CleanReleaseTui:
|
||
# @PURPOSE: Render main screen and initialize state machine.
|
||
# @PRE: Terminal supports ncurses-compatible mode.
|
||
# @POST: UI enters READY state with loaded candidate/profile context.
|
||
def run(self): ...
|
||
|
||
# @PURPOSE: Handle key actions (run check, view details, export, exit).
|
||
# @PRE: Current UI state exists.
|
||
# @POST: Deterministic transition to next valid state.
|
||
def handle_input(self): ...
|
||
|
||
# @PURPOSE: Render progress and status updates from orchestrator.
|
||
# @PRE: Check run started.
|
||
# @POST: UI reflects latest stage statuses and final result.
|
||
def render_status(self): ...
|
||
# [/DEF:backend.src.scripts.clean_release_tui:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.api.routes.clean_release:Module]
|
||
# @TIER: STANDARD
|
||
# @SEMANTICS: api, clean-release, compliance, report, release-gate
|
||
# @PURPOSE: Expose release compliance endpoints for CI and controlled integrations.
|
||
# @LAYER: API
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.compliance_orchestrator
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.report_builder
|
||
# @INVARIANT: API never returns release-approved state when orchestration result is blocked/failed.
|
||
module CleanReleaseRouter:
|
||
# @PURPOSE: Start compliance check for a candidate.
|
||
# @PRE: Candidate exists and caller is authorized.
|
||
# @POST: Returns check_run_id and initial RUNNING status.
|
||
def start_check(self): ...
|
||
|
||
# @PURPOSE: Read compliance check status and stage details.
|
||
# @PRE: check_run_id exists.
|
||
# @POST: Returns deterministic status payload with stage matrix.
|
||
def get_check_status(self): ...
|
||
|
||
# @PURPOSE: Retrieve final compliance report.
|
||
# @PRE: check run reached terminal state.
|
||
# @POST: Returns report metadata and violations summary.
|
||
def get_report(self): ...
|
||
# [/DEF:backend.src.api.routes.clean_release:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.services.clean_release.config_loader:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: clean-release, config, yaml, policy-source, declarative
|
||
# @PURPOSE: Load and validate .clean-release.yaml from repository root, providing typed config to all pipeline stages.
|
||
# @LAYER: Infrastructure
|
||
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.policy_engine
|
||
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.compliance_orchestrator
|
||
# @INVARIANT: Config load must fail fast on invalid/missing required fields for enterprise-clean profile.
|
||
# @TEST_CONTRACT: YamlFilePath -> CleanReleaseConfig
|
||
# @TEST_FIXTURE: valid_enterprise_config -> {"profile":"enterprise-clean","scan_mode":"repo","prohibited_categories":["test-data"],"allowed_sources":["*.corp.local"]}
|
||
# @TEST_EDGE: missing_yaml -> repo without .clean-release.yaml must raise ConfigNotFoundError
|
||
# @TEST_EDGE: missing_allowed_sources -> enterprise-clean without allowed_sources must fail validation
|
||
# @TEST_EDGE: invalid_scan_mode -> scan_mode="unknown" must raise ValueError
|
||
# @TEST_INVARIANT: config_validation_integrity -> VERIFIED_BY: [valid_enterprise_config, missing_allowed_sources]
|
||
class CleanReleaseConfigLoader:
|
||
# @PURPOSE: Discover and load .clean-release.yaml from target path.
|
||
# @PRE: Path to repository root or explicit config path provided.
|
||
# @POST: Returns validated CleanReleaseConfig or raises ConfigError.
|
||
def load_config(self): ...
|
||
|
||
# @PURPOSE: Validate config schema and business rules.
|
||
# @PRE: Raw YAML parsed.
|
||
# @POST: Returns typed config with all required fields populated.
|
||
def validate_config(self): ...
|
||
# [/DEF:backend.src.services.clean_release.config_loader:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.services.clean_release.filesystem_scanner:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: clean-release, scanner, filesystem, artifacts, url-detection
|
||
# @PURPOSE: Scan filesystem (repo/build/docker) for prohibited artifacts and external URLs in text files.
|
||
# @LAYER: Domain
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.config_loader
|
||
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.compliance_orchestrator
|
||
# @INVARIANT: Scanner must respect ignore_paths and never modify scanned files.
|
||
# @TEST_CONTRACT: ScanTarget + CleanReleaseConfig -> ScanResult
|
||
# @TEST_FIXTURE: repo_with_test_data -> {"path":"test/data.csv","category":"test-data","classification":"excluded-prohibited"}
|
||
# @TEST_EDGE: binary_file_skip -> binary files must be skipped during URL extraction
|
||
# @TEST_EDGE: symlink_loop -> circular symlinks must not cause infinite recursion
|
||
# @TEST_EDGE: ignore_path_respected -> files in ignore_paths must never appear in results
|
||
# @TEST_INVARIANT: scan_completeness -> VERIFIED_BY: [repo_with_test_data, ignore_path_respected]
|
||
class FilesystemScanner:
|
||
# @PURPOSE: Scan target for prohibited artifacts using prohibited_paths and prohibited_categories.
|
||
# @PRE: Config loaded with prohibited rules.
|
||
# @POST: Returns list of classified artifacts with violations.
|
||
def scan_artifacts(self): ...
|
||
|
||
# @PURPOSE: Extract URLs/hosts from all text files and match against allowed_sources.
|
||
# @PRE: Config loaded with allowed_sources patterns.
|
||
# @POST: Returns list of external endpoint violations.
|
||
def scan_endpoints(self): ...
|
||
# [/DEF:backend.src.services.clean_release.filesystem_scanner:Module]
|
||
|
||
---
|
||
|
||
# [DEF:backend.src.services.clean_release.db_cleanup_executor:Module]
|
||
# @TIER: CRITICAL
|
||
# @SEMANTICS: clean-release, database, cleanup, test-data, enterprise
|
||
# @PURPOSE: Execute database cleanup rules from .clean-release.yaml to remove test users and demo data.
|
||
# @LAYER: Domain
|
||
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.config_loader
|
||
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.compliance_orchestrator
|
||
# @INVARIANT: Preserve-listed records must never be deleted regardless of condition match.
|
||
# @TEST_CONTRACT: DatabaseCleanupConfig -> CleanupResult
|
||
# @TEST_FIXTURE: cleanup_test_users -> {"table":"ab_user","condition":"username IN ('test_user')","preserve":["admin"]}
|
||
# @TEST_EDGE: preserve_overrides_condition -> preserved record matching condition must survive cleanup
|
||
# @TEST_EDGE: empty_tables_list -> enabled=true with empty tables must raise ConfigError
|
||
# @TEST_EDGE: dry_run_mode -> dry run must report planned deletions without executing them
|
||
# @TEST_INVARIANT: preserve_integrity -> VERIFIED_BY: [cleanup_test_users, preserve_overrides_condition]
|
||
class DatabaseCleanupExecutor:
|
||
# @PURPOSE: Execute cleanup rules in dry-run mode first, then optionally apply.
|
||
# @PRE: Database connection and cleanup config available.
|
||
# @POST: Returns cleanup report with deleted/preserved counts per table.
|
||
def execute_cleanup(self): ...
|
||
|
||
# @PURPOSE: Verify that preserve rules are respected post-cleanup.
|
||
# @PRE: Cleanup executed.
|
||
# @POST: Returns validation result confirming preserved records exist.
|
||
def verify_preserves(self): ...
|
||
# [/DEF:backend.src.services.clean_release.db_cleanup_executor:Module]
|
||
|
||
---
|
||
|
||
## Contract Trace (Key User Scenario)
|
||
|
||
Сценарий: оператор запускает TUI-проверку и получает BLOCKED из-за внешнего источника.
|
||
|
||
1. [`clean_release_tui`](#defbackendsrcscriptsclean_release_tuimodule) принимает `F5` и переводит состояние READY -> RUNNING.
|
||
2. [`compliance_orchestrator`](#defbackendsrcservicesclean_releasecompliance_orchestratormodule) запускает stage pipeline.
|
||
3. [`policy_engine`](#defbackendsrcservicesclean_releasepolicy_enginemodule) обнаруживает `external-source` нарушение и возвращает fail stage + violation.
|
||
4. [`compliance_orchestrator`](#defbackendsrcservicesclean_releasecompliance_orchestratormodule) завершает run статусом BLOCKED.
|
||
5. [`report_builder`](#defbackendsrcservicesclean_releasereport_buildermodule) формирует отчёт с remediation.
|
||
6. [`clean_release_tui`](#defbackendsrcscriptsclean_release_tuimodule) отображает BLOCKED + таблицу нарушений + путь восстановления. |