Environment promotion
Promotion order (preview, then staging, then production) is a standard a platform declares once, in intent.yaml. Within a single plan orun compiles it into DAG edges the runner enforces. Across separate plans it compiles into evidence gates that are recorded on the plan but not yet enforced; see standards.
Environment promotion dependencies define ordering and gating relationships between environments. They express the deployment pipeline flow — for example, staging must succeed before production can run.
This is separate from component-level dependsOn, which expresses service-to-service dependencies within an environment.
Syntax
Add promotion.dependsOn to any environment that requires prior environment success:
environments:
preview:
activation:
triggerRefs:
- github-pull-request
parameterDefaults:
"*":
lane: verify
staging:
activation:
triggerRefs:
- github-push-main
promotion:
dependsOn:
- environment: preview
parameterDefaults:
"*":
lane: verify
production:
activation:
triggerRefs:
- github-tag-release
promotion:
dependsOn:
- environment: staging
parameterDefaults:
"*":
lane: release
With defaults applied, each entry is equivalent to:
promotion:
dependsOn:
- environment: staging
strategy: same-component
condition: success
satisfy: same-plan-or-previous-success
match:
revision: source
How It Compiles
The behavior depends on whether the dependency environment is active in the same plan.
Same-plan (both environments active)
When both environments are activated by the same trigger (or selected with --env), promotion dependencies compile into normal DAG edges:
web-app.dev.deploy → web-app.staging.deploy
api.dev.deploy → api.staging.deploy
The plan output uses standard dependsOn:
{
"id": "web-app.staging.deploy",
"dependsOn": ["web-app.dev.deploy"]
}
Cross-plan (dependency environment not active)
When environments are activated by different triggers (e.g., PR activates preview, push activates staging), the dependency environment won't be in the same plan. In this case, promotion compiles into gates: a record of the prior success the job is meant to require.
{
"id": "web-app.production.release",
"dependsOn": [],
"gates": [
{
"type": "environment-promotion",
"environment": "staging",
"component": "web-app",
"condition": "success",
"match": { "revision": "source" }
}
]
}
Gates encode that the same component should have succeeded in the referenced environment for the same source revision.
Cross-plan promotion gates are recorded in the plan but not yet enforced at run time — cross-pipeline (cross-invocation) gating is deferred (the env-scoping "Z" model calls this "Option C"). In-plan dependsOn ordering is the only enforced promotion mechanism. orun plan/run print a one-line notice when a plan carries gates, so you know they are not gating execution. To order a promotion chain end-to-end today, include the related environments in one plan/run (e.g. --all-envs). See the env-scoping spec.
Strategies
same-component (default)
Each component in the target environment depends only on the same component in the dependency environment:
web-app@dev → web-app@staging
api@dev → api@staging
Components not subscribed to both environments are gracefully skipped.
environment-barrier
Every job in the target environment waits for every job in the dependency environment:
web-app@dev ─┬─→ web-app@staging
api@dev ─┘ ┌→ api@staging
web-app@dev ───┘
api@dev ────→ api@staging
Use this for strict release-train semantics where the entire environment must be green before promotion.
Satisfy Modes
The satisfy field controls how the dependency is checked:
| Mode | Behavior |
|---|---|
same-plan | Uses in-plan DAG ordering when both environments are in the plan. If the dependency environment is not in the (scoped) plan, the edge is pruned with a warning and the selected environment runs standalone (env-scoping "Z" model — earlier releases errored here). |
previous-success | Records a prior-evidence gate. Gates are advisory today (see the note above) — not enforced at run time. |
same-plan-or-previous-success | Uses in-plan DAG edges when both are in the plan; records an (advisory) gate otherwise. (default) |
Match
The match field controls how prior success evidence is validated:
match:
revision: source
revision: source means the gate checks that the same source commit (or the commit pointed to by a tag) succeeded in the prior environment.
Interaction with Trigger Bindings
Promotion dependencies work alongside trigger bindings and environment activation:
- Trigger activation decides which environments are active in a given plan
- Promotion dependencies decide ordering and gating between those environments
- Component dependencies decide ordering within the active set
A typical setup:
automation:
triggerBindings:
github-pull-request:
on:
provider: github
event: pull_request
plan:
scope: changed
github-push-main:
on:
provider: github
event: push
branches: [main]
plan:
scope: changed
github-tag-release:
on:
provider: github
event: push
tags: ["v*"]
plan:
scope: full
environments:
preview:
activation:
triggerRefs: [github-pull-request]
parameterDefaults:
"*":
lane: verify
staging:
activation:
triggerRefs: [github-push-main]
promotion:
dependsOn:
- environment: preview
parameterDefaults:
"*":
lane: verify
production:
activation:
triggerRefs: [github-tag-release]
promotion:
dependsOn:
- environment: staging
parameterDefaults:
"*":
lane: release
This gives you:
- PR → preview only (validate changes)
- Push to main → staging only (a recorded gate: preview should have passed for this commit)
- Tag → production only (a recorded gate: staging should have passed for this commit)
The gates are recorded, not enforced (see the note above). To make the order binding today,
plan the environments together, for example with --all-envs.
Validation
Orun validates promotion dependencies at normalization time:
- Referenced environments must exist
- Self-references are rejected
- Cycles are detected (e.g., A → B → A)