Publish an Immutable Release
Turn an approved preview into a traceable release without rebuilding or silently changing the artifact.
Run it in the public monorepo
This course is built around the public Zheta Kubernetes Lab monorepo. The excerpt below is runnable source, not pseudocode.
Source: services/control_plane/domain.py
def retire(self, project: Project, actor: str) -> None:
self._owner(project, actor)
project.status = "retired"
with self.repository.transaction():
self.repository.save(project)
self._audit(project, actor, "project.retired")
self.runtime.delete(project.organization_id, project.project_id)
def delete(self, project: Project, actor: str) -> None:
self._owner(project, actor)
if project.status != "retired":
raise ValueError("retire project before deletion")
Code to reality
- Declared intent
- Enforce the product lifecycle so published runtime state is retired before durable deletion.
- Interpreter
- The injected Forge model applies ownership and state-machine rules independently of HTTP or storage technology.
- Software effect
- Retirement removes the hosted runtime and records status before deletion can tombstone control-plane state.
- Hardware effect
- Runtime containers and storage can be released in order while retained evidence remains physically durable.
- Observable evidence
- A denied early delete, successful retirement, absent runtime, tombstoned project, and retained audit event prove order.
Start with the people and the result they need
The source tables below remain the detailed contract. Begin with these customer paths:
- D12-UC-01
- Person: Product owner
- Job: Publish an approved application version to its stable domain
- Observable result: Research Brief, Service Desk, or Field Inspection users receive the exact reviewed artifact
- D12-UC-02
- Person: Release manager
- Job: Withdraw or contain a failed publication before wider exposure
- Observable result: Traffic remains on the prior good release or moves to a safe maintenance response
Turn each customer job into a testable story
Now turn each customer job into a story with a result that an engineer can check:
- D12-US-01
- Story: As a product owner, I want publication to use the approved artifact digest, so that the public result cannot differ from the preview I accepted
- Observable acceptance: Receipt proves source, digest, approval, deployment revision, domain, response, environment, timestamp, run, and trace
- D12-US-02
- Story: As a release manager, I want policy to stop an invalid candidate before traffic changes, so that the last known-good application remains available
- Observable acceptance: Invalid digest or missing approval is denied and the stable domain still serves the recorded prior digest
Add real state and observable proof
Finally trace each story through the system that owns its state and the evidence that proves the outcome:
- D12-FLOW-01
- Trigger: Product owner selects Publish approved version
- Responsible systems: Approval Service, Artifact Registry, Release Orchestrator, Runtime Deployer, Route Controller, generated-app Policy Evaluator
- Authoritative state: Release Store for approved binding; Artifact Registry for digest-addressed bytes; runtime API for deployment and route
- Owned record: ReleaseCandidate
- Observable evidence: Release receipt with actor, resource, scope, preconditions, expected and observed digest, environment, timestamp, approval, run, deployment, and…
- Failure signal: Signature failure, digest mismatch, deployment not ready, TLS failure, or response digest mismatch
- D12-FLOW-02
- Trigger: Release manager submits a candidate with a changed digest or missing approval
- Responsible systems: Release Orchestrator, policy engine, Route Controller, probe runner
- Authoritative state: Release Store for denied decision and current active release
- Owned record: RuntimeRelease
- Observable evidence: Denial reason, rejected digest, unchanged route target, prior-release response, environment, timestamp, and immutable run ID
- Failure signal: Policy denial absent, route changed despite denial, or prior-release probe failed
The enterprise problem and today’s slice
Enterprise problem: A customer can approve a Zheta Forge preview, yet a publish pipeline that rebuilds from a mutable branch may expose different code, prompts, dependencies, or policies to end users. Whole-course context: The incoming evidence is a content-addressed artifact and provider collaborator state; today creates the local release record. Today’s slice: We bind owner authority, artifact digest, idempotency key, runtime payload, and audit action, while treating approval, signature, environment policy, DNS/TLS, and generated-app visibility as unimplemented production gates. End-of-day evidence: The restart/multi-instance test proves one release for a repeated key, and runtime plus audit records carry its artifact and release IDs; there is no live production endpoint proof. Still unsolved: Approval and promotion controls, subsequent change, rollback, and production operations remain deferred.
Customer use cases
Publication is unsafe when “latest” can change after review, because the customer cannot know what end users received. These use cases make the approved bytes and release policy explicit.
| Use case ID | Actor | Customer job | Success outcome | Denial or recovery evidence |
|---|---|---|---|---|
| D12-UC-01 | Product owner | Publish an approved application version to its stable domain | Research Brief, Service Desk, or Field Inspection users receive the exact reviewed artifact | Publish is denied if digest, approval, policy, or environment binding differs from the reviewed candidate |
| D12-UC-02 | Release manager | Withdraw or contain a failed publication before wider exposure | Traffic remains on the prior good release or moves to a safe maintenance response | Failed candidate receives no traffic; prior release positive control and release decision are preserved |
Actor-centred user stories
An approval checkbox has no value if it is not bound to immutable inputs. These stories define publication as a verifiable transition rather than another build.
| Story ID | Use case IDs | User story | Observable acceptance conditions |
|---|---|---|---|
| D12-US-01 | D12-UC-01 | As a product owner, I want publication to use the approved artifact digest, so that the public result cannot differ from the preview I accepted | Receipt proves source, digest, approval, deployment revision, domain, response, environment, timestamp, run, and trace |
| D12-US-02 | D12-UC-02 | As a release manager, I want policy to stop an invalid candidate before traffic changes, so that the last known-good application remains available | Invalid digest or missing approval is denied and the stable domain still serves the recorded prior digest |
End-to-end product flows
A release can pass CI and still serve the wrong bytes, so the flow must trace customer approval through routing to a response. The negative flow proves traffic does not move when the binding is invalid.
| Flow ID | Use case IDs | Path | Trigger | Numbered steps | Terminal evidence |
|---|---|---|---|---|---|
| D12-FLOW-01 | D12-UC-01 | Happy | Product owner selects Publish approved version | 1. Resolve the approved candidate. 2. Verify artifact digest and signatures. 3. Evaluate environment release policy. 4. Deploy the existing artifact. 5. Shift the stable route. 6. Probe identity, generated-app authorization, and response digest. | Release receipt with actor, resource, scope, preconditions, expected and observed digest, environment, timestamp, approval, run, deployment, and trace IDs |
| D12-FLOW-02 | D12-UC-02 | Denied | Release manager submits a candidate with a changed digest or missing approval | 1. Detect binding mismatch. 2. Record policy denial. 3. Leave route on prior release. 4. Probe the prior release as positive control. | Denial reason, rejected digest, unchanged route target, prior-release response, environment, timestamp, and immutable run ID |
The product owner requests one clear transition: make the reviewed application available at its stable domain. The diagram does not yet assume how the platform enforces that promise.
System design derived from the flows
Publication crosses ownership boundaries, so one service must not own approval, artifact bytes, runtime routing, and application visibility. The release orchestrator coordinates these owners using immutable references.
| Use case ID | Entry point | Responsible services | Authoritative store | Failure evidence |
|---|---|---|---|---|
| D12-UC-01 | Control-plane Publish action | Approval Service, Artifact Registry, Release Orchestrator, Runtime Deployer, Route Controller, generated-app Policy Evaluator | Release Store for approved binding; Artifact Registry for digest-addressed bytes; runtime API for deployment and route | Signature failure, digest mismatch, deployment not ready, TLS failure, or response digest mismatch |
| D12-UC-02 | Release policy gate | Release Orchestrator, policy engine, Route Controller, probe runner | Release Store for denied decision and current active release | Policy denial absent, route changed despite denial, or prior-release probe failed |
The control plane owns approval and release policy, the runtime owns deployment and routing, and the generated application owns end-user visibility. None of those grants implies another; the release carries opaque references and each boundary independently authorises its action.
Data model and ownership
A deployment timestamp cannot prove what was published because mutable tags and reconstructed metadata can lie. Durable records bind the approved digest to the active runtime revision and customer evidence.
Generated-application database: Not created in this runnable slice — the control plane records artifact and release identities while generated-app visibility, tenant roles, and domain records remain an unimplemented application-owned boundary.
| Record or entity | Store and owner | Primary key | Foreign key or opaque reference | Tenant key | Material constraint | Lifecycle and deletion | Use case IDs |
|---|---|---|---|---|---|---|---|
| ReleaseCandidate | Control-plane Release Store, owned by Release Orchestrator | release_candidate_id | Opaque project_revision_id and artifact_digest | organization_id | Approval binds exact digest, policy version, and target environment | Create from preview, approve or reject, retain as immutable audit evidence | D12-UC-01, D12-UC-02 |
| RuntimeRelease | Hosted-runtime deployment store, owned by Runtime Deployer | runtime_release_id | Opaque release_candidate_id and artifact_digest | runtime_tenant_id | One active route target per application and environment; artifact is digest-addressed | Deploy, mark ready, activate or fail, retain for rollback, delete after retention | D12-UC-01, D12-UC-02 |
| PublicationVisibility | Generated-application database, owned by Policy Evaluator | visibility_policy_id | Opaque runtime_release_id | app_tenant_id | Visibility change cannot grant a role or tenant not declared by the application | Draft, activate with release, revoke, export, delete with tenant policy | D12-UC-01, D12-UC-02 |
The immutable digest progresses through explicit release states; it is never rebuilt between approval and deployment. Route and response evidence lets a reviewer distinguish a correct publish, a denied candidate, and a runtime failure.
Promote bytes, not branch names
A branch or mutable image tag can point at different content over time, which destroys the chain from review to deployment. The current model publishes the latest content-addressed artifact with an idempotency key; it does not implement the proposed ApplicationRelease custom resource, approval receipt, signature verification, or production promotion gate. Read the exact operation in services/control_plane/domain.py.
def publish(self, project: Project, actor: str, idempotency_key: str) -> dict[str, str]:
self._owner(project, actor)
if not idempotency_key:
raise ValueError("Idempotency-Key required")
existing = next((release for release in project.releases if release["idempotency_key"] == idempotency_key), None)
if existing:
return existing
| Effect field | What happens |
|---|---|
| Declared intent | Require owner authority and a request idempotency key so a retried publish returns the same release |
| Interpreter | Python executes Forge.publish; repository, artifact-store, runtime, and event adapters handle boundary effects |
| Software effect | A new release points at the latest stored artifact, project status becomes published, and runtime receives the artifact source; duplicate keys return the existing release |
| Hardware effect | Local service CPU, network sockets, and SQLite volumes are used; no EKS node, load balancer, or AWS production resource is created |
| Evidence | test_restart_multi_instance_and_idempotent_publish, rel-1, artifact digest, runtime payload, and release.published event prove the implemented local behavior |
Key takeaways
Publication is a policy-bound routing transition to an already reviewed immutable artifact.
- Approval must bind digest, environment, and policy version.
- Runtime readiness and customer response are stronger evidence than a successful pipeline.
- A denied candidate must leave the prior release observably healthy.
Checklist
A release is publishable only when review and runtime identity remain connected.
- [ ] Artifact is addressed by digest and signature result is recorded
- [ ] Approval binds exact inputs and target environment
- [ ] Provider, runtime, and generated-app authorizations remain separate
- [ ] Positive response and denied-candidate evidence include immutable IDs