таски готовы
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user