The workspace-engine is the Go service that drives every release forward. It polls a Postgres work queue (Documentation Index
Fetch the complete documentation index at: https://docs.ctrlplane.dev/llms.txt
Use this file to discover all available pages before exploring further.
reconcile_work_scope), leases items by kind,
and runs the matching controller. Each controller’s output is enqueueing more
work, so a single release moves through phases by chaining items through the
queue.
The release-flow chain
When a release-target needs to be evaluated (a new version was created, a policy changed, a job finished, a resource started matching), adesired-release work item lands in the queue. From there:
Four controllers, one queue between them. No controller calls another
directly — handoff is always via insert-then-lease. That means each phase is
independently retriable, leasable, and observable, and the engine can run as
multiple instances safely.
How every controller works
Every controller is areconcile.Processor registered for one kind. The
pattern is identical across all of them: lease an event, recompute the desired
state from current Postgres state, persist the result, enqueue follow-up.
Two things make this a reconciler rather than a job runner. First,
controllers are stateless — every invocation re-reads input from Postgres
rather than carrying state forward in memory. If the world changes between
events (a policy is disabled, an approval lands, a new version appears), the
next event picks up the change automatically. Second, the loop closes back
to the start — when a job finishes, jobverificationmetric enqueues another
desired-release event and desiredrelease recomputes from scratch.
Idempotent recomputation is the orchestration model.
Inside desiredrelease
desiredrelease is the only controller in the chain that does meaningful
internal work — the other three are mostly routing or checking. Here is what
happens on a single lease:
Two things worth knowing:
- Policy evaluation is inline, not a separate controller. A
policyevaldirectory exists atsvc/controllers/policyeval/but that’s a different controller that writes per-version rule evaluations for the UI. The gating logic that decides whether a version can deploy lives in thepolicyevallibrary subpackage atsvc/controllers/desiredrelease/policyeval/and is called as a function from insidedesiredrelease. - Versions are evaluated newest-first as a stream. The controller doesn’t load all candidate versions then filter — it iterates them and stops at the first one that passes all policy rules. That’s what makes “skip blocked versions but deploy the newest passing one” cheap.
Other release-flow controllers
jobeligibility — given a release record, decides whether a job can run
right now. Runs two evaluators: releasetargetconcurrency (under the
configured concurrency cap?) and retry (under the retry budget?). If both
pass, enqueue job-dispatch. If not, requeue with notBefore.
jobdispatch — given a job, picks the right job-agent adapter (GitHub
Actions, ArgoCD, Terraform Cloud, Argo Workflows, or the test runner) and
sends the job over HTTPS. The agent’s externalId is recorded so results can
be correlated back later.
jobverificationmetric — given a finished job, polls verification
providers (Datadog, HTTP probes, Terraform Cloud run status, etc.) until they
return pass/fail. On completion, calls EnqueueDesiredRelease to close the
loop.
Controllers outside the release-flow chain
Thesvc/controllers/ directory contains several other controllers that exist
for UI surface or precomputed state, not for moving a release through phases:
policyeval(top-level) — computes per-version rule evaluations so the UI can show “why isn’t this version deploying yet.”deploymentplan/deploymentplanresult— power plan previews and dry-run views.deploymentresourceselectoreval/environmentresourceselectoreval— precompute which resources currently match a deployment or environment selector.relationshipeval— evaluates resource relationship rules into the resource graph.forcedeploy— handles user-triggered manual deploys (a separate path from the policy-gated chain).