Files
ss-tools/specs/025-clean-release-compliance/research.md
2026-03-09 16:52:46 +03:00

6.5 KiB

Phase 0 Research: Clean Release Compliance Subsystem Redesign

Decision 1: The subsystem becomes API/CLI-first and TUI becomes a thin client

Decision
Primary release operations are owned by application services and exposed through CLI and HTTP API first. TUI is retained only as a thin operator interface that reads state and triggers actions through the same facade.

Rationale
The current implementation mixes UI state with preparation, manifest creation and compliance execution. This blocks automation and makes behavior depend on the interface used.

Alternatives considered

  • Keep TUI as primary orchestrator and add wrappers around it: rejected because it preserves hidden business logic inside the interface.
  • Remove TUI entirely: rejected because operators still need an interactive console flow in enterprise environments.

Decision 2: Policy and registry are trusted snapshots, never runtime UI/env payloads

Decision
Policy and source registry are resolved by a dedicated read-only resolution service from trusted stores, then frozen into immutable snapshots for each compliance run.

Rationale
The redesign explicitly separates trusted and untrusted inputs. Candidate input, artifacts JSON and operator choices are not allowed to define policy contents or final report outcomes.

Alternatives considered

  • Continue using .clean-release.yaml and env bootstrap as policy source: rejected because it violates the new trust model.
  • Let TUI construct policy in demo and real mode differently: rejected because it breaks evidence integrity and reproducibility.

Decision 3: Manifest, report and snapshots are immutable; run history is append-only

Decision
DistributionManifest, CleanPolicySnapshot, SourceRegistrySnapshot, ComplianceReport, ApprovalDecision and PublicationRecord are immutable. ComplianceRun, ComplianceStageRun, ComplianceViolation and audit log are append-only once created; only non-terminal run fields may progress during execution.

Rationale
The main value of the subsystem is evidence integrity. Mutable manifest/report records make audit and publication safety unverifiable.

Alternatives considered

  • Update manifest/report in place: rejected because historical evidence would be lost.
  • Allow deleting old runs to keep storage small: rejected because real mode must preserve evidence.

Decision 4: Release lifecycle is modeled as an explicit state machine

Decision
The candidate lifecycle is formalized as DRAFT -> PREPARED -> MANIFEST_BUILT -> CHECK_PENDING -> CHECK_RUNNING -> CHECK_PASSED|CHECK_BLOCKED|CHECK_ERROR -> APPROVED -> PUBLISHED -> REVOKED, with hard guards on forbidden transitions.

Rationale
Current logic spreads status changes across TUI and orchestration code. A formal state machine makes approval/publication gating deterministic and testable.

Alternatives considered

  • Keep loose status updates per module: rejected because it produces hidden invalid states.
  • Collapse all states into a smaller set: rejected because manifest, check and approval stages need separate audit visibility.

Decision 5: Compliance execution is a pluggable stage pipeline integrated with TaskManager

Decision
Each compliance run becomes a TaskManager task. The run stores lifecycle metadata while stage logs are emitted as task logs or structured sub-events. The pipeline remains pluggable with stage-specific decisions and violations.

Rationale
The repository already has a mature async task lifecycle and reporting patterns. Reusing it reduces duplicated orchestration infrastructure and aligns with repository constitution.

Alternatives considered

  • Keep synchronous orchestrator execution: rejected due to non-blocking API requirements.
  • Build a second custom task subsystem inside clean release: rejected as redundant and harder to observe.

Decision 6: Interfaces are split into CLI, REST API and thin TUI over one facade

Decision
A single CleanReleaseFacade exposes use cases for candidate overview, manifest build, compliance run, approval and publication. CLI, API and TUI all call the facade. Headless mode belongs to CLI/API only.

Rationale
A facade keeps interface code thin and prevents re-implementing business rules per entrypoint.

Alternatives considered

  • Let each interface call lower-level services directly: rejected because state validation and DTO assembly would drift.
  • Keep a headless branch inside TUI: rejected because headless is not a UI concern.

Decision 7: Repositories are decomposed by responsibility, even if exposed through one internal facade

Decision
Persistence is split by bounded responsibility: candidate, artifacts, manifest, policy, compliance run, report, approval, publication and audit. A convenience facade may exist, but ownership remains explicit.

Rationale
The current CleanReleaseRepository is too broad for the redesigned evidence model. Explicit repository boundaries make append-only and immutable behavior easier to enforce.

Alternatives considered

  • Keep one universal repository class: rejected because contracts stay ambiguous.
  • Persist everything only through TaskManager: rejected because domain entities need direct retrieval independently of task history.

Decision 8: Demo mode is preserved but isolated by namespace

Decision
Demo mode is handled by a dedicated demo service and isolated storage namespace. Demo runs, policies, candidates and reports never share identifiers or history with real mode.

Rationale
Demo mode remains useful for operator training, but it must not contaminate real compliance evidence.

Alternatives considered

  • Simulate demo behavior inside real storage: rejected because it risks false evidence and operator confusion.
  • Drop demo mode entirely: rejected because it removes a safe training path.

Decision 9: Migration proceeds incrementally, starting by extracting services out of TUI

Decision
Migration starts by extracting build/run logic into new services/facade, then removes env-driven policy injection, then introduces immutable snapshots, then adds CLI/API contracts, and only after that thins the TUI.

Rationale
The current codebase already has working routes, models and tests. A big-bang rewrite would create unnecessary integration risk.

Alternatives considered

  • Rewrite the whole subsystem at once: rejected because it is harder to validate incrementally.
  • Patch TUI only: rejected because it does not solve the architectural problem.