таски готовы

This commit is contained in:
2026-03-09 16:52:46 +03:00
parent d5c1d330f4
commit 0cf02bcf82
11 changed files with 2456 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
# Specification Quality Checklist: Clean Release Compliance Subsystem Redesign
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-03-09
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, file structure) drive the requirements
- [x] Focused on operator value, governance, auditability, and release workflow outcomes
- [x] Written for product/release stakeholders, not only for implementers
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance intent
- [x] User scenarios cover primary lifecycle flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No blocking ambiguity remains for `/speckit.plan`
- [x] Specification is ready for `/speckit.plan` and `/speckit.tasks`
## Notes
- Architectural direction is intentional because the feature itself is a subsystem redesign rather than a small end-user capability.
- Trust model, lifecycle invariants, and immutable evidence were kept at the requirement level because they are the product value of this redesign.

View File

@@ -0,0 +1,714 @@
openapi: 3.1.0
info:
title: Clean Release API
version: 0.1.0
description: API-first contract for clean release candidate lifecycle, compliance runs, approvals and publications.
servers:
- url: /api
x-interface-actor-mapping:
description: External API request payloads use domain-specific actor fields; interface adapters may accept a unified actor context internally but must persist canonical *_by fields.
mappings:
candidate_register: created_by
manifest_build: created_by
compliance_run: requested_by
approval_or_reject: decided_by
publish: published_by
revoke: actor
paths:
/clean-release/candidates:
post:
summary: Register a release candidate
operationId: registerCleanReleaseCandidate
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterCandidateRequest'
responses:
'201':
description: Candidate created
content:
application/json:
schema:
$ref: '#/components/schemas/CandidateOverview'
get:
summary: List release candidates
operationId: listCleanReleaseCandidates
responses:
'200':
description: Candidate list
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/CandidateOverview'
/clean-release/candidates/{candidate_id}:
get:
summary: Get candidate overview
operationId: getCleanReleaseCandidate
parameters:
- $ref: '#/components/parameters/CandidateId'
responses:
'200':
description: Candidate overview
content:
application/json:
schema:
$ref: '#/components/schemas/CandidateOverview'
'404':
$ref: '#/components/responses/NotFound'
/clean-release/candidates/{candidate_id}/artifacts/import:
post:
summary: Import candidate artifacts
operationId: importCleanReleaseArtifacts
parameters:
- $ref: '#/components/parameters/CandidateId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ImportArtifactsRequest'
responses:
'200':
description: Artifacts imported
content:
application/json:
schema:
type: object
required: [candidate_id, imported_count, status]
properties:
candidate_id:
type: string
imported_count:
type: integer
status:
type: string
/clean-release/candidates/{candidate_id}/artifacts:
get:
summary: List candidate artifacts
operationId: listCleanReleaseArtifacts
parameters:
- $ref: '#/components/parameters/CandidateId'
responses:
'200':
description: Candidate artifacts
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/CandidateArtifact'
/clean-release/candidates/{candidate_id}/manifest:
post:
summary: Build a new manifest snapshot
operationId: buildCleanReleaseManifest
parameters:
- $ref: '#/components/parameters/CandidateId'
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
requested_by:
type: string
responses:
'201':
description: Manifest created
content:
application/json:
schema:
$ref: '#/components/schemas/DistributionManifest'
/clean-release/candidates/{candidate_id}/manifests:
get:
summary: List manifests for candidate
operationId: listCleanReleaseManifests
parameters:
- $ref: '#/components/parameters/CandidateId'
responses:
'200':
description: Candidate manifests
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/DistributionManifest'
/clean-release/manifests/{manifest_id}:
get:
summary: Get manifest snapshot
operationId: getCleanReleaseManifest
parameters:
- name: manifest_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Manifest snapshot
content:
application/json:
schema:
$ref: '#/components/schemas/DistributionManifest'
'404':
$ref: '#/components/responses/NotFound'
/clean-release/candidates/{candidate_id}/compliance-runs:
post:
summary: Request a compliance run
operationId: createCleanReleaseComplianceRun
parameters:
- $ref: '#/components/parameters/CandidateId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateComplianceRunRequest'
responses:
'202':
description: Compliance run accepted
content:
application/json:
schema:
$ref: '#/components/schemas/ComplianceRun'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
/clean-release/compliance-runs/{run_id}:
get:
summary: Get compliance run status
operationId: getCleanReleaseComplianceRun
parameters:
- name: run_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Compliance run
content:
application/json:
schema:
$ref: '#/components/schemas/ComplianceRun'
'404':
$ref: '#/components/responses/NotFound'
/clean-release/compliance-runs/{run_id}/stages:
get:
summary: List stage results for a run
operationId: listCleanReleaseComplianceStages
parameters:
- name: run_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Stage list
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/ComplianceStageRun'
/clean-release/compliance-runs/{run_id}/violations:
get:
summary: List violations for a run
operationId: listCleanReleaseComplianceViolations
parameters:
- name: run_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Violation list
content:
application/json:
schema:
type: object
required: [items]
properties:
items:
type: array
items:
$ref: '#/components/schemas/ComplianceViolation'
/clean-release/compliance-runs/{run_id}/report:
get:
summary: Get final report for a run
operationId: getCleanReleaseComplianceReport
parameters:
- name: run_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Compliance report
content:
application/json:
schema:
$ref: '#/components/schemas/ComplianceReport'
'404':
$ref: '#/components/responses/NotFound'
/clean-release/candidates/{candidate_id}/approve:
post:
summary: Approve a candidate using a report
operationId: approveCleanReleaseCandidate
parameters:
- $ref: '#/components/parameters/CandidateId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ApprovalRequest'
responses:
'200':
description: Approval created
content:
application/json:
schema:
$ref: '#/components/schemas/ApprovalDecision'
'409':
$ref: '#/components/responses/Conflict'
/clean-release/candidates/{candidate_id}/reject:
post:
summary: Reject a candidate using a report
operationId: rejectCleanReleaseCandidate
parameters:
- $ref: '#/components/parameters/CandidateId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ApprovalRequest'
responses:
'200':
description: Rejection created
content:
application/json:
schema:
$ref: '#/components/schemas/ApprovalDecision'
'409':
$ref: '#/components/responses/Conflict'
/clean-release/candidates/{candidate_id}/publish:
post:
summary: Publish an approved candidate
operationId: publishCleanReleaseCandidate
parameters:
- $ref: '#/components/parameters/CandidateId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PublishRequest'
responses:
'200':
description: Publication created
content:
application/json:
schema:
$ref: '#/components/schemas/PublicationRecord'
'409':
$ref: '#/components/responses/Conflict'
/clean-release/publications/{publication_id}/revoke:
post:
summary: Revoke a publication
operationId: revokeCleanReleasePublication
parameters:
- name: publication_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RevokePublicationRequest'
responses:
'200':
description: Publication revoked
content:
application/json:
schema:
$ref: '#/components/schemas/PublicationRecord'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
components:
parameters:
CandidateId:
name: candidate_id
in: path
required: true
schema:
type: string
responses:
NotFound:
description: Entity not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Conflict:
description: Illegal lifecycle transition or conflicting state
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
schemas:
RegisterCandidateRequest:
type: object
required: [candidate_id, version, source_snapshot_ref, created_by]
properties:
candidate_id:
type: string
version:
type: string
source_snapshot_ref:
type: string
build_id:
type: string
created_by:
type: string
provenance_ref:
type: string
ImportArtifactsRequest:
type: object
required: [artifacts]
properties:
artifacts:
type: array
items:
$ref: '#/components/schemas/CandidateArtifactInput'
CandidateArtifactInput:
type: object
required: [path, sha256, size]
properties:
path:
type: string
sha256:
type: string
size:
type: integer
declared_category:
type: string
detected_category:
type: string
source_uri:
type: string
source_host:
type: string
metadata:
type: object
additionalProperties: true
CreateComplianceRunRequest:
type: object
required: [requested_by]
properties:
manifest_id:
type: string
requested_by:
type: string
ApprovalRequest:
type: object
required: [report_id, decided_by]
properties:
report_id:
type: string
decided_by:
type: string
comment:
type: string
PublishRequest:
type: object
required: [report_id, target_channel, actor]
properties:
report_id:
type: string
target_channel:
type: string
actor:
type: string
RevokePublicationRequest:
type: object
required: [actor, reason]
properties:
actor:
type: string
reason:
type: string
CandidateOverview:
type: object
required: [candidate_id, version, source_snapshot_ref, status]
properties:
candidate_id:
type: string
version:
type: string
source_snapshot_ref:
type: string
status:
type: string
latest_manifest_id:
type: string
latest_manifest_digest:
type: string
latest_run_id:
type: string
latest_run_status:
type: string
latest_report_id:
type: string
latest_report_final_status:
type: string
latest_policy_snapshot_id:
type: string
latest_policy_version:
type: string
latest_registry_snapshot_id:
type: string
latest_registry_version:
type: string
latest_approval_decision:
type: string
latest_publication_id:
type: string
latest_publication_status:
type: string
CandidateArtifact:
type: object
required: [id, candidate_id, path, sha256, size, metadata]
properties:
id:
type: string
candidate_id:
type: string
path:
type: string
sha256:
type: string
size:
type: integer
detected_category:
type: string
declared_category:
type: string
source_uri:
type: string
source_host:
type: string
metadata:
type: object
additionalProperties: true
DistributionManifest:
type: object
required: [id, candidate_id, manifest_version, manifest_digest, artifacts_digest, created_at, created_by, source_snapshot_ref, content_json, immutable]
properties:
id:
type: string
candidate_id:
type: string
manifest_version:
type: integer
manifest_digest:
type: string
artifacts_digest:
type: string
created_at:
type: string
format: date-time
created_by:
type: string
source_snapshot_ref:
type: string
content_json:
type: object
additionalProperties: true
immutable:
type: boolean
ComplianceRun:
type: object
required: [run_id, candidate_id, status, task_id]
properties:
run_id:
type: string
candidate_id:
type: string
manifest_id:
type: string
manifest_digest:
type: string
policy_snapshot_id:
type: string
registry_snapshot_id:
type: string
requested_by:
type: string
requested_at:
type: string
format: date-time
started_at:
type: string
format: date-time
finished_at:
type: string
format: date-time
status:
type: string
final_status:
type: string
failure_reason:
type: string
report_id:
type: string
task_id:
type: string
ComplianceStageRun:
type: object
required: [id, run_id, stage_name, status, details_json]
properties:
id:
type: string
run_id:
type: string
stage_name:
type: string
status:
type: string
started_at:
type: string
format: date-time
finished_at:
type: string
format: date-time
decision:
type: string
details_json:
type: object
additionalProperties: true
ComplianceViolation:
type: object
required: [id, run_id, stage_name, code, severity, message, evidence_json]
properties:
id:
type: string
run_id:
type: string
stage_name:
type: string
code:
type: string
severity:
type: string
artifact_path:
type: string
artifact_sha256:
type: string
message:
type: string
evidence_json:
type: object
additionalProperties: true
ComplianceReport:
type: object
required: [report_id, run_id, candidate_id, final_status, summary_json, generated_at, immutable]
properties:
report_id:
type: string
run_id:
type: string
candidate_id:
type: string
final_status:
type: string
summary_json:
type: object
additionalProperties: true
generated_at:
type: string
format: date-time
immutable:
type: boolean
ApprovalDecision:
type: object
required: [id, candidate_id, report_id, decision, decided_by, decided_at]
properties:
id:
type: string
candidate_id:
type: string
report_id:
type: string
decision:
type: string
decided_by:
type: string
decided_at:
type: string
format: date-time
comment:
type: string
PublicationRecord:
type: object
required: [id, candidate_id, report_id, published_by, published_at, target_channel, status]
properties:
id:
type: string
candidate_id:
type: string
report_id:
type: string
published_by:
type: string
published_at:
type: string
format: date-time
target_channel:
type: string
publication_ref:
type: string
status:
type: string
ErrorResponse:
type: object
required: [code, message]
properties:
code:
type: string
message:
type: string
details:
type: object
additionalProperties: true

View File

@@ -0,0 +1,78 @@
# CLI Contract: Clean Release Compliance Subsystem Redesign
## Command Groups
### Candidate
```bash
clean-release candidate register --candidate-id <id> --version <version> --source-snapshot <ref> [--build-id <id>] [--provenance-ref <ref>] [--actor <actor>]
clean-release candidate import-artifacts --candidate-id <id> --input <artifacts.json> [--actor <actor>]
clean-release candidate show --candidate-id <id> [--json]
clean-release candidate list [--json]
```
### Manifest
```bash
clean-release manifest build --candidate-id <id> [--actor <actor>] [--json]
clean-release manifest show --manifest-id <id> [--json]
clean-release manifest list --candidate-id <id> [--json]
```
### Compliance
```bash
clean-release compliance run --candidate-id <id> [--manifest-id <manifest_id>] [--actor <actor>] [--json]
clean-release compliance status --run-id <run_id> [--json]
clean-release compliance report --run-id <run_id> [--json]
clean-release compliance violations --run-id <run_id> [--json]
```
### Release
```bash
clean-release release approve --candidate-id <id> --report-id <report_id> --actor <actor> [--comment <text>] [--json]
clean-release release reject --candidate-id <id> --report-id <report_id> --actor <actor> [--comment <text>] [--json]
clean-release release publish --candidate-id <id> --report-id <report_id> --channel <channel> --actor <actor> [--json]
clean-release release revoke --publication-id <publication_id> --actor <actor> --reason <text> [--json]
```
### Demo
```bash
clean-release demo seed [--profile <name>] [--json]
clean-release demo reset [--json]
```
## Output Rules
- Default mode prints concise operator-friendly summaries.
- `--json` prints machine-readable DTO payloads.
- Errors print machine-readable codes and short text to stderr.
- Compliance run creation returns `run_id` and `task_id` immediately.
- If `--manifest-id` is omitted, CLI uses the latest manifest for the candidate or returns invalid input when no manifest exists.
## Actor Mapping Rule
- CLI always accepts external actor context as `--actor`.
- Interface adapters map `--actor` to internal domain fields by action type:
- candidate register -> `created_by`
- manifest build -> `created_by`
- compliance run -> `requested_by`
- release approve/reject -> `decided_by`
- release publish -> `published_by`
- release revoke -> revocation actor field in command payload or audit event
- This mapping is deterministic and hidden from operators; CLI does not expose multiple actor flag names for different commands.
## Exit Codes
- `0`: Passed / successful mutation / successful read.
- `1`: Business blocked (`BLOCKED`, forbidden publish/approve because of valid business rule).
- `2`: Invalid input (`candidate not found`, `manifest missing`, malformed request).
- `3`: System error (`policy store unavailable`, persistence failure, unexpected exception).
## CLI Behavior Constraints
- Business actions are explicit CLI arguments, not env-driven side effects.
- CLI supports headless operation and never requires curses/TTY.
- CLI does not synthesize policy or registry values locally.

View File

@@ -0,0 +1,391 @@
# Module Contracts: Clean Release Compliance Subsystem Redesign
## Backend Domain Models Contract
# [DEF:CleanReleaseDomainModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, domain, lifecycle, immutability, evidence]
# @PURPOSE: Define canonical clean release entities, lifecycle states and evidence invariants for candidate, manifest, run, report, approval and publication records.
# @LAYER: Domain
# @RELATION: DEPENDS_ON -> [DEF:TaskManagerModule]
# @INVARIANT: Immutable snapshots are never mutated after creation; forbidden lifecycle transitions are rejected.
# @PRE: Lifecycle commands reference existing candidate/report/publication identifiers when required.
# @POST: State transitions are valid and terminal evidence remains readable.
# @TEST_CONTRACT: CleanReleaseDomainModule -> {Input: lifecycle command + current aggregate, Output: updated aggregate or domain error}
# @TEST_FIXTURE: candidate_state_machine -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","status":"CHECK_PASSED"}
# @TEST_EDGE: approve_without_passed_report -> reject transition
# @TEST_EDGE: publish_without_approval -> reject transition
# @TEST_EDGE: mutate_existing_manifest -> reject update
# @TEST_INVARIANT: lifecycle_integrity -> VERIFIED_BY: [approve_without_passed_report, publish_without_approval, mutate_existing_manifest]
# [/DEF:CleanReleaseDomainModule]
---
## Backend Clean Release Facade Contract
# [DEF:CleanReleaseFacadeModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, facade, orchestration, dto]
# @PURPOSE: Expose one stable application facade for CLI, TUI, REST API and future Web UI over the clean release lifecycle.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:CandidatePreparationServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:ManifestServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:PolicyResolutionServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:ComplianceExecutionServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:ApprovalServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:PublicationServiceModule]
# @INVARIANT: Interface adapters never bypass facade for business mutations.
# @PRE: Incoming commands are validated and include actor context.
# @POST: Returns DTOs with enough data for thin clients to render without local business recomputation.
# @TEST_CONTRACT: CleanReleaseFacadeModule -> {Input: validated command, Output: CandidateOverviewDTO|ManifestDTO|ComplianceRunDTO|ReportDTO|ApprovalDTO|PublicationDTO}
# @TEST_FIXTURE: facade_happy_path -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","actor":"release-bot"}
# @TEST_EDGE: missing_candidate -> returns explicit not-found error
# @TEST_EDGE: illegal_transition -> returns transition error
# @TEST_EDGE: missing_policy_snapshot -> returns trusted-source error
# @TEST_INVARIANT: thin_client_boundary -> VERIFIED_BY: [missing_candidate, illegal_transition, missing_policy_snapshot]
# [/DEF:CleanReleaseFacadeModule]
---
## Backend Candidate Preparation Service Contract
# [DEF:CandidatePreparationServiceModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, candidate, artifacts, preparation]
# @PURPOSE: Register release candidates, import artifacts, validate artifact sets and progress candidates to PREPARED.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:CleanReleaseDomainModule]
# @RELATION: DEPENDS_ON -> [DEF:CandidateRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:ArtifactRepositoryModule]
# @INVARIANT: Candidate preparation does not run compliance checks or synthesize manifests implicitly.
# @PRE: Candidate identifiers are unique and artifact payloads are structurally valid.
# @POST: Candidate and artifact records are persisted and candidate status advances only through allowed states.
# @TEST_CONTRACT: CandidatePreparationServiceModule -> {Input: candidate payload + artifacts, Output: candidate aggregate}
# @TEST_FIXTURE: prepared_candidate -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","artifacts":[{"path":"dist/app.whl","sha256":"abc"}]}
# @TEST_EDGE: duplicate_candidate_id -> reject create
# @TEST_EDGE: malformed_artifact_payload -> reject import
# @TEST_EDGE: empty_artifact_set -> reject mark_prepared
# @TEST_INVARIANT: candidate_input_integrity -> VERIFIED_BY: [duplicate_candidate_id, malformed_artifact_payload, empty_artifact_set]
# [/DEF:CandidatePreparationServiceModule]
---
## Backend Manifest Service Contract
# [DEF:ManifestServiceModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, manifest, digest, immutability]
# @PURPOSE: Build immutable manifest snapshots and return latest manifest views for candidates.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:CandidateRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:ArtifactRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:ManifestRepositoryModule]
# @INVARIANT: Existing manifest versions remain immutable; rebuild creates a new version.
# @PRE: Candidate exists and artifact set is valid for manifest generation.
# @POST: New manifest digest is deterministic for the selected artifact set.
# @TEST_CONTRACT: ManifestServiceModule -> {Input: candidate_id, Output: DistributionManifest}
# @TEST_FIXTURE: manifest_rebuild -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","manifest_version":1}
# @TEST_EDGE: build_without_candidate -> reject request
# @TEST_EDGE: build_with_changed_artifacts -> create new version
# @TEST_EDGE: overwrite_existing_manifest -> reject mutation
# @TEST_INVARIANT: manifest_snapshot_integrity -> VERIFIED_BY: [build_without_candidate, build_with_changed_artifacts, overwrite_existing_manifest]
# [/DEF:ManifestServiceModule]
---
## Backend Policy Resolution Service Contract
# [DEF:PolicyResolutionServiceModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, policy, registry, trust_model]
# @PURPOSE: Resolve active policy and source registry from trusted read-only stores into immutable snapshots.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:PolicySnapshotRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:TrustedPolicyStoreModule]
# @INVARIANT: No interface-provided payload may alter resolved policy or registry contents.
# @PRE: Requested profile exists in trusted store.
# @POST: Returns immutable policy snapshot and linked registry snapshot.
# @TEST_CONTRACT: PolicyResolutionServiceModule -> {Input: profile, Output: CleanPolicySnapshot + SourceRegistrySnapshot}
# @TEST_FIXTURE: trusted_default_profile -> INLINE_JSON {"profile":"default"}
# @TEST_EDGE: missing_profile -> reject request
# @TEST_EDGE: registry_missing -> reject request
# @TEST_EDGE: ui_override_attempt -> ignore override and fail validation
# @TEST_INVARIANT: trusted_input_boundary -> VERIFIED_BY: [missing_profile, registry_missing, ui_override_attempt]
# [/DEF:PolicyResolutionServiceModule]
---
## Backend Compliance Execution Service Contract
# [DEF:ComplianceExecutionServiceModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, compliance, task_manager, stages, report]
# @PURPOSE: Create compliance runs, bind them to TaskManager, execute stage pipeline, persist stage results, violations and final reports.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:PolicyResolutionServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:StagePipelineModule]
# @RELATION: DEPENDS_ON -> [DEF:ComplianceRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:ReportRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:AuditServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:TaskManagerModule]
# @INVARIANT: A compliance request becomes exactly one run and at most one final immutable report.
# @PRE: Candidate and manifest exist; trusted snapshots are resolvable.
# @POST: Run state, stage records, violations and report are mutually consistent.
# @POST: API-facing request method is non-blocking and returns run/task identifiers before stage completion.
# @TEST_CONTRACT: ComplianceExecutionServiceModule -> {Input: candidate_id + manifest_id + actor, Output: ComplianceRunDTO or ComplianceReport}
# @TEST_FIXTURE: blocking_run -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","manifest_id":"man-001"}
# @TEST_EDGE: run_without_manifest -> reject request
# @TEST_EDGE: task_crash_mid_run -> final_status ERROR with preserved partial evidence
# @TEST_EDGE: blocked_violation_without_report -> reject finalization
# @TEST_INVARIANT: run_report_consistency -> VERIFIED_BY: [run_without_manifest, task_crash_mid_run, blocked_violation_without_report]
# [/DEF:ComplianceExecutionServiceModule]
---
## Backend Stage Pipeline Contract
# [DEF:StagePipelineModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, stages, pluggable_pipeline]
# @PURPOSE: Provide pluggable compliance stages with deterministic ordering and structured results.
# @LAYER: Domain
# @RELATION: CALLED_BY -> [DEF:ComplianceExecutionServiceModule]
# @INVARIANT: Mandatory stages execute in stable order unless run stops on terminal error policy.
# @PRE: Compliance context contains candidate, manifest, policy snapshot and registry snapshot.
# @POST: Each stage returns decision, violations and details without mutating trusted snapshots.
# [/DEF:StagePipelineModule]
---
## Backend Approval Service Contract
# [DEF:ApprovalServiceModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, approval, gate]
# @PURPOSE: Approve or reject candidates based on completed compliance reports.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:ReportRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:ApprovalRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:AuditServiceModule]
# @INVARIANT: Approval is impossible without a valid PASSED report; latest approval decision governs publication gate.
# @PRE: Candidate exists and referenced report belongs to the candidate.
# @POST: Approval or rejection decision is persisted immutably; rejection blocks publication gate without mutating compliance evidence.
# @TEST_CONTRACT: ApprovalServiceModule -> {Input: candidate_id + report_id + actor, Output: ApprovalDecision}
# @TEST_FIXTURE: passed_report -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","report_id":"rpt-001","final_status":"PASSED"}
# @TEST_EDGE: approve_blocked_report -> reject request
# @TEST_EDGE: approve_foreign_report -> reject request
# @TEST_EDGE: duplicate_approve_terminal_state -> reject or preserve existing state deterministically
# @TEST_EDGE: reject_then_publish -> publish remains blocked until a later valid approve
# @TEST_INVARIANT: approval_gate_integrity -> VERIFIED_BY: [approve_blocked_report, approve_foreign_report, duplicate_approve_terminal_state, reject_then_publish]
# [/DEF:ApprovalServiceModule]
---
## Backend Publication Service Contract
# [DEF:PublicationServiceModule:Module]
# @TIER: CRITICAL
# @SEMANTICS: [clean_release, publication, revoke]
# @PURPOSE: Publish approved candidates to target channels and revoke publications without deleting historical evidence.
# @LAYER: Application
# @RELATION: DEPENDS_ON -> [DEF:ApprovalServiceModule]
# @RELATION: DEPENDS_ON -> [DEF:PublicationRepositoryModule]
# @RELATION: DEPENDS_ON -> [DEF:AuditServiceModule]
# @INVARIANT: Publication is impossible without candidate approval; revoke does not erase original publication record.
# @PRE: Candidate is approved and report is the approved basis for publication.
# @POST: Publication or revocation record is persisted immutably with channel and actor context.
# @TEST_CONTRACT: PublicationServiceModule -> {Input: candidate_id + report_id + channel + actor, Output: PublicationRecord}
# @TEST_FIXTURE: approved_candidate -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","status":"APPROVED"}
# @TEST_EDGE: publish_without_approval -> reject request
# @TEST_EDGE: revoke_unknown_publication -> reject request
# @TEST_EDGE: republish_after_revoke -> deterministic policy required
# @TEST_INVARIANT: publication_gate_integrity -> VERIFIED_BY: [publish_without_approval, revoke_unknown_publication, republish_after_revoke]
# [/DEF:PublicationServiceModule]
---
## Backend Audit Service Contract
# [DEF:AuditServiceModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, audit, append_only]
# @PURPOSE: Persist append-only audit events for release lifecycle and compliance execution.
# @LAYER: Infra
# @RELATION: CALLED_BY -> [DEF:CandidatePreparationServiceModule]
# @RELATION: CALLED_BY -> [DEF:ManifestServiceModule]
# @RELATION: CALLED_BY -> [DEF:ComplianceExecutionServiceModule]
# @RELATION: CALLED_BY -> [DEF:ApprovalServiceModule]
# @RELATION: CALLED_BY -> [DEF:PublicationServiceModule]
# @INVARIANT: Audit records are append-only in real mode.
# @PRE: Event context contains actor and operation identifiers.
# @POST: One structured audit event is persisted per critical lifecycle mutation.
# [/DEF:AuditServiceModule]
---
## Backend Repository Contracts
# [DEF:CandidateRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, candidate]
# @PURPOSE: Persist and query release candidates and candidate overview projections.
# @LAYER: Infra
# @INVARIANT: Candidate writes honor lifecycle guards defined in the domain module.
# [/DEF:CandidateRepositoryModule]
# [DEF:ArtifactRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, artifacts]
# @PURPOSE: Persist and query candidate artifacts with checksum metadata.
# @LAYER: Infra
# @INVARIANT: Artifact checksum/path records remain stable after import.
# [/DEF:ArtifactRepositoryModule]
# [DEF:ManifestRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, manifest]
# @PURPOSE: Persist immutable manifests and provide latest-version lookup.
# @LAYER: Infra
# @INVARIANT: Existing manifest versions are read-only.
# [/DEF:ManifestRepositoryModule]
# [DEF:PolicySnapshotRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, policy_snapshot]
# @PURPOSE: Persist immutable policy and registry snapshots used by runs.
# @LAYER: Infra
# @INVARIANT: Snapshot content cannot be mutated after persistence.
# [/DEF:PolicySnapshotRepositoryModule]
# [DEF:ComplianceRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, run, stage, violation]
# @PURPOSE: Persist compliance runs, stage records and violations.
# @LAYER: Infra
# @INVARIANT: Historical run evidence is append-only in real mode.
# [/DEF:ComplianceRepositoryModule]
# [DEF:ReportRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, report]
# @PURPOSE: Persist immutable compliance reports and support report lookup by run and candidate.
# @LAYER: Infra
# @INVARIANT: Completed reports remain immutable.
# [/DEF:ReportRepositoryModule]
# [DEF:ApprovalRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, approval]
# @PURPOSE: Persist immutable approval decisions and query latest decision state.
# @LAYER: Infra
# @INVARIANT: Approval decisions are historical facts, not mutable flags.
# [/DEF:ApprovalRepositoryModule]
# [DEF:PublicationRepositoryModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, repository, publication]
# @PURPOSE: Persist publication and revocation records.
# @LAYER: Infra
# @INVARIANT: Publication history is append-only.
# [/DEF:PublicationRepositoryModule]
# [DEF:TrustedPolicyStoreModule:Module]
# @TIER: STANDARD
# @SEMANTICS: [clean_release, trusted_store, policy]
# @PURPOSE: Abstract the trusted read-only source of policies and source registries.
# @LAYER: Infra
# @INVARIANT: Store reads are side-effect free for clean release operations.
# [/DEF:TrustedPolicyStoreModule]
---
## Backend REST API Contract
# [DEF:CleanReleaseApiContract:Module]
# @TIER: CRITICAL
# @SEMANTICS: [api, clean_release, async, dto]
# @PURPOSE: Define HTTP contract for candidate lifecycle, manifests, compliance runs, approvals and publications.
# @LAYER: Interface
# @RELATION: DEPENDS_ON -> [DEF:CleanReleaseFacadeModule]
# @RELATION: IMPLEMENTS -> [DEF:Std:API_FastAPI]
# @INVARIANT: Long-running compliance execution endpoints are non-blocking and machine-readable.
# @PRE: Request is authenticated and actor context is available.
# @POST: Mutation endpoints return canonical DTOs or explicit validation/system errors.
# @POST: Compliance run creation returns run_id and task_id without waiting for final completion; latest manifest may be resolved server-side when not explicitly provided.
# @TEST_CONTRACT: CleanReleaseApiContract -> {Input: HTTP request, Output: JSON DTO or machine-readable error}
# @TEST_FIXTURE: api_candidate_create -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","version":"1.2.0"}
# @TEST_EDGE: invalid_transition_http -> 409 conflict
# @TEST_EDGE: missing_candidate_http -> 404 not found
# @TEST_EDGE: invalid_input_http -> 422 validation error
# @TEST_EDGE: reject_without_passed_report_http -> 409 conflict
# @TEST_INVARIANT: api_contract_stability -> VERIFIED_BY: [invalid_transition_http, missing_candidate_http, invalid_input_http, reject_without_passed_report_http]
# [/DEF:CleanReleaseApiContract]
---
## Backend CLI Contract
# [DEF:CleanReleaseCliContract:Module]
# @TIER: CRITICAL
# @SEMANTICS: [cli, clean_release, headless]
# @PURPOSE: Provide headless command-line access to candidate lifecycle, compliance, approval, publication and demo seed/reset flows.
# @LAYER: Interface
# @RELATION: DEPENDS_ON -> [DEF:CleanReleaseFacadeModule]
# @INVARIANT: CLI exit codes distinguish passed, blocked, invalid input and system error outcomes.
# @PRE: User provides explicit command arguments for business actions.
# @POST: CLI emits human-readable output by default and JSON output when requested.
# @TEST_CONTRACT: CleanReleaseCliContract -> {Input: argv, Output: stdout/stderr + exit code}
# @TEST_FIXTURE: cli_run_json -> INLINE_JSON {"argv":["clean-release","compliance","run","--candidate-id","2026.03.09-rc1","--json"]}
# @TEST_EDGE: cli_missing_manifest -> exit code 2
# @TEST_EDGE: cli_blocked_run -> exit code 1
# @TEST_EDGE: cli_system_error -> exit code 3
# @TEST_INVARIANT: cli_headless_integrity -> VERIFIED_BY: [cli_missing_manifest, cli_blocked_run, cli_system_error]
# [/DEF:CleanReleaseCliContract]
---
## TUI Thin Client Contract
<!-- [DEF:CleanReleaseTuiApp:Component] -->
/**
* @TIER: CRITICAL
* @SEMANTICS: [tui, thin_client, operator, live_status]
* @PURPOSE: Render current clean release overview and trigger facade actions without owning business logic.
* @LAYER: UI
* @RELATION: DEPENDS_ON -> [DEF:CleanReleaseFacadeModule]
* @INVARIANT: TUI never constructs policy, registry, manifest or fake run state locally.
* @PRE: Running terminal has a valid TTY; candidate context is resolvable.
* @POST: Operator can build manifest, run compliance, approve and publish only when transitions are valid.
* @UX_STATE: Loading -> Candidate overview is refreshing and action keys are temporarily disabled.
* @UX_STATE: Ready -> Candidate summary, latest manifest, latest run and latest report are visible.
* @UX_STATE: Running -> Current stage and task/run progress are visible from real task events.
* @UX_STATE: Blocked -> Violations list and blocking reason are visually dominant; approve/publish disabled.
* @UX_STATE: Error -> Failure reason is explicit and no hidden retry occurs.
* @UX_FEEDBACK: F5/F6/F8/F9 actions show immediate command acknowledgment and then refresh from persisted DTOs.
* @UX_RECOVERY: Operator can refresh, inspect violations, rebuild manifest or rerun compliance after fixing candidate inputs.
* @TEST_CONTRACT: CleanReleaseTuiApp -> {Input: keypress + facade DTOs, Output: rendered state + triggered facade action}
* @TEST_FIXTURE: tui_ready_candidate -> INLINE_JSON {"candidate_id":"2026.03.09-rc1","status":"MANIFEST_BUILT"}
* @TEST_EDGE: no_tty_environment -> refuse startup and redirect to CLI
* @TEST_EDGE: missing_manifest_on_F5 -> inline blocking message
* @TEST_EDGE: blocked_report_on_F8 -> approve action disabled
* @TEST_INVARIANT: tui_thin_client_boundary -> VERIFIED_BY: [no_tty_environment, missing_manifest_on_F5, blocked_report_on_F8]
*/
<!-- [/DEF:CleanReleaseTuiApp] -->
---
## Contract Usage Simulation (Key Scenario)
Scenario traced: operator runs compliance from TUI after building a manifest.
1. `CleanReleaseTuiApp` requests `CandidateOverviewDTO` from `CleanReleaseFacadeModule`.
2. Operator presses `F5`.
3. `CleanReleaseFacadeModule` calls `ComplianceExecutionServiceModule.request_run(...)`.
4. `PolicyResolutionServiceModule` resolves trusted snapshots.
5. `ComplianceExecutionServiceModule` creates a run, binds it to `TaskManagerModule`, returns `run_id` and `task_id` immediately.
6. `StagePipelineModule` emits ordered stage results and violations.
7. `AuditServiceModule` persists append-only events.
8. `ReportRepositoryModule` persists immutable final report on terminal completion.
9. TUI refreshes and renders persisted DTOs without local recomputation.
Continuity check: no interface mismatch found across facade, services and thin-client rendering path.

View File

@@ -0,0 +1,359 @@
# Data Model: Clean Release Compliance Subsystem Redesign
**Feature**: [`025-clean-release-compliance`](specs/025-clean-release-compliance)
**Spec**: [`spec.md`](specs/025-clean-release-compliance/spec.md)
**Research**: [`research.md`](specs/025-clean-release-compliance/research.md)
## 1. Entity: ReleaseCandidate
Represents the release unit that is being prepared, checked, approved and published.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Stable candidate identifier. |
| version | string | Yes | Release version label. |
| source_snapshot_ref | string | Yes | Reference to code/source snapshot used to create candidate. |
| build_id | string | No | Upstream build or pipeline identifier. |
| created_at | datetime | Yes | Candidate creation timestamp. |
| created_by | string | Yes | Actor that created the candidate. |
| provenance_ref | string | No | Optional provenance or attestation reference. |
| status | enum | Yes | Current lifecycle state. |
### Validation Rules
- `id`, `version`, `source_snapshot_ref`, `created_at`, `created_by`, `status` must be present.
- `status` must be one of: `DRAFT`, `PREPARED`, `MANIFEST_BUILT`, `CHECK_PENDING`, `CHECK_RUNNING`, `CHECK_PASSED`, `CHECK_BLOCKED`, `CHECK_ERROR`, `APPROVED`, `PUBLISHED`, `REVOKED`.
- Terminal publication evidence cannot exist without a valid candidate.
### Lifecycle / State Transitions
- `DRAFT -> PREPARED`
- `PREPARED -> MANIFEST_BUILT`
- `MANIFEST_BUILT -> CHECK_PENDING`
- `CHECK_PENDING -> CHECK_RUNNING`
- `CHECK_RUNNING -> CHECK_PASSED`
- `CHECK_RUNNING -> CHECK_BLOCKED`
- `CHECK_RUNNING -> CHECK_ERROR`
- `CHECK_PASSED -> APPROVED`
- `APPROVED -> PUBLISHED`
- `PUBLISHED -> REVOKED`
---
## 2. Entity: CandidateArtifact
Represents one artifact associated with a release candidate.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Stable artifact identifier. |
| candidate_id | string | Yes | Owning release candidate. |
| path | string | Yes | Artifact path or logical location. |
| sha256 | string | Yes | Artifact checksum. |
| size | integer | Yes | Artifact size in bytes. |
| detected_category | string | No | Category determined by system or pipeline. |
| declared_category | string | No | Category declared by external input. |
| source_uri | string | No | Artifact source URI if known. |
| source_host | string | No | Parsed source host if known. |
| metadata | object | Yes | Additional attributes. |
### Validation Rules
- `sha256` must be present and stable for the artifact content.
- `size >= 0`.
- `declared_category` and `detected_category` may differ, but the difference must remain observable.
---
## 3. Entity: DistributionManifest
Immutable snapshot describing the candidate payload selected for distribution.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Stable manifest identifier. |
| candidate_id | string | Yes | Owning release candidate. |
| manifest_version | integer | Yes | Monotonic manifest version per candidate. |
| manifest_digest | string | Yes | Overall digest of manifest content. |
| artifacts_digest | string | Yes | Digest over artifact selection. |
| created_at | datetime | Yes | Manifest creation time. |
| created_by | string | Yes | Actor that built the manifest. |
| source_snapshot_ref | string | Yes | Source snapshot bound to the manifest. |
| content_json | object | Yes | Serialized immutable manifest content. |
| immutable | boolean | Yes | Must be `true` after creation. |
### Validation Rules
- `manifest_version >= 1` and strictly increases for a candidate.
- Existing manifest content cannot be overwritten.
- `manifest_digest` must uniquely reflect `content_json`.
---
## 4. Entity: CleanPolicySnapshot
Immutable policy snapshot used to evaluate a run.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Snapshot identifier. |
| policy_id | string | Yes | Logical policy identifier in trusted store. |
| policy_version | string | Yes | Trusted policy version. |
| created_at | datetime | Yes | Snapshot creation time. |
| content_json | object | Yes | Frozen policy content. |
| registry_snapshot_id | string | Yes | Bound source registry snapshot. |
| immutable | boolean | Yes | Must be `true`. |
### Validation Rules
- Snapshot must reference a valid registry snapshot.
- UI/env input cannot mutate `content_json` after creation.
---
## 5. Entity: SourceRegistrySnapshot
Immutable registry snapshot for allowed sources and schemes.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Snapshot identifier. |
| registry_id | string | Yes | Logical trusted registry identifier. |
| registry_version | string | Yes | Trusted registry version. |
| created_at | datetime | Yes | Snapshot creation time. |
| allowed_hosts | array[string] | Yes | Allowed hosts or host patterns. |
| allowed_schemes | array[string] | Yes | Allowed URL schemes. |
| allowed_source_types | array[string] | Yes | Allowed source type labels. |
| immutable | boolean | Yes | Must be `true`. |
---
## 6. Entity: ComplianceRun
Operational record for one compliance execution request.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Run identifier. |
| candidate_id | string | Yes | Candidate being checked. |
| manifest_id | string | Yes | Manifest used for the run. |
| manifest_digest | string | Yes | Manifest digest copied at request time. |
| policy_snapshot_id | string | Yes | Policy snapshot used. |
| registry_snapshot_id | string | Yes | Registry snapshot used. |
| requested_by | string | Yes | Actor that requested the run. |
| requested_at | datetime | Yes | Request time. |
| started_at | datetime | No | Execution start time. |
| finished_at | datetime | No | Execution finish time. |
| status | enum | Yes | Execution status. |
| final_status | enum | No | Final compliance result. |
| failure_reason | string | No | System or validation failure summary. |
| task_id | string | No | Linked async task identifier. |
### Validation Rules
- `status` must be one of `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`.
- `final_status`, when present, must be one of `PASSED`, `BLOCKED`, `ERROR`.
- `task_id` is mutable only until execution binding is established.
---
## 7. Entity: ComplianceStageRun
Stage-level execution record inside one run.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Stage run identifier. |
| run_id | string | Yes | Owning compliance run. |
| stage_name | string | Yes | Canonical stage name. |
| status | string | Yes | Stage execution status. |
| started_at | datetime | No | Stage start time. |
| finished_at | datetime | No | Stage finish time. |
| decision | string | No | `PASSED`, `BLOCKED`, or `ERROR`. |
| details_json | object | Yes | Structured stage details. |
---
## 8. Entity: ComplianceViolation
Violation produced by one stage within one run.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Violation identifier. |
| run_id | string | Yes | Owning compliance run. |
| stage_name | string | Yes | Stage that detected the issue. |
| code | string | Yes | Canonical violation code. |
| severity | string | Yes | Severity label. |
| artifact_path | string | No | Related artifact path. |
| artifact_sha256 | string | No | Related artifact checksum. |
| message | string | Yes | Human-readable explanation. |
| evidence_json | object | Yes | Structured evidence. |
### Validation Rules
- Violations cannot be deleted in real mode.
- `code`, `severity`, `message` must be present.
---
## 9. Entity: ComplianceReport
Immutable result derived from a completed run.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Report identifier. |
| run_id | string | Yes | Source compliance run. |
| candidate_id | string | Yes | Candidate checked by this run. |
| final_status | string | Yes | Final outcome. |
| summary_json | object | Yes | Structured summary of stages and violations. |
| generated_at | datetime | Yes | Report generation time. |
| immutable | boolean | Yes | Must be `true`. |
---
## 10. Entity: ApprovalDecision
Approval or rejection bound to a candidate and report.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Decision identifier. |
| candidate_id | string | Yes | Candidate being decided. |
| report_id | string | Yes | Report used as approval basis. |
| decision | string | Yes | `APPROVED` or `REJECTED`. |
| decided_by | string | Yes | Actor making the decision. |
| decided_at | datetime | Yes | Decision timestamp. |
| comment | string | No | Optional operator note. |
---
## 11. Entity: PublicationRecord
Publication or revocation record bound to a candidate.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Publication identifier. |
| candidate_id | string | Yes | Published candidate. |
| report_id | string | Yes | Approved report used as basis. |
| published_by | string | Yes | Publishing actor. |
| published_at | datetime | Yes | Publish time. |
| target_channel | string | Yes | Target release channel. |
| publication_ref | string | No | External publish reference. |
| status | string | Yes | Publication status label. |
---
## 12. Entity: CandidateOverviewDTO
Read model used by CLI, TUI and API to show current candidate state.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| candidate_id | string | Yes | Candidate identifier. |
| version | string | Yes | Candidate version. |
| source_snapshot_ref | string | Yes | Source snapshot ref. |
| status | string | Yes | Current lifecycle status. |
| latest_manifest_id | string | No | Latest manifest identifier. |
| latest_manifest_digest | string | No | Latest manifest digest. |
| latest_run_id | string | No | Latest run identifier. |
| latest_run_status | string | No | Latest run execution status. |
| latest_report_id | string | No | Latest report identifier. |
| latest_report_final_status | string | No | Latest report final status. |
| latest_policy_snapshot_id | string | No | Latest policy snapshot identifier. |
| latest_policy_version | string | No | Latest policy version used for latest run. |
| latest_registry_snapshot_id | string | No | Latest registry snapshot identifier. |
| latest_registry_version | string | No | Latest registry version used for latest run. |
| latest_approval_decision | string | No | Latest approval decision affecting publication gate. |
| latest_publication_id | string | No | Latest publication identifier. |
| latest_publication_status | string | No | Latest publication status. |
---
## 13. Entity: ComplianceRunDTO
Read model for run status tracking.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| run_id | string | Yes | Run identifier. |
| candidate_id | string | Yes | Candidate identifier. |
| status | string | Yes | Run execution status. |
| final_status | string | No | Final compliance result. |
| report_id | string | No | Final report identifier. |
| task_id | string | Yes | Linked task identifier. |
---
## 14. Entity: ReportDTO
Compact report view used by interfaces.
### Fields
| Field | Type | Required | Description |
|---|---|---|---|
| report_id | string | Yes | Report identifier. |
| candidate_id | string | Yes | Candidate identifier. |
| final_status | string | Yes | Final report result. |
| policy_version | string | Yes | Policy version used for the report. |
| manifest_digest | string | Yes | Manifest digest used for the run. |
| violation_count | integer | Yes | Number of violations. |
| generated_at | datetime | Yes | Report generation time. |
---
## 15. Relationships
- `ReleaseCandidate 1 -> N CandidateArtifact`
- `ReleaseCandidate 1 -> N DistributionManifest`
- `ReleaseCandidate 1 -> N ComplianceRun`
- `ComplianceRun 1 -> N ComplianceStageRun`
- `ComplianceRun 1 -> N ComplianceViolation`
- `ComplianceRun 1 -> 1 ComplianceReport` (for terminal runs that produce a report)
- `ComplianceReport 1 -> N ApprovalDecision` (history allowed, latest valid decision governs)
- `ReleaseCandidate 1 -> N PublicationRecord`
- `CleanPolicySnapshot 1 -> 1 SourceRegistrySnapshot`
## 16. Invariants
- Policy, registry, manifest, report, approval and publication snapshots are immutable after creation.
- Real mode compliance evidence is append-only and not user-deletable.
- `APPROVED` requires a valid `PASSED` report.
- `PUBLISHED` requires a valid approval.
- `REJECTED` is modeled as an immutable latest approval decision, not as a separate candidate lifecycle state.
- Demo namespace never shares identifiers or historical storage with real mode.
## 17. Scale Assumptions
- Candidate histories may accumulate multiple manifests and multiple compliance runs per release.
- A single run may produce many violations and stage details, so read models should not require loading all artifacts for each overview request.
- Most interface reads use latest-only summary views, while audit and compliance screens require full history retrieval.

View File

@@ -0,0 +1,163 @@
# Implementation Plan: Clean Release Compliance Subsystem Redesign
**Branch**: `025-clean-release-compliance` | **Date**: 2026-03-09 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from [`/specs/025-clean-release-compliance/spec.md`](./spec.md)
## Summary
Перевести текущую clean-release подсистему из TUI-first orchestration в API/CLI-first release/compliance subsystem с четырьмя жёстко разделёнными слоями:
1. domain model для candidate, manifest, policy snapshot, run, report, approval, publication;
2. application services для candidate preparation, manifest build, policy resolution, compliance execution, approval, publication и audit;
3. infrastructure layer для repositories, trusted policy access, artifact storage, audit storage и task execution;
4. thin interfaces для CLI, TUI, REST API и будущего Web UI.
Ключевой результат: TUI перестаёт быть местом, где живёт release logic. Все критические действия выполняются единым facade/application layer, compliance evidence становится immutable/append-only, а long-running runs интегрируются с существующим TaskManager.
## Technical Context
**Language/Version**: Python 3.9+ for backend services, CLI, API and TUI
**Primary Dependencies**: FastAPI, Pydantic models, existing `TaskManager`, existing reports service patterns, repository adapters, curses-compatible TUI runtime
**Storage**: PostgreSQL for metadata and snapshots, filesystem/object storage for artifacts and persisted reports, trusted policy/registry store (read-only source)
**Testing**: pytest unit/integration/contract suites, CLI smoke tests, API contract checks, TUI facade smoke tests
**Target Platform**: Linux server and CI pipeline in enterprise environment
**Project Type**: Backend web service with operational CLI/TUI interfaces
**Performance Goals**:
- request to start compliance returns run/task identifiers in <= 2 seconds for a typical candidate;
- candidate overview and status reads return in <= 1 second for the latest candidate history;
- final report becomes available immediately after terminal run finalization.
**Constraints**:
- policy and registry are trusted read-only inputs, never sourced from TUI/env bootstrap payloads;
- trusted policy store location, active profile selection, mode switching and storage wiring must be resolved through existing `ConfigManager` access patterns rather than ad hoc constants or raw env reads;
- long-running compliance execution must use `TaskManager` and non-blocking API behavior;
- real mode audit history and compliance evidence are append-only;
- TUI must remain usable for operators but cannot contain hidden business logic;
- migration should be incremental because existing code already exposes `clean_release` routes, services and a TUI entrypoint.
**Scale/Scope**:
- tens to hundreds of release candidates per month;
- each candidate may include thousands of artifacts;
- multiple compliance runs and reports may exist for one candidate;
- redesign touches backend domain, API, CLI, TUI and operational documentation.
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
1. **Semantic Protocol Compliance**: All new modules in this plan are defined via explicit contracts in [`contracts/modules.md`](./contracts/modules.md). Code implementation must follow `[DEF]`, `@PRE`, `@POST`, testing tags and closing anchors. Status: PASS.
2. **Modular Architecture**: Business logic is moved into `backend/src/services/clean_release/` services and repository adapters; interfaces remain thin. Status: PASS.
3. **Independent Testability**: Spec defines independent tests for each user story; tasks will preserve isolated validation paths. Status: PASS.
4. **Asynchronous Execution**: Compliance run execution is explicitly mapped to `TaskManager`, and API launch endpoints are non-blocking. Status: PASS.
5. **Config Discipline**: Policy and registry sources are moved out of ad hoc env/bootstrap input into trusted resolution services. Status: PASS with migration note that existing env-based flows must be deprecated, not silently retained.
6. **Centralized Config**: Trusted store endpoints, mode/profile selection and storage wiring must reuse `ConfigManager` / `get_config_manager()` patterns required by repository constitution. Status: PASS with explicit implementation follow-up in foundational tasks.
## Project Structure
### Documentation (this feature)
```text
specs/025-clean-release-compliance/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── ux_reference.md
├── contracts/
│ ├── modules.md
│ ├── clean-release-api.openapi.yaml
│ └── cli.md
├── checklists/
│ └── requirements.md
└── tasks.md
```
### Source Code (repository root)
```text
backend/
├── src/
│ ├── api/routes/clean_release.py
│ ├── dependencies.py
│ ├── models/clean_release.py
│ ├── scripts/
│ │ ├── clean_release_cli.py
│ │ └── clean_release_tui.py
│ └── services/clean_release/
│ ├── __init__.py
│ ├── facade.py
│ ├── enums.py
│ ├── exceptions.py
│ ├── dto.py
│ ├── mappers.py
│ ├── candidate_service.py
│ ├── manifest_service.py
│ ├── policy_resolution_service.py
│ ├── compliance_execution_service.py
│ ├── approval_service.py
│ ├── publication_service.py
│ ├── audit_service.py
│ ├── demo_data_service.py
│ ├── stages/
│ │ ├── base.py
│ │ ├── data_purity.py
│ │ ├── internal_sources_only.py
│ │ ├── no_external_endpoints.py
│ │ └── manifest_consistency.py
│ └── repositories/
│ ├── candidate_repository.py
│ ├── artifact_repository.py
│ ├── manifest_repository.py
│ ├── policy_repository.py
│ ├── compliance_repository.py
│ ├── report_repository.py
│ ├── approval_repository.py
│ ├── publication_repository.py
│ └── audit_repository.py
└── tests/
├── api/routes/
├── scripts/
└── services/clean_release/
```
**Structure Decision**: Сохраняем существующую backend-centric структуру проекта и расширяем текущий пакет `backend/src/services/clean_release/` вместо выделения нового top-level приложения. Это уменьшает migration risk и позволяет поэтапно заменить текущие `preparation_service`, `manifest_builder`, `compliance_orchestrator` и `clean_release_tui.py` на фасадную архитектуру.
## Phase 0 Research Scope
Research resolved the main architectural questions and produced decisions in [`research.md`](./research.md):
- trust model for policy and registry;
- immutable snapshot strategy;
- lifecycle/state machine design;
- TaskManager integration strategy;
- interface split between CLI/API/TUI;
- repository decomposition and migration approach;
- demo mode isolation.
## Phase 1 Design Outputs
Phase 1 produces:
- [`data-model.md`](./data-model.md) with canonical entities, states and relationships;
- [`contracts/modules.md`](./contracts/modules.md) with module-level contracts for new services and interfaces;
- [`contracts/clean-release-api.openapi.yaml`](./contracts/clean-release-api.openapi.yaml) and [`contracts/cli.md`](./contracts/cli.md) for programmatic interfaces;
- [`ux_reference.md`](./ux_reference.md) for thin-client TUI/CLI behavior;
- [`quickstart.md`](./quickstart.md) for implementation and validation scenarios.
## Post-Design Constitution Re-Check
1. **No interface-owned business logic**: Preserved by facade + application services. PASS.
2. **Async long-running work**: Preserved by explicit `ComplianceExecutionService -> TaskManager` mapping. PASS.
3. **Independent testability**: Preserved via user-story tasks and dedicated contract tests. PASS.
4. **Config discipline and trust boundaries**: Preserved by read-only policy resolution service and immutable snapshots. PASS.
## Implementation Risks To Control
- Existing `clean_release_tui.py` currently owns preparation/build/run logic and must be thinned without breaking operator workflow.
- Existing repository and model naming may conflict with the redesigned entity boundaries; migration shims may be needed.
- Existing `/api/clean-release/checks*` endpoints may require compatibility strategy while introducing candidate/manifest/run-oriented endpoints.
- Existing `.clean-release.yaml` driven flow from `023-clean-repo-enterprise` conflicts with the new trusted policy-store model and must be deliberately deprecated or scoped.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| Multiple service modules instead of one orchestrator | Trust boundaries, immutability and lifecycle rules need explicit ownership | One orchestrator keeps the current blending of UI, policy, execution and persistence concerns |
| Separate repositories/facade layer | Needed for append-only evidence and independent interfaces | Single universal repository would keep ambiguous ownership and weak contracts |
| TaskManager integration for runs | Constitution requires async lifecycle and observability | Synchronous API/TUI execution would violate non-blocking execution requirements |

View File

@@ -0,0 +1,117 @@
# Quickstart: Clean Release Compliance Subsystem Redesign
## Purpose
Use this package to implement and validate the redesigned clean release subsystem defined in:
- Spec: [`spec.md`](specs/025-clean-release-compliance/spec.md)
- Plan: [`plan.md`](specs/025-clean-release-compliance/plan.md)
- UX reference: [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md)
- Data model: [`data-model.md`](specs/025-clean-release-compliance/data-model.md)
- Contracts: [`contracts/modules.md`](specs/025-clean-release-compliance/contracts/modules.md), [`contracts/clean-release-api.openapi.yaml`](specs/025-clean-release-compliance/contracts/clean-release-api.openapi.yaml), [`contracts/cli.md`](specs/025-clean-release-compliance/contracts/cli.md)
## 1) Backend implementation flow
1. Introduce canonical lifecycle/domain entities and state machine guards.
2. Split current clean release logic into dedicated application services and repository interfaces.
3. Resolve policy and registry from trusted stores into immutable snapshots.
4. Move compliance execution to `TaskManager` and persist stage events, violations and reports.
5. Expose candidate/manifest/compliance/release operations through one facade.
6. Rebuild API and CLI on top of the facade.
7. Refactor TUI into a read/trigger-only client.
## 2) Headless operator flow target
### Candidate registration and artifact import
```bash
clean-release candidate register \
--candidate-id 2026.03.09-rc1 \
--version 1.2.0 \
--source-snapshot v1.2.0-rc1 \
--actor release-bot
clean-release candidate import-artifacts \
--candidate-id 2026.03.09-rc1 \
--input artifacts.json \
--actor release-bot
```
### Manifest and compliance
```bash
clean-release manifest build --candidate-id 2026.03.09-rc1 --actor release-bot
clean-release compliance run --candidate-id 2026.03.09-rc1 --manifest-id man-001 --actor release-bot --json
clean-release compliance status --run-id run-001 --json
clean-release compliance report --run-id run-001 --json
```
### Approval and publication
```bash
clean-release release approve --candidate-id 2026.03.09-rc1 --report-id rpt-001 --actor release-owner
clean-release release publish --candidate-id 2026.03.09-rc1 --report-id rpt-001 --channel prod --actor release-owner
```
## 3) REST API smoke checklist
- `POST /api/clean-release/candidates` creates candidate and returns overview DTO.
- `POST /api/clean-release/candidates/{candidate_id}/artifacts/import` persists artifacts without triggering compliance.
- `POST /api/clean-release/candidates/{candidate_id}/manifest` creates a new immutable manifest version.
- `POST /api/clean-release/candidates/{candidate_id}/compliance-runs` returns `202` with `run_id` and `task_id`.
- `GET /api/clean-release/compliance-runs/{run_id}` returns live execution status.
- `GET /api/clean-release/compliance-runs/{run_id}/stages` returns ordered stage results.
- `GET /api/clean-release/compliance-runs/{run_id}/violations` returns append-only violations.
- `GET /api/clean-release/compliance-runs/{run_id}/report` returns immutable final report for completed runs.
- `POST /api/clean-release/candidates/{candidate_id}/approve` enforces passed-report gate.
- `POST /api/clean-release/candidates/{candidate_id}/publish` enforces approval gate.
## 4) UX conformance checks (must pass)
- TUI shows latest candidate, latest manifest, latest run and latest report without mutating them.
- `F6` builds manifest explicitly; `F5` never auto-builds hidden manifest.
- `F8` is disabled or blocked with explicit reason unless candidate is in `CHECK_PASSED`.
- `F9` is disabled or blocked with explicit reason unless candidate is `APPROVED`.
- Running state is sourced from real run/task progress, not simulated local state.
- Non-TTY startup redirects the operator to CLI/API path instead of headless TUI mode.
- Demo mode actions never expose or modify real-mode history.
## 5) Contract checks (must pass)
- Domain transitions conform to the lifecycle in [`data-model.md`](specs/025-clean-release-compliance/data-model.md).
- API payloads conform to [`clean-release-api.openapi.yaml`](specs/025-clean-release-compliance/contracts/clean-release-api.openapi.yaml).
- CLI commands and exit codes conform to [`cli.md`](specs/025-clean-release-compliance/contracts/cli.md).
- Immutable entities are never updated in place.
- Real-mode runs, reports, violations and audit events remain append-only.
## 6) Suggested validation commands
Backend targeted tests:
```bash
cd backend && .venv/bin/python3 -m pytest tests/services/clean_release tests/api/routes -k clean_release -q
```
CLI smoke tests:
```bash
cd backend && .venv/bin/python3 -m pytest tests/scripts/test_clean_release_cli.py -q
```
TUI thin-client smoke tests:
```bash
cd backend && .venv/bin/python3 -m pytest tests/scripts/test_clean_release_tui_v2.py -q
```
## 7) Migration checkpoints
1. Old TUI logic no longer directly prepares candidates, builds manifests or finalizes runs.
2. Legacy `/api/clean-release/checks*` entrypoints have explicit compatibility or deprecation behavior.
3. Trusted policy resolution no longer depends on UI/env bootstrap payloads.
4. Compliance run state is visible through both TaskManager and clean-release run records.
5. Demo namespace and real namespace are visibly isolated.
## 8) Done criteria for planning handoff
- All planning artifacts exist and are internally consistent.
- State machine, trust boundaries and immutable evidence model are defined.
- CLI and REST contracts are stable enough for parallel implementation.
- TUI UX reference is explicitly thin-client only.
- Ready to decompose into executable work items via [`tasks.md`](specs/025-clean-release-compliance/tasks.md).

View File

@@ -0,0 +1,125 @@
# 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.

View File

@@ -0,0 +1,153 @@
# Feature Specification: Clean Release Compliance Subsystem Redesign
**Feature Branch**: `025-clean-release-compliance`
**Created**: 2026-03-09
**Status**: Draft
**Input**: User description: "Редизайн текущего clean release TUI-checker в нормальную API/CLI-first release/compliance subsystem с разделением domain/application/infrastructure/interfaces, immutable snapshots, append-only compliance evidence, approval/publication flow, thin TUI client, REST API и интеграцией с TaskManager."
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Headless candidate and manifest lifecycle (Priority: P1)
Как release-менеджер или CI pipeline, я хочу регистрировать релиз-кандидат, импортировать артефакты и строить manifest без TUI, чтобы вход в release workflow был воспроизводимым в автоматизированном или headless сценарии.
**Why this priority**: Это базовая ценность редизайна. Пока жизненный цикл кандидата зависит от TUI, release flow не годится для автоматизации и надёжного enterprise use.
**Independent Test**: Можно создать кандидата, импортировать набор артефактов, построить manifest и запросить overview только через CLI или HTTP API, не используя интерактивный интерфейс.
**Acceptance Scenarios**:
1. **Given** у оператора есть описание релиз-кандидата и набор артефактов, **When** он выполняет headless workflow регистрации, импорта и построения manifest, **Then** система сохраняет кандидата и immutable snapshot manifest без участия TUI.
2. **Given** manifest успешно построен, **When** оператор запрашивает candidate overview через CLI или API, **Then** он получает актуальный lifecycle state и идентификатор latest manifest.
3. **Given** pipeline подготавливает следующий этап release workflow, **When** он использует headless candidate and manifest operations, **Then** ему не требуется интерактивный интерфейс для продолжения release process.
---
### User Story 2 - Trusted and immutable compliance evidence (Priority: P1)
Как сотрудник комплаенса или аудита, я хочу, чтобы policy, registry, manifest, run, violations и report были отделены друг от друга и сохранялись как доверенные или append-only сущности, чтобы итог выпуска был доказуемым и не зависел от UI-состояния или случайных env/json подмен.
**Why this priority**: Главная архитектурная проблема текущего решения - смешение trust boundaries и UI logic с бизнес-решениями. Без исправления этого нельзя считать результат compliance надёжным.
**Independent Test**: Можно зафиксировать policy snapshot и registry snapshot, выполнить run, затем проверить, что manifest/report/violations не переписывались и что итоговый статус выводится только из сохранённого evidence.
**Acceptance Scenarios**:
1. **Given** активная policy получена из доверенного policy store, **When** запускается compliance, **Then** run использует snapshot policy и snapshot registry, а не значения из UI или временных env-переменных.
2. **Given** для кандидата уже создан manifest, **When** входные данные кандидата меняются, **Then** система создаёт новый version manifest вместо изменения существующего snapshot.
3. **Given** run завершён, **When** оператор запрашивает историю проверки, **Then** старые run, violations и reports остаются доступными и не удаляются в real mode.
---
### User Story 3 - Controlled approval and publication gate (Priority: P2)
Как владелец релиза, я хочу выполнять approve, publish и revoke только после валидного compliance результата, чтобы состояние выпуска было формально управляемым и не зависело от ручных договорённостей.
**Why this priority**: Проверка compliance имеет смысл только тогда, когда она реально управляет дальнейшими переходами release lifecycle.
**Independent Test**: Можно попытаться утвердить или опубликовать кандидата в запрещённом состоянии и убедиться, что система блокирует переход; затем пройти валидный путь `CHECK_PASSED -> APPROVED -> PUBLISHED`.
**Acceptance Scenarios**:
1. **Given** кандидат имеет последний report со статусом `PASSED`, **When** уполномоченный оператор выполняет approve, **Then** система фиксирует approval decision и переводит кандидата в состояние `APPROVED`.
2. **Given** кандидат не имеет успешного compliance report или latest approval decision имеет значение `REJECTED`, **When** оператор пытается выполнить approve или publish вне допустимого правила, **Then** система отклоняет действие и сообщает причину блокировки.
3. **Given** кандидат опубликован в целевой канал, **When** требуется отзыв выпуска, **Then** система создаёт publication revocation record без удаления исходной истории публикации.
---
### User Story 4 - Thin operational interfaces (Priority: P3)
Как инженер сопровождения, я хочу использовать TUI и другие интерфейсы только как клиенты чтения и запуска операций, чтобы интерфейсы не содержали скрытой бизнес-логики, не подменяли policy и не создавали визуальные fake-runs.
**Why this priority**: Тонкие интерфейсы упрощают сопровождение, делают поведение одинаковым между CLI, API, TUI и будущим Web UI, а также уменьшают риск расхождения логики.
**Independent Test**: Можно открыть TUI, выполнить build manifest, run compliance, approve и publish, а затем проверить, что все операции и их последствия совпадают с CLI/API сценариями и не зависят от внутреннего UI state.
**Acceptance Scenarios**:
1. **Given** оператор открывает TUI, **When** он запускает build manifest или compliance, **Then** TUI вызывает application services и только отображает актуальные DTO/статусы.
2. **Given** система работает в demo mode, **When** оператор запускает demo workflow, **Then** demo данные и demo history остаются полностью изолированными от real mode.
3. **Given** оператор работает в headless окружении без TTY, **When** он пытается использовать функциональность release lifecycle, **Then** система направляет его в CLI/API путь, а не переводит TUI в скрытый псевдо-headless режим.
### Edge Cases
- Что происходит, если артефакт имеет расхождение между `declared_category` и `detected_category`?
- Что происходит, если кандидат изменился после построения manifest, но до запуска compliance?
- Как система различает business blocked, invalid input и system error при возврате статуса и exit code?
- Что происходит, если policy store недоступен в момент запроса compliance?
- Как обрабатывается повторный approve или повторный publish для уже терминально обработанного кандидата?
- Что происходит, если run был отменён или task crashed после старта, но до генерации report?
- Как система предотвращает пересечение demo namespace и real namespace в storage и audit history?
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: Система MUST предоставлять программно управляемый release lifecycle, доступный без TUI через CLI и HTTP API.
- **FR-002**: Система MUST поддерживать отдельную сущность release candidate с идентификатором, версией, ссылкой на source snapshot, provenance и статусом lifecycle.
- **FR-003**: Система MUST поддерживать импорт и хранение candidate artifacts с checksum, размером, declared category и detected category.
- **FR-004**: Система MUST различать declared category и detected category и фиксировать факт их расхождения как часть compliance evidence.
- **FR-005**: Система MUST строить distribution manifest как отдельный immutable snapshot с собственным идентификатором, version, digest и полным содержимым.
- **FR-006**: Если состав кандидата изменился после построения manifest, система MUST создавать новый manifest version вместо изменения существующего snapshot.
- **FR-007**: Система MUST получать policy только из доверенного policy store и сохранять используемую policy как immutable policy snapshot.
- **FR-008**: Система MUST получать source registry только из доверенного registry source и сохранять используемый registry как immutable snapshot.
- **FR-009**: Ни один интерфейс, пользовательский ввод или env-переменная MUST NOT определять содержимое policy snapshot, registry snapshot, итоговый report status или audit history.
- **FR-010**: Система MUST создавать отдельную сущность compliance run, связанную с candidate, manifest, policy snapshot и registry snapshot.
- **FR-011**: Compliance run MUST сохранять append-only stage execution records, violations и итоговый report.
- **FR-012**: Система MUST поддерживать статусы выполнения run не менее чем `PENDING`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`.
- **FR-013**: Система MUST поддерживать итоговые статусы compliance не менее чем `PASSED`, `BLOCKED`, `ERROR`.
- **FR-014**: Система MUST различать outcome types `PASSED`, `BLOCKED`, `ERROR_INVALID_INPUT`, `ERROR_SYSTEM` и делать их однозначно наблюдаемыми через CLI/API.
- **FR-015**: Compliance pipeline MUST состоять из независимых стадий с отдельным stage result, decision, details и violations.
- **FR-016**: Система MUST позволять расширять compliance pipeline новыми стадиями без переписывания интерфейсов запуска и чтения run.
- **FR-017**: Release candidate lifecycle MUST поддерживать разрешённые переходы `DRAFT -> PREPARED -> MANIFEST_BUILT -> CHECK_PENDING -> CHECK_RUNNING -> CHECK_PASSED|CHECK_BLOCKED|CHECK_ERROR -> APPROVED -> PUBLISHED -> REVOKED`.
- **FR-018**: Система MUST блокировать `APPROVED` без успешного compliance result и MUST блокировать `PUBLISHED` без approval.
- **FR-019**: Система MUST сохранять отдельный approval decision с actor, временем, комментарием и ссылкой на report.
- **FR-020**: Система MUST трактовать approval и reject как immutable decisions, где latest decision governs publication gate; `REJECTED` блокирует публикацию, но не меняет compliance evidence и не переписывает lifecycle state кандидата.
- **FR-021**: Система MUST сохранять отдельный publication record с channel, actor, временем, ref и статусом, а revoke MUST оформляться как отдельное действие без удаления исходной публикации.
- **FR-022**: Каждое long-running compliance execution MUST выполняться как асинхронная task execution единица с наблюдаемым lifecycle, логами stage events и итоговой summary.
- **FR-023**: API запуск compliance MUST быть non-blocking и возвращать идентификаторы run и связанной task execution для последующего наблюдения.
- **FR-024**: Система MUST вести append-only audit log для создания кандидата, импорта артефактов, построения manifest, запуска compliance, завершения стадий, фиксации violations, генерации report, approval, publication и revoke.
- **FR-025**: В real mode система MUST NOT удалять historical runs, reports, violations или audit events.
- **FR-026**: Demo mode MUST использовать отдельный storage namespace, отдельные policy snapshots и отдельную историю, полностью изолированную от real mode.
- **FR-027**: TUI MUST быть thin client интерфейсом чтения и запуска операций и MUST NOT скрыто строить manifest, подмешивать policy, подменять registry, очищать history реального режима или создавать fake run только ради UI.
- **FR-028**: Для headless сценариев система MUST предоставлять CLI команды для candidate lifecycle, compliance, approval, publication и revoke.
- **FR-029**: Система MUST предоставлять HTTP endpoints для candidate lifecycle, manifest operations, compliance runs, report retrieval, approval и publication.
- **FR-030**: Система MUST предоставлять сводное представление candidate overview с данными о последнем manifest, последнем run, последнем report, latest policy snapshot, latest approval/publication state и текущем lifecycle state.
- **FR-031**: Compliance report MUST быть отдельной immutable сущностью с финальным статусом, summary и связью с конкретным run.
- **FR-032**: Система MUST позволять оператору получать список violations и stage details отдельно от итогового report.
- **FR-033**: Business actions для candidate, manifest, compliance, approval и publication MUST использовать единый application facade, одинаковый для CLI, TUI, API и будущего Web UI.
### Key Entities *(include if feature involves data)*
- **Release Candidate**: Объект управления выпуском, описывающий, что именно собираются выпустить и в каком lifecycle состоянии это находится.
- **Candidate Artifact**: Артефакт релиз-кандидата с доказуемыми признаками происхождения, контрольной суммой и двумя категориями классификации.
- **Distribution Manifest**: Immutable snapshot состава поставки, зафиксированный для конкретного кандидата и конкретной версии manifest.
- **Clean Policy Snapshot**: Доверенный immutable snapshot policy, использованный во время проверки.
- **Source Registry Snapshot**: Доверенный immutable snapshot реестра разрешённых источников.
- **Compliance Run**: Операционная сущность, представляющая отдельный запуск проверки для кандидата и manifest.
- **Compliance Stage Run**: Результат выполнения отдельной стадии pipeline внутри конкретного run.
- **Compliance Violation**: Зафиксированное нарушение с severity, evidence и ссылкой на stage/run.
- **Compliance Report**: Отдельный immutable итоговый отчёт по завершённому run.
- **Approval Decision**: Формальный акт approve/reject с actor, comment и ссылкой на report.
- **Publication Record**: Формальная запись публикации или отзыва с channel и статусом.
- **Candidate Overview**: Производное представление для интерфейсов, объединяющее состояние кандидата, latest manifest, latest run и latest report.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: 100% обязательных действий release lifecycle для стандартного сценария (`register -> import-artifacts -> build-manifest -> run-compliance -> approve -> publish`) выполняются без TUI через CLI или API.
- **SC-002**: 100% завершённых compliance runs оставляют неизменяемый report и связанный audit trail, доступный для чтения после завершения.
- **SC-003**: 100% попыток approve без успешного compliance result и publish без approval отклоняются системой.
- **SC-004**: Не менее 95% стандартных запусков compliance получают machine-readable outcome без ручного исправления состояния системы.
- **SC-005**: Не менее 90% операторов могут пройти базовый release workflow по quickstart без обращения к скрытым TUI-сценариям или ручным DB-операциям.
- **SC-006**: Время получения идентификатора run/task после запроса запуска compliance не превышает 2 секунд для типового кандидата.
- **SC-007**: В 100% real-mode сценариев historical runs, violations, reports и audit events не удаляются командами пользовательского интерфейса.
## Assumptions
- В проекте уже доступен общий TaskManager, пригодный для оркестрации long-running compliance tasks.
- Для policy и registry может быть предоставлен доверенный read-only источник, отдельный от пользовательского UI input.
- Существующий clean-release модуль допускает поэтапную миграцию без одномоментного удаления старых API/TUI entrypoints.
- Операторы релиза, approval actors и publication actors аутентифицированы существующими механизмами приложения.
- Demo mode нужен для демонстрации и тестовых сценариев, но не должен влиять на real mode evidence.

View File

@@ -0,0 +1,225 @@
# Tasks: Clean Release Compliance Subsystem Redesign
**Input**: Design documents from [`/specs/025-clean-release-compliance/`](specs/025-clean-release-compliance)
**Prerequisites**: [`plan.md`](specs/025-clean-release-compliance/plan.md), [`spec.md`](specs/025-clean-release-compliance/spec.md), [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md), [`research.md`](specs/025-clean-release-compliance/research.md), [`data-model.md`](specs/025-clean-release-compliance/data-model.md), [`contracts/`](specs/025-clean-release-compliance/contracts)
**Tests**: Include service, API, CLI and TUI smoke tests because this is a lifecycle-critical subsystem redesign.
**Organization**: Tasks are grouped by user story to enable independent implementation and testing.
## Format: `[ID] [P?] [Story] Description`
---
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Prepare new clean-release redesign scaffolding, fixtures and test entrypoints.
- [ ] T001 Create clean release redesign module skeletons in `backend/src/services/clean_release/` and `backend/src/services/clean_release/repositories/`
- [ ] T002 [P] Add redesign fixture set in `backend/tests/fixtures/clean_release/fixtures_release_v2.json`
- [ ] T003 [P] Add API contract test scaffolding in `backend/src/api/routes/__tests__/test_clean_release_v2_api.py` and `backend/src/api/routes/__tests__/test_clean_release_v2_release_api.py`
- [ ] T004 [P] Add CLI and TUI smoke test scaffolding in `backend/tests/scripts/test_clean_release_cli.py` and `backend/tests/scripts/test_clean_release_tui_v2.py`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Build canonical lifecycle, persistence boundaries and shared facade before any user story.
- [ ] T005 Implement clean release enums, exceptions and DTOs in `backend/src/services/clean_release/enums.py`, `backend/src/services/clean_release/exceptions.py` and `backend/src/services/clean_release/dto.py`
- [ ] T006 Implement canonical clean release domain entities and lifecycle guards in `backend/src/models/clean_release.py` (CRITICAL: PRE valid aggregate identifiers and state commands; POST immutable evidence and valid transitions only; TESTS: invalid transition, manifest immutability, publish gate)
- [ ] T007 [P] Implement repository interfaces and durable adapters in `backend/src/services/clean_release/repositories/candidate_repository.py`, `backend/src/services/clean_release/repositories/artifact_repository.py`, `backend/src/services/clean_release/repositories/manifest_repository.py`, `backend/src/services/clean_release/repositories/policy_repository.py`, `backend/src/services/clean_release/repositories/compliance_repository.py`, `backend/src/services/clean_release/repositories/report_repository.py`, `backend/src/services/clean_release/repositories/approval_repository.py`, `backend/src/services/clean_release/repositories/publication_repository.py` and `backend/src/services/clean_release/repositories/audit_repository.py`
- [ ] T008 [P] Implement facade and DTO mapping in `backend/src/services/clean_release/facade.py` and `backend/src/services/clean_release/mappers.py`
- [ ] T009 Wire clean release dependencies for repositories, trusted policy access and task manager in `backend/src/dependencies.py`
- [ ] T009a Implement `ConfigManager`-backed resolution for trusted policy store, profile selection, mode and storage wiring in `backend/src/dependencies.py` and `backend/src/services/clean_release/policy_resolution_service.py`
- [ ] T010 Add legacy compatibility shim and migration helpers in `backend/src/services/clean_release/__init__.py` and `backend/src/services/clean_release/repository.py`
**Checkpoint**: Foundational layer complete; user stories can proceed.
---
## Phase 3: User Story 1 - Headless release candidate lifecycle (Priority: P1) 🎯 MVP
**Goal**: Make candidate registration, artifact import, manifest build and lifecycle visibility available through CLI/API without TUI.
**Independent Test**: Register candidate, import artifacts, build manifest and query overview using only CLI/API.
### Tests for User Story 1
- [ ] T011 [P] [US1] Add lifecycle and manifest versioning tests in `backend/tests/services/clean_release/test_candidate_manifest_services.py`
- [ ] T012 [P] [US1] Add API contract tests for candidate/artifact/manifest endpoints in `backend/src/api/routes/__tests__/test_clean_release_v2_api.py`
- [ ] T013 [P] [US1] Add CLI smoke tests for candidate register/import/manifest build in `backend/tests/scripts/test_clean_release_cli.py`
### Implementation for User Story 1
- [ ] T014 [US1] Implement candidate preparation service in `backend/src/services/clean_release/candidate_service.py` (CRITICAL: PRE unique candidate id and valid artifacts; POST candidate/artifacts persisted and status advances only through legal states; TESTS: duplicate id, malformed artifact input, empty artifact set)
- [ ] T015 [US1] Implement manifest service in `backend/src/services/clean_release/manifest_service.py` (CRITICAL: PRE candidate prepared and artifacts available; POST immutable manifest snapshot with deterministic digest and version increment; TESTS: rebuild creates new version, existing manifest cannot be mutated, missing candidate rejected)
- [ ] T016 [US1] Implement policy resolution service with trusted snapshot reads in `backend/src/services/clean_release/policy_resolution_service.py` (CRITICAL: PRE trusted profile exists; POST immutable policy and registry snapshots without UI/env overrides; TESTS: missing profile, missing registry, override attempt)
- [ ] T017 [US1] Implement candidate and manifest CLI commands in `backend/src/scripts/clean_release_cli.py`
- [ ] T018 [US1] Implement candidate/artifact/manifest REST endpoints and expanded overview DTO mapping in `backend/src/api/routes/clean_release.py`
- [ ] T019 [US1] Verify implementation matches [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md) (Happy Path & Errors)
**Checkpoint**: US1 independently functional and usable from headless automation.
---
## Phase 4: User Story 2 - Trusted and immutable compliance evidence (Priority: P1)
**Goal**: Execute compliance as an observable, append-only TaskManager-backed pipeline with immutable reports and trusted snapshots.
**Independent Test**: Start a run through API/CLI, observe task/run progress, inspect stage records and violations, then verify immutable final report persistence.
### Tests for User Story 2
- [ ] T020 [P] [US2] Add stage pipeline and run finalization tests in `backend/tests/services/clean_release/test_compliance_execution_service.py`
- [ ] T021 [P] [US2] Add TaskManager integration tests for clean release runs in `backend/tests/services/clean_release/test_compliance_task_integration.py`
- [ ] T022 [P] [US2] Add report and audit immutability tests in `backend/tests/services/clean_release/test_report_audit_immutability.py`
### Implementation for User Story 2
- [ ] T023 [US2] Implement pluggable stage base and default stage modules in `backend/src/services/clean_release/stages/base.py`, `backend/src/services/clean_release/stages/data_purity.py`, `backend/src/services/clean_release/stages/internal_sources_only.py`, `backend/src/services/clean_release/stages/no_external_endpoints.py` and `backend/src/services/clean_release/stages/manifest_consistency.py`
- [ ] T024 [US2] Implement compliance execution service in `backend/src/services/clean_release/compliance_execution_service.py` (CRITICAL: PRE candidate exists and explicit or latest manifest plus trusted snapshots are resolvable; POST run, stage records, violations and report remain mutually consistent; TESTS: run without manifest, task crash mid-run, blocked report finalization)
- [ ] T025 [US2] Bind compliance runs to TaskManager and reports service in `backend/src/services/clean_release/compliance_execution_service.py`, `backend/src/services/reports/report_service.py` and `backend/src/dependencies.py`
- [ ] T026 [US2] Implement compliance REST endpoints for run creation, run status, stages, violations and report in `backend/src/api/routes/clean_release.py`
- [ ] T027 [US2] Implement compliance CLI commands (`run`, `status`, `report`, `violations`) in `backend/src/scripts/clean_release_cli.py` with latest-manifest fallback when `--manifest-id` is omitted
- [ ] T028 [US2] Implement append-only audit hooks for run lifecycle and violations in `backend/src/services/clean_release/audit_service.py`
- [ ] T029 [US2] Verify implementation matches [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md) (Happy Path & Errors)
**Checkpoint**: US2 independently functional with real run evidence and immutable reporting.
---
## Phase 5: User Story 3 - Controlled approval and publication gate (Priority: P2)
**Goal**: Enforce legal approval/publication transitions over completed compliance results.
**Independent Test**: Attempt invalid approve/publish transitions, then complete the valid `CHECK_PASSED -> APPROVED -> PUBLISHED -> REVOKED` flow.
### Tests for User Story 3
- [ ] T030 [P] [US3] Add approval gate tests in `backend/tests/services/clean_release/test_approval_service.py`
- [ ] T031 [P] [US3] Add publication gate tests in `backend/tests/services/clean_release/test_publication_service.py`
- [ ] T032 [P] [US3] Add API/CLI tests for approve, reject, publish and revoke in `backend/src/api/routes/__tests__/test_clean_release_v2_release_api.py` and `backend/tests/scripts/test_clean_release_cli.py`
### Implementation for User Story 3
- [ ] T033 [US3] Implement approval service in `backend/src/services/clean_release/approval_service.py` (CRITICAL: PRE report belongs to candidate and final status is PASSED for approve; POST immutable decision persisted, approve may advance candidate state, reject blocks publication gate without rewriting compliance evidence; TESTS: approve blocked report, approve foreign report, duplicate approve, reject then publish)
- [ ] T034 [US3] Implement publication service in `backend/src/services/clean_release/publication_service.py` (CRITICAL: PRE candidate approved; POST immutable publication/revocation record and legal state transition; TESTS: publish without approval, revoke unknown publication, republish after revoke)
- [ ] T035 [US3] Implement release CLI commands (`approve`, `reject`, `publish`, `revoke`) in `backend/src/scripts/clean_release_cli.py`
- [ ] T036 [US3] Implement release REST endpoints in `backend/src/api/routes/clean_release.py`
- [ ] T037 [US3] Extend facade overview/read models for policy snapshot, approval and publication state in `backend/src/services/clean_release/facade.py` and `backend/src/services/clean_release/dto.py`
- [ ] T038 [US3] Verify implementation matches [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md) (Happy Path & Errors)
**Checkpoint**: US3 independently functional with explicit release gates.
---
## Phase 6: User Story 4 - Thin operational interfaces (Priority: P3)
**Goal**: Convert TUI into a real thin client and isolate demo behavior from real-mode evidence.
**Independent Test**: Operate the same candidate through TUI using facade-backed actions and confirm that TUI behavior matches CLI/API semantics without hidden side effects.
### Tests for User Story 4
- [ ] T039 [P] [US4] Add TUI thin-client smoke tests for facade actions and blocked transitions in `backend/tests/scripts/test_clean_release_tui_v2.py`
- [ ] T040 [P] [US4] Add demo namespace isolation tests in `backend/tests/services/clean_release/test_demo_mode_isolation.py`
- [ ] T041 [P] [US4] Add non-TTY startup behavior tests in `backend/tests/scripts/test_clean_release_tui_v2.py`
### Implementation for User Story 4
- [ ] T042 [US4] Refactor TUI to call only facade methods and render DTOs in `backend/src/scripts/clean_release_tui.py` (CRITICAL: PRE valid TTY and candidate context; POST no hidden manifest/policy/run mutations outside facade; TESTS: no TTY, missing manifest on F5, blocked report on F8)
- [ ] T043 [US4] Implement isolated demo data service and namespace handling in `backend/src/services/clean_release/demo_data_service.py` and `backend/src/services/clean_release/repositories/`
- [ ] T044 [US4] Remove real-mode `clear_history` and pseudo-headless fallback behavior in `backend/src/scripts/clean_release_tui.py`
- [ ] T045 [US4] Implement TUI overview panels and action keys `F5/F6/F7/F8/F9/F10` aligned with facade DTOs in `backend/src/scripts/clean_release_tui.py`
- [ ] T046 [US4] Verify implementation matches [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md) (Happy Path & Errors)
**Checkpoint**: US4 independently functional with thin-client TUI and isolated demo mode.
---
## Phase 7: Polish & Cross-Cutting Concerns
**Purpose**: Finalize migration, compatibility and operational documentation.
- [ ] T047 [P] Add compatibility/deprecation tests for legacy `/api/clean-release/checks*` and `/api/clean-release/candidates/prepare` paths in `backend/src/api/routes/__tests__/test_clean_release_legacy_compat.py`
- [ ] T048 [P] Update operational documentation for new CLI/API/TUI workflow in `README.md` and `docs/installation.md`
- [ ] T049 Run end-to-end quickstart validation and capture results in `specs/025-clean-release-compliance/quickstart.md`
- [ ] T050 Migrate or wrap legacy clean release modules in `backend/src/services/clean_release/preparation_service.py`, `backend/src/services/clean_release/manifest_builder.py`, `backend/src/services/clean_release/compliance_orchestrator.py` and `backend/src/services/clean_release/repository.py`
- [ ] T051 Align clean release report surfacing with shared reports/task views in `backend/src/services/reports/report_service.py` and `backend/src/api/routes/reports.py`
- [ ] T052 Run semantic compliance review for touched clean release modules and close critical `[DEF]`/contract gaps in `backend/src/models/clean_release.py`, `backend/src/services/clean_release/` and `backend/src/scripts/clean_release_tui.py`
---
## Dependencies & Execution Order
### Phase Dependencies
- **Phase 1 (Setup)**: No dependencies.
- **Phase 2 (Foundational)**: Depends on Phase 1 and blocks all stories.
- **Phase 3 (US1)**: Depends on Phase 2.
- **Phase 4 (US2)**: Depends on Phase 2 and reuses outputs from US1 trusted snapshot and facade work.
- **Phase 5 (US3)**: Depends on Phase 2 and a stable report model from US2.
- **Phase 6 (US4)**: Depends on Phases 3-5 because TUI must sit on stable facade/API semantics.
- **Phase 7 (Polish)**: Depends on all selected stories.
### User Story Dependencies
- **US1 (P1)**: First deliverable and MVP.
- **US2 (P1)**: Depends on facade/repository foundations and benefits from US1 candidate/manifest flow.
- **US3 (P2)**: Depends on successful report persistence from US2.
- **US4 (P3)**: Depends on stable facade and release-gate behavior from US1-US3.
Graph: `US1 -> US2 -> US3 -> US4`
### Parallel Opportunities
- Setup tasks T002, T003, T004.
- Foundational tasks T007 and T008 after T005/T006 are stable.
- US1 tests T011, T012, T013.
- US2 tests T020, T021, T022.
- US3 tests T030, T031, T032.
- US4 tests T039, T040, T041.
- Polish tasks T047 and T048.
---
## Parallel Example: User Story 1
```bash
Task: "T011 [US1] Add lifecycle and manifest tests in backend/tests/services/clean_release/test_candidate_manifest_services.py"
Task: "T012 [US1] Add API contract tests in backend/src/api/routes/__tests__/test_clean_release_v2_api.py"
Task: "T013 [US1] Add CLI smoke tests in backend/tests/scripts/test_clean_release_cli.py"
```
## Parallel Example: User Story 2
```bash
Task: "T020 [US2] Add stage pipeline tests in backend/tests/services/clean_release/test_compliance_execution_service.py"
Task: "T021 [US2] Add TaskManager integration tests in backend/tests/services/clean_release/test_compliance_task_integration.py"
Task: "T022 [US2] Add report immutability tests in backend/tests/services/clean_release/test_report_audit_immutability.py"
```
---
## Implementation Strategy
### MVP First (Recommended)
1. Complete Phase 1 and Phase 2.
2. Deliver Phase 3 (US1) so candidate lifecycle works headlessly through CLI/API.
3. Validate independent test for US1.
4. Then add US2 for trusted compliance evidence before moving to release gates and TUI refactor.
### Incremental Delivery
1. US1: headless candidate lifecycle.
2. US2: trusted compliance execution + immutable evidence.
3. US3: approval/publication gate.
4. US4: thin TUI + demo isolation.
5. Phase 7: compatibility, docs and semantic cleanup.
### UX Preservation Rule
No task in this plan is allowed to reintroduce hidden business logic into TUI or to degrade the explicit operator flow in [`ux_reference.md`](specs/025-clean-release-compliance/ux_reference.md).
Each user story contains a mandatory UX verification task: T019, T029, T038, T046.

View File

@@ -0,0 +1,95 @@
# UX Reference: Clean Release Compliance Subsystem Redesign
**Feature Branch**: `025-clean-release-compliance`
**Created**: 2026-03-09
**Status**: Draft
## 1. User Persona & Context
- **Who is the user?**: Release manager, compliance operator, enterprise support engineer.
- **What is their goal?**: Safely move a release candidate through candidate preparation, compliance, approval and publication without hidden state.
- **Context**: Usually works in terminal-first infrastructure, often in restricted or headless environments, sometimes with a TTY, sometimes from CI.
## 2. The Happy Path Narrative
Оператор регистрирует кандидата и импортирует артефакты через CLI или API. Затем он открывает TUI и сразу видит candidate overview: latest manifest, active policy snapshot, latest run, violations и publication state. Нажатие `F6` строит manifest, `F5` запускает compliance, а экран показывает реальный прогресс stage-by-stage из task/run logs, а не локальную имитацию. После `PASSED` оператор выполняет `F8` approve и `F9` publish, и каждое действие мгновенно отражается в overview без скрытых сайд-эффектов.
## 3. Interface Mockups
### CLI Interaction
```bash
$ clean-release candidate register \
--candidate-id 2026.03.09-rc1 \
--version 1.2.0 \
--source-snapshot v1.2.0-rc1
Candidate created: 2026.03.09-rc1
Status: DRAFT
$ clean-release manifest build --candidate-id 2026.03.09-rc1
Manifest created: man-001
Manifest digest: sha256:9fa...
Status: MANIFEST_BUILT
$ clean-release compliance run --candidate-id 2026.03.09-rc1 --json
{
"run_id": "run-001",
"candidate_id": "2026.03.09-rc1",
"status": "PENDING",
"task_id": "task-123"
}
```
### TUI Layout & Flow
**Screen/Component**: Clean Release Overview
- **Layout**: Three-pane terminal layout.
- Top header: current candidate, lifecycle state, active mode (`real` or `demo`).
- Left pane: candidate summary, latest manifest, approval/publication state.
- Right pane: latest compliance run, stage timeline, violations list.
- Bottom action bar: `F5 Run`, `F6 Manifest`, `F7 Refresh`, `F8 Approve`, `F9 Publish`, `F10 Exit`.
- **Key Elements**:
- **Candidate Summary**: shows candidate id, version, source snapshot, current state.
- **Latest Manifest Card**: manifest id, version, digest, created time.
- **Policy Snapshot Card**: policy id/version and registry version used for latest run.
- **Violations Table**: severity, code, artifact path, short message.
- **States**:
- **Default**: Existing overview visible, no hidden mutation.
- **Running**: Current run and current stage are highlighted; logs update live from task events.
- **Passed**: Action bar enables `Approve` when transition is legal.
- **Blocked/Error**: Violations or failure reason become the dominant focus; approval/publish actions stay disabled.
## 4. The Error Experience
**Philosophy**: Surface the real state and tell the operator what transition is blocked. Never hide missing prerequisites by auto-fixing them in the UI.
### Scenario A: Missing Manifest
- **User Action**: Presses `F5` to run compliance before a manifest exists.
- **System Response**:
- TUI: inline error banner `Manifest required before compliance run` and highlight on `F6 Build manifest`.
- CLI: `Error: candidate 2026.03.09-rc1 has no manifest. Build a manifest first.`
- **Recovery**: Operator runs build manifest, returns to overview, retries compliance.
### Scenario B: Blocked By Policy
- **System Response**: Run ends in `BLOCKED`, latest report card turns warning state, violations table is focused automatically.
- **Recovery**: Operator can inspect violations, export/report details, fix candidate inputs, build a new manifest and request a new run.
### Scenario C: Policy Store Unavailable
- **System Response**: Request is rejected as input/system error before stage execution; UI explicitly says policy snapshot could not be resolved.
- **Recovery**: Retry when trusted policy source is restored. No fake run is shown.
### Scenario D: Headless Environment
- **System Response**: TUI refuses to start without TTY and instructs operator to use CLI/API flow.
- **Recovery**: Run equivalent `clean-release ...` command or call API.
## 5. Tone & Voice
- **Style**: Concise, operational, deterministic.
- **Terminology**: Use `candidate`, `manifest`, `policy snapshot`, `compliance run`, `report`, `approval`, `publication` consistently.
- **Avoided Terms**: Avoid vague legacy words like `checker`, `fake run`, `history cleanup`, `headless ready` inside the operator UX.