Files
ss-tools/specs/023-clean-repo-enterprise/contracts/modules.md
2026-03-04 19:33:47 +03:00

274 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 + таблицу нарушений + путь восстановления.