таски готовы
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
78
specs/025-clean-release-compliance/contracts/cli.md
Normal file
78
specs/025-clean-release-compliance/contracts/cli.md
Normal 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.
|
||||
391
specs/025-clean-release-compliance/contracts/modules.md
Normal file
391
specs/025-clean-release-compliance/contracts/modules.md
Normal 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.
|
||||
359
specs/025-clean-release-compliance/data-model.md
Normal file
359
specs/025-clean-release-compliance/data-model.md
Normal 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.
|
||||
163
specs/025-clean-release-compliance/plan.md
Normal file
163
specs/025-clean-release-compliance/plan.md
Normal 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 |
|
||||
117
specs/025-clean-release-compliance/quickstart.md
Normal file
117
specs/025-clean-release-compliance/quickstart.md
Normal 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).
|
||||
125
specs/025-clean-release-compliance/research.md
Normal file
125
specs/025-clean-release-compliance/research.md
Normal 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.
|
||||
153
specs/025-clean-release-compliance/spec.md
Normal file
153
specs/025-clean-release-compliance/spec.md
Normal 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.
|
||||
225
specs/025-clean-release-compliance/tasks.md
Normal file
225
specs/025-clean-release-compliance/tasks.md
Normal 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.
|
||||
95
specs/025-clean-release-compliance/ux_reference.md
Normal file
95
specs/025-clean-release-compliance/ux_reference.md
Normal 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.
|
||||
Reference in New Issue
Block a user