Proposal lifecycle
A proposal is the DevOps Engineer’s answer to “how should this service be built and deployed?”, turned into files for your platform repo. This page follows one from the button to a running pod.
Analysis ──▶ Promote to proposal ──▶ DevOps Engineer emits files
│
▼
1. Pre-emit validation ── fail ──▶ stop, no PR
│
▼
2. Constitution gate (OPA) ── deny ──▶ stop, no PR
│
▼
3. Advisory gap check (never blocks)
│
▼
4. Pull request on your platform repo
│ you review and merge
▼
5. Merge detected
│ you sync in ArgoCD
▼
6. Deploy observed (optional)
│
▼
7. Next commit: tag promotion proposalEvery step appears on the proposal’s timeline (#/proposals/<id>).
The diagram is for a Kubernetes target. For an ECS Fargate target (Beta), merging the pull request is the deploy and there is no ArgoCD sync or deploy observation yet; see ECS Fargate targets.
Before: analysis
On a service, Run analysis starts the Code Reviewer. It clones the service repo in your runner and reports framework, language, runtime, dependencies, entrypoints, and whether the repo has tests, a Dockerfile, and CI. When it finishes, Promote to proposal starts the DevOps Engineer.
The service must belong to an Application with at least one deployment
target (a target cluster in the app). The proposal is built for the
Application’s first target: its name, its tier (dev, uat or prod) and
its kind (kubernetes or ecs-fargate). The kind decides which files the
DevOps Engineer can emit at all: a Fargate run is never offered the Helm,
Kustomize or ArgoCD tools, and a Kubernetes run is never offered the task
definition tool.
The DevOps Engineer emits
Grounded in the matching framework pack, your environment profile, and what is already in your platform repo, it emits the files described in Platform repo layout. The image reference and tag are supplied to it (your registry, your repo name, the commit it analyzed); it does not invent them.
1. Pre-emit validation
Does the output actually work? Each check, by name on the timeline:
| Check | Fails when |
|---|---|
chart_renders | helm template fails on the chart |
overlay_builds | kustomize build fails on an overlay |
workflow_parses | A workflow or ArgoCD Application is not valid YAML |
action_sourced | A uses: action is not pinned to a full commit SHA, or does not come from a curated pack |
k8s_identifier | A name or label breaks Kubernetes naming rules (RFC 1123), or a Helm release name is too long |
image_reference | The image repository is not the one TruStacks supplied (in Helm values or an ECS task definition) |
task_definition_valid | ECS Fargate: the task definition is not a JSON object; a required field is missing; it is not FARGATE with awsvpc; container names repeat; cpu and memory are not a valid Fargate pair; a port is out of range or hostPort differs from containerPort; privileged is set; or a key is unknown or mis-cased |
binding_valid | ECS Fargate: deploy-target.json does not have exactly the expected keys, or a value that is set does not match its format |
Mis-cased keys are refused for a reason. The AWS SDK inside the deploy
action silently drops a task-definition key it does not recognise. So
readOnlyRootFilesystem: true (capital O) registers a writable root
filesystem and looks right in review. The check names the key and its
correct spelling. Unit forms such as "1 vCPU" and "2 GB" are normalised,
not refused, and the 32 vCPU sizes (61440, 122880 and 249856 MiB) are
accepted.
What cannot be proven offline, and is not claimed: that the image exists, that your roles, cluster and service exist, that the IAM is right, that the CPU architecture matches the image, and that the service runs on Fargate. The deploy waits for the service to stabilise, so these fail visibly at deploy time rather than hanging.
A check that could not run counts as a failure. If a tool is missing or a pack did not load, the proposal stops as unverified rather than passing unchecked. On failure no pull request is opened, and the timeline shows the exact error.
2. Constitution gate
The runner evaluates the constitution (and any
overlay it loaded) against the files with OPA, inside your cluster. Any deny
stops the proposal. Overlay denies only add to the constitution’s; from
runner 0.2.14 an overlay that reaches outside data.overlay is rejected and
the proposal gets no verdict (see
your own rules). The timeline lists each rule that fired and why. Nothing
is written to your platform repo.
Denials count toward Policy in force on the Overview: how many proposals were checked and which rules refused work, over a window.
3. Advisory gap check
The runner runs the constitution’s gap query against your environment profile and lists tooling categories you have not declared. Advisory only: it never blocks the pull request.
4. Pull request
The runner pushes a branch to your platform repo and opens a pull request. The body explains what changed and why, cites the rules and profile entries behind each choice, and lists preservation notes for anything of yours it kept.
TruStacks never merges. You review and merge, under your branch protection.
5. Merge detection
Hosted TruStacks polls GitHub for the pull request’s state. No webhook to configure. On merge it records the merge commit, which is what ArgoCD will sync. The proposal shows open, merged, or closed.
6. Deploy observed (optional)
Applications are emitted with manual sync, so someone syncs in ArgoCD after
the merge. If the runner’s observer is on
(observer.enabled=true, see Runner Helm chart),
the runner watches the ArgoCD Application read-only and reports when it is
Synced and Healthy at the merged commit. The Application’s deploy-status chip
shows the result.
7. Image tag promotion
Once your service builds images, each new commit on the service repo’s
default branch needs to reach the platform repo as a new image.tag. Hosted
TruStacks polls the service repo’s default branch; when it moves, the runner
opens a new proposal that bumps the tag. It goes through steps 1 to 4 like any
other proposal, so every tag change in production has a pull request, a
verdict, and an approver.
The tag can be proposed before the image exists. If you merge before CI
finishes pushing the image, the pod waits in ImagePullBackOff until the
image lands, then starts. If the image never lands (CI broken, registry
credentials revoked), it waits indefinitely. The deploy-status chip shows
it; nothing alerts you yet.
The tag is the service commit SHA. If the deployed tag is older than the newest commit, that is usually an open promotion pull request waiting for review.
ECS Fargate targets (Beta)
Steps 1 to 4 are the same. After that:
- Merging deploys. The deploy workflow runs on the push to
mainthat the merge makes, applies the committed task definition, and waits for the ECS service to stabilise. There is no Sync step, so the pull request review is the human approval. On aprod-tier target the deploy job must name a GitHub environment; required reviewers on that environment, where your GitHub plan offers them, add a second approval. - The first merge’s deploy fails until you fill in
deploy-target.json. TruStacks emits it blank. The failed run names the missing key. - No deploy observation yet. The runner’s observer watches ArgoCD only. Check the deploy workflow’s run in GitHub Actions.
- Tag promotion works the same way. A new service commit produces a proposal with the new commit-SHA tag in the task definition, and merging it deploys. If you merge before CI has pushed the image, the deploy job fails visibly instead of waiting.
- Rollback is a revert pull request.
See ECS Fargate layout for the files and your AWS prerequisites.
Where to go next
- Platform repo layout · what the files look like
- Constitution · the rules at step 2
- Policy in force · which policy your runner enforced