Skip to content

Manifest reference

The manifest is the single input cascade compiles into GitHub Actions workflows. This page documents every field, grouped by the order you meet them, from the fields nearly everyone sets down to the reserved and validated-only tails almost nobody touches.

Each field carries an emission status:

StatusMeaning
emittedChanges generated workflow output.
validated-onlyParsed and schema-checked, but never appears in generated YAML.
reservedParsed, but has no generator consumption today.

A manifest holds both pipeline configuration and managed deployment state under a top-level wrapper key (ci: by default):

ci:
config: # Pipeline definition (you write this)
schema_version: 1
trunk_branch: main
environments: [dev, test, prod]
# builds, deploys, and the rest below
state: # Deployment tracking (managed by cascade; do not edit)
dev:
sha: "abc123"
version: "v1.2.0-rc.3"
latest_release: # Most recent published release (managed)
version: "v1.1.0"
sha: "abc000"

The wrapper key is set by manifest_key and the file path by manifest_file.

FieldStatusTypeDefaultDescription
schema_versionemittedint1 when omittedManifest schema generation. Omitting it emits a CLI warning; set schema_version: 1 in every manifest.
manifest_fileemittedstring.github/manifest.yamlPath to the manifest file.
manifest_keyemittedstringciTop-level wrapper key inside the file.
action_folderemittedstringmanage-releaseFolder name for the generated manage-release action.

Every field below lives under ci.config unless stated otherwise. The ci.state block is covered in State section.

cascade ships a hand-authored JSON Schema. Registering it gives autocomplete, type checking, enum hints, and hover docs while you author the manifest. The schema covers structure, types, and enums; cascade parse-config remains the authority for semantic and cross-field rules.

The schema is published at:

https://stablekernel.github.io/cascade/manifest.schema.json

Print the embedded copy with cascade schema (write it to a file with cascade schema --output manifest.schema.json).

Add this directive to the top of the manifest so the YAML language server (VS Code, Neovim, and others) picks it up automatically:

# yaml-language-server: $schema=https://stablekernel.github.io/cascade/manifest.schema.json
ci:
config:
schema_version: 1
trunk_branch: main

Or map the schema to your manifest path in VS Code settings.json:

{
"yaml.schemas": {
"https://stablekernel.github.io/cascade/manifest.schema.json": ".github/manifest.yaml"
}
}

The two fields that define the pipeline shape.

FieldStatusTypeRequiredDefaultDescription
trunk_branchemittedstringYesmainThe trunk branch releases flow from.
environmentsemittedlistNo-The promotion chain. Omit for a no-environment library or CLI project.
ci:
config:
schema_version: 1
trunk_branch: main
environments: [dev, test, prod]
cli_version: v0.9.1
FieldStatusTypeDefaultDescription
triggersemittedlist-Global path patterns that activate orchestration. See Trigger patterns.
release_triggeremittedstringpushHow orchestrate fires. push keeps push-on-trunk plus workflow_dispatch; dispatch drops push: so releases run only on manual workflow_dispatch.
tag_prefixemittedstringvVersion tag prefix.

Workflow-level trigger types beyond push are set under extra_triggers.

Optional, additive block that reshapes the release tag grammar. A manifest that omits it produces cascade’s historical grammar exactly: vX.Y.Z releases, -rc.N pre-releases, and .hotfix.M hotfixes, so existing repositories are unaffected.

ci:
config:
tag_grammar:
prefix: v
prerelease_token: rc
prerelease_separator: "."
dryrun_token: dryrun
strict_prefix: false
FieldStatusTypeDefaultDescription
prefixemittedstringvLiteral prefix cascade puts on every new tag.
prerelease_tokenemittedstringrcToken that marks a release-candidate tag.
prerelease_separatoremittedstring.Separator between the token and its number. . yields rc.4; an empty string yields rc4.
dryrun_tokenemittedstringdryrunToken that marks a rehearsal tag.
strict_prefixemittedboolfalseWhen false, reads accept any alphabetic prefix so historical and foreign-cased tags still parse. When true, reads require the exact configured prefix.

Relationship to tag_prefix. tag_prefix still sets the prefix on its own when tag_grammar is absent. When both tag_prefix and tag_grammar.prefix are set, tag_grammar.prefix wins, and cascade parse-config emits a non-fatal warning naming both keys so the redundancy is visible. Resolution is well defined either way; the warning is advisory only.

Reading pre-existing tags. On read, cascade tolerates a pre-existing foreign pre-release shape (for example beta.1 or rc1) and build metadata (for example +build.5) so a repository whose history predates tag_grammar stays visible to version discovery. cascade never emits those shapes itself, and a recognized foreign pre-release always sorts below its release.

cascade’s own releases. cascade’s own self-release workflows stay pinned to the default grammar regardless of what a driven repository configures. tag_grammar reshapes the tags a driven repository’s pipeline cuts, not cascade’s own release process.

See Versioning and schema for the hotfix version grammar and the full reserved-shapes catalog.

These fields pin the cascade CLI and third-party actions the generated workflows install.

FieldStatusTypeDefaultDescription
cli_versionemittedstringlatestcascade CLI version the generated workflows install via setup-cli. Use latest, a prerelease channel, or a specific vX.Y.Z.
cli_version_shaemittedstring-40-hex commit SHA that cli_version resolves to. Under pin_mode: sha, the generated setup-cli ref pins to this commit.
pin_modeemittedstringtagThird-party action pin policy: tag emits <action>@<major-tag>; sha emits <action>@<commit-sha> with the version as a trailing comment.
action_pinsemittedmap-Per-action ref overrides keyed by action path (for example actions/checkout), applied regardless of pin_mode.
ValueBehavior
latestMost recent stable release (default).
betaNewest prerelease build.
vX.Y.ZA specific version (for example v0.9.1). Pin for reproducibility.

Under pin_mode: sha, pair cli_version with cli_version_sha, the 40-character lowercase-hex commit the cli_version tag resolves to. The generated setup-cli ref then pins to that immutable commit, with cli_version carried as a trailing comment:

uses: stablekernel/cascade/.github/actions/setup-cli@9dc69a1f66753a3865c38c34eca5a931f677c803 # v0.9.1

The with: version: input the action reads to select the release asset stays the human-readable tag, so only the action source is pinned to a commit. The field is optional and takes effect only under pin_mode: sha. Because cascade release tags are annotated, resolve the underlying commit (not the tag object) with git ls-remote https://github.com/stablekernel/cascade 'refs/tags/<tag>^{}'.

Generated workflows are build output. cascade owns the third-party action pins inside them (for example actions/checkout and actions/github-script) and reconciles that ownership back to the manifest. The supported way to change a pinned action is pin_mode and action_pins, not a hand-edit of the generated YAML. A hand-edited pin is reported as drift by the next cascade verify and overwritten by the next regenerate.

pin_mode sets the reference style for every third-party action cascade emits:

ValueBehavior
tagDefault. Emits <action>@<major-tag> (for example actions/checkout@v4). Never @latest.
shaEmits <action>@<commit-sha> # <version>, pinning each action to an immutable commit with the human-readable version as a trailing comment. Under sha, pair cli_version with cli_version_sha so the cascade self-action ref is pinned too.

The sha values come from a single committed pin table (internal/generate/action_pins.yaml); no per-repo configuration is needed beyond setting pin_mode: sha.

action_pins overrides the built-in ref for individual actions, keyed by action path. The value is the bare ref emitted after @ for that same action (a tag or a commit SHA); it cannot repoint an action to a different owner or repository. An override applies regardless of pin_mode:

ci:
config:
pin_mode: sha
action_pins:
actions/checkout: 0123456789abcdef0123456789abcdef01234567

That emits uses: actions/checkout@0123456789abcdef0123456789abcdef01234567. An action neither in the built-in table nor overridden is emitted unchanged. action_pins is also the write target for cascade reconcile, which adopts an external governed-pin change (for example a Dependabot bump) into the manifest.

Two seams call GitHub on cascade’s behalf: release_token for release API calls and the rc tag, and state_token for writing manifest state to trunk. Both default to ${{ secrets.GITHUB_TOKEN }}, which is enough for a single-repo project whose trunk is unprotected.

FieldStatusTypeDefaultDescription
release_tokenemittedstringstate_token if set, else ${{ secrets.GITHUB_TOKEN }}Token expression for release API calls and the rc tag. Inherits state_token when unset so the rc-to-release chain has a trigger-capable token.
state_tokenemittedstring${{ secrets.GITHUB_TOKEN }}Token expression for writing manifest state to the trunk branch.
release_token_appemittedobject-GitHub App identity that mints a release token at run time (app_id, private_key).
state_token_appemittedobject-GitHub App identity that mints a state-write token at run time (app_id, private_key).

Reference secrets by bare name; cascade wraps a bare name in a ${{ secrets.* }} expression for you:

ci:
config:
release_token: RELEASE_PAT
state_token: STATE_PAT

A GitHub App avoids storing a long-lived PAT. Point release_token_app and state_token_app at App secrets; cascade mints a fresh, short-lived installation token per run via actions/create-github-app-token, guarded to real GitHub with if: ${{ github.server_url == 'https://github.com' }}:

ci:
config:
release_token_app:
app_id: CASCADE_APP_ID
private_key: CASCADE_APP_PRIVATE_KEY
state_token_app:
app_id: CASCADE_APP_ID
private_key: CASCADE_APP_PRIVATE_KEY

Add the App to the repository ruleset bypass list so it can write a protected trunk. Store only the private key as a secret. On act or gitea the minting step is skipped (the github.server_url guard does not match) and consumers fall back to the static release_token / state_token, so set both an App source and a static token if you run the same manifest locally and against real GitHub.

Optional git identity and signing configuration for state commits.

ci:
config:
git:
mode: custom
user_name: deploy-bot
user_email: bot@example.com
gpg_key_id: GPG_KEY_ID
gpg_key_secret: GPG_PRIVATE_KEY
FieldStatusTypeDefaultDescription
modeemittedstringdefaultdefault, custom, or external.
user_nameemittedstringgithub-actions[bot]Git user.name (when mode: custom).
user_emailemittedstringgithub-actions[bot]@users.noreply.github.comGit user.email.
gpg_key_idemittedstring-Secret name holding the GPG key ID.
gpg_key_secretemittedstring-Secret name holding the GPG private key.

default uses the github-actions[bot] identity, custom uses your supplied name and email, and external skips git config entirely (the runner is assumed pre-configured). When both gpg_key_id and gpg_key_secret are set, cascade imports the key, enables commit.gpgsign, and signs state commits.

Optional pre-build validation callback.

ci:
config:
validate:
workflow: .github/workflows/validate.yaml
supports_dry_run: false
triggers: [src/**]
inputs:
check_lint: true
env_inputs:
prod:
check_security: true
run_policy: default
on_failure: abort
retries: 0
FieldStatusTypeDefaultDescription
workflowemittedstring-Path to the validation workflow.
supports_dry_runemittedboolfalseWhether the callback handles the dry_run input.
triggersemittedlist-File patterns that trigger validation.
inputsemittedmap{}Static inputs passed to the workflow.
env_inputsemittedmap{}Per-environment input overrides.
run_policyemittedstringdefaultExecution policy. See Policy fields.
on_failureemittedstringabortFailure handling. See Policy fields.
retriesemittedint0Retry attempts (0-3).

Builds produce artifacts (container images, binaries, and the like). builds is a list.

ci:
config:
builds:
- name: app
workflow: .github/workflows/build-app.yaml
triggers: [src/**, Dockerfile]
depends_on: []
inputs:
dockerfile: ./Dockerfile
env_inputs:
prod:
sign_image: true
permissions:
contents: read
id-token: write
matrix:
dimensions:
os: [linux, darwin]
arch: [amd64, arm64]
max_parallel: 4
fail_fast: false
state_tags: [image_tag]
auto_commits: false
run_policy: default
on_failure: abort
retries: 0
FieldStatusTypeRequiredDescription
nameemittedstringYesUnique build identifier.
workflowemittedstringYesPath to the build workflow.
triggersemittedlistNoGlob patterns that trigger this build.
depends_onemittedlistNoOther callbacks to wait for (hard dependency).
optional_depends_onemitted (ordering)listNoSoft dependency: orders this job after the named jobs when they exist, without failing generation if a name is absent. The counterpart to depends_on.
inputsemittedmapNoStatic inputs to the workflow.
env_inputsemittedmapNoPer-environment input overrides.
permissionsemittedmapNoJob-level permissions: for this callback’s caller job. See Permissions.
matrixemittedobjectNoBuild fan-out. See matrix.
state_tagsemitted (behavior)listNoState-capture tags recorded for this build (the field behind the callback contract’s State Capture).
auto_commitsemitted (behavior)boolNoWhen true, cascade captures the HEAD sha the callback advanced to (runtime env AUTO_COMMITS_HEAD_SHA), for callbacks that commit during their run.
run_policyemittedstringNoExecution policy. See Policy fields.
on_failureemittedstringNoFailure handling. See Policy fields.
retriesemittedintNoRetry attempts (0-3).
runs_onvalidated-onlyobjectNoParsed and validated, never emitted. See Validated-only fields.
concurrencyvalidated-onlyobjectNoParsed and validated, never emitted. See Validated-only fields.
timeout_minutesvalidated-onlyintNoParsed and validated, never emitted. See Validated-only fields.

The build’s artifact_id output (if declared) is captured into state automatically. Other declared outputs are forwarded to dependent deploys as inputs.

matrix (builds only) fans a build across a cross-product of dimensions.

Sub-fieldStatusTypeDescription
dimensionsemittedmapThe cross-product axes (for example os: [linux, darwin], arch: [amd64, arm64]).
max_parallelemittedintCaps concurrent matrix legs (0 uses the GitHub Actions default).
fail_fastemittedboolWhether a failing leg cancels the rest. Unset applies the GitHub Actions default (true for matrix builds).

A permissions map renders as a job-level permissions: block on the caller job that invokes the callback, scoping the GITHUB_TOKEN to least privilege for that one job. GitHub Actions treats a job-level block as the complete permission set: it replaces the workflow default rather than merging. Declare the full set the callback needs, including contents: read if it checks out code and id-token: write for OIDC. cascade emits exactly the scopes you declare and never injects an implicit one.

permissions:
contents: read
id-token: write

This is the shipped OIDC answer: a per-callback permissions: block carrying id-token: write grants that one job an OIDC token without widening any other job.

Deploys target environments. deploys is a list and shares most fields with builds.

ci:
config:
deploys:
- name: infra
workflow: .github/workflows/deploy-infra.yaml
triggers: [cdk/**]
supports_dry_run: true
depends_on: []
permissions:
contents: read
id-token: write
rollout:
max_parallel: 2
fail_fast: false
run_policy: default
on_failure: abort
retries: 0
FieldStatusTypeRequiredDescription
nameemittedstringYesUnique deploy identifier.
workflowemittedstringYesPath to the deploy workflow.
triggersemittedlistNoGlob patterns that trigger this deploy.
depends_onemittedlistNoOther callbacks to wait for.
optional_depends_onemitted (ordering)listNoSoft dependency, as for builds.
supports_dry_runemittedboolNoWhether the callback handles dry_run.
inputsemittedmapNoStatic inputs.
env_inputsemittedmapNoPer-environment overrides.
permissionsemittedmapNoJob-level permissions: for the caller job. See Permissions.
rolloutpartialobjectNoDeploy rollout strategy. See rollout.
state_tagsemitted (behavior)listNoState-capture tags recorded for this deploy.
auto_commitsemitted (behavior)boolNoCaptures the advanced HEAD sha, as for builds.
run_policyemittedstringNoExecution policy.
on_failureemittedstringNoFailure handling.
retriesemittedintNoRetry attempts (0-3).
runs_onvalidated-onlyobjectNoParsed and validated, never emitted.
concurrencyvalidated-onlyobjectNoParsed and validated, never emitted.
timeout_minutesvalidated-onlyintNoParsed and validated, never emitted.

Deploys are classified by their configuration:

TypeConfigurationWhen it runs
Trigger-basedHas triggersWhen matching files change.
Build-linkedHas depends_on referencing a buildWhen the referenced build runs.
UnconstrainedNo triggers or depends_onAlways runs.

Build-linked deploys inherit the build’s triggers for change detection during promotions.

rollout (deploys only) tunes the deploy job’s strategy: block. Its status is partial: two sub-fields are emitted, the rest are reserved.

Sub-fieldStatusTypeDescription
max_parallelemittedintCaps concurrent rollout waves. Emitted into the deploy job’s strategy: block.
fail_fastemittedboolWhether a failing wave cancels the rest. Emitted into the strategy: block. Unset differs from an explicit false.
typereservedstringdefault, rolling, canary, or blue_green. Parsed, not yet wired to generation.
canaryreservedobjectCanary sub-block (steps, analysis, percent, bake_time, promote_callback, rollback_callback). Inert today.
blue_greenreservedobjectBlue/green sub-block. Inert today.

See Versioning and schema for the full reserved rollout shape.

The publish callback runs once per build when a release is published, at the point where an rc version becomes a final semver. Use it to retag artifacts that still carry their rc version.

ci:
config:
publish:
workflow: .github/workflows/publish.yaml
FieldStatusTypeRequiredDescription
workflowemittedstringYesPath to the publish workflow (reusable, workflow_call trigger).

publish also accepts permissions, rollout, state_tags, and auto_commits with the same semantics as a deploy, and runs_on / concurrency / timeout_minutes as validated-only. The callback is invoked once per configured build and receives build_name, old_version (the rc version in the registry), new_version (the final semver), sha, and artifact_id (the immutable digest from the build’s artifact_id output). cascade carries metadata only; the publish workflow performs the registry operation.

external (primary repos) and notify (satellite repos) coordinate deployments across repositories. A repository cannot set both.

external is designed for satellite-repo artifact coordination: a satellite owns its own build, deploys to its first environment, then notifies the primary, which records the satellite’s SHA and version in the shared manifest and includes its deploys in every subsequent promotion. It is not a GitOps mirror.

ci:
config:
external:
- repo: org/cdk-infra
ref: main
deploys:
- name: cdk
workflow: .github/workflows/deploy-cdk.yaml
triggers: [cdk/**]
on_update:
deploy:
workflow: org/cdk-infra/.github/workflows/deploy.yaml
FieldStatusTypeRequiredDescription
repoemittedstringYesExternal repository (for example org/cdk-infra).
refemittedstringNoBranch or tag reference (default: trunk_branch).
deploysemittedlistYesDeployables from this repo.
deploys[].nameemittedstringYesUnique deploy identifier.
deploys[].workflowemittedstringYesWorkflow path (local, or org/repo/.github/workflows/x.yaml@ref for external).
deploys[].triggersemittedlistNoFile patterns for change detection.
deploys[].on_update.deploy.workflowemittedstringNoReusable workflow to run as a scoped deploy when this slot is recorded.

By default the receiver is record-only. Setting on_update.deploy.workflow opts a component into a scoped deploy that runs synchronously in the same receiver run, right after the slot is recorded and only if the record step succeeds. Inline run: and shell: are not supported. See Coordinate multiple repos for the operator recipe.

For satellite repos that report deployments back to a primary.

ci:
config:
notify:
repo: org/my-backend
workflow: external-update.yaml
token: PRIMARY_REPO_TOKEN
deploy_name: artifact-a
environment: staging
FieldStatusTypeRequiredDefaultDescription
repoemittedstringYes-Primary repository to notify.
workflowemittedstringNoexternal-update.yamlWorkflow name.
tokenemittedstringNoPRIMARY_REPO_TOKENSecret name for cross-repo dispatch.
deploy_nameemittedstringNofirst local deploy, then first buildDeploy name to dispatch, when the primary recognizes this satellite under a different name.
environmentemittedstringNofirst local environment, then devEnvironment to dispatch, when the primary expects a different one.

The primary validates the dispatched deploy_name and environment against its own config, so a satellite whose local names differ from what the primary expects must send the parent-recognized values.

ci:
config:
release:
disabled: false
workflow: .github/workflows/release-assets.yaml
changelog:
disabled: false
contributors: true
FieldStatusTypeDefaultDescription
disabledemittedboolfalseDisable cascade release management.
tagemittedstring-callback.output reference for an external release tool.
workflowemittedstring-Release workflow dispatched against a release tag to build and attach binaries.
version_overridesreservedobject-Reserved pointer (dir:) to maintainer-committed version-intent override files. See Versioning.

When workflow is set, cascade dispatches it (via gh workflow run --ref <tag>) rather than relying only on the tag-push trigger, which GitHub does not reliably start when the tagged commit carries a CI-skip marker. Omit the section to use cascade defaults.

FieldStatusTypeDefaultDescription
disabledemittedboolfalseDisable changelog generation entirely.
workflowemittedstring-Path to a custom changelog workflow.
contributorsemittedboolfalseInclude contributor attribution via the GitHub API.

Omit the section to use the built-in conventional commit parser.

FieldStatusTypeDefaultDescription
allow_breaking_changesemittedboolfalseDisable the breaking-change gate for this repository.

By default the gate is enabled: a feat!: or BREAKING CHANGE: commit blocks the pre-release to release boundary (and release to prod) during a promote or release, so the major bump is a deliberate act. Set allow_breaking_changes: true at the config level to turn the gate off once for the whole repository. Those crossings then proceed without the per-run allow_breaking_changes workflow input, which stays available for repositories that leave the gate on.

ci:
config:
allow_breaking_changes: true

Leave it unset (the default) to keep the gate on. A manifest that omits the field generates byte-identical workflows to before.

Per-environment settings keyed by environment name. Consumed for native GitHub Environment support and deployment URLs.

ci:
config:
environments: [production]
environment_config:
production:
gha_environment: production
environment_url: "https://app.example.com"
required_reviewers: [octocat]
wait_timer: 10
branch_policy: protected
Sub-fieldStatusDescription
gha_environmentemittedMaps the cascade environment to a real GitHub Environment (native deployments, environment_url).
environment_urlemittedURL reported on the Deployment status for that environment.
required_reviewersemitted (via environments command)Reviewers the environments command applies to the GitHub Environment.
wait_timeremitted (via environments command)Wait timer the environments command applies.
branch_policyemitted (via environments command)Branch policy the environments command applies.

GitHub Environment support is shipped: gha_environment is consumed for native deployments, and the cascade environments command emits required_reviewers, wait_timer, and branch_policy for an operator to apply. See Add or change environments.

Fields that shape the cascade-owned workflows as a whole rather than a single callback.

Top-level concurrency block emitted onto the orchestrate, promote, hotfix, rollback, release, and external-update workflows.

ci:
config:
concurrency:
group: cascade-${{ github.ref }}
cancel_in_progress: false
Sub-fieldStatusTypeDescription
groupemittedstringThe concurrency group expression.
cancel_in_progressemittedboolWhether a new run cancels an in-progress run in the same group.
ci:
config:
job_timeout_minutes: 30
FieldStatusTypeDefaultDescription
job_timeout_minutesemittedint30Sets timeout-minutes on cascade-owned jobs.

Non-push trigger types wired onto the generated workflows.

ci:
config:
extra_triggers:
schedule:
- cron: "0 7 * * *"
repository_dispatch:
types: [deploy-request]
merge_group: {}
Sub-fieldStatusDescription
scheduleemittedList of cron schedule entries. Each entry has one required key, cron.
repository_dispatchemittedWires the repository_dispatch trigger; types lists the event types.
workflow_runemittedWires the workflow_run trigger.
merge_groupemittedPresent (even empty) wires the merge-queue trigger. The validation lane behavior lives in the separate merge_queue block.

Opts the rollback workflow into a repository_dispatch trigger, driving the rollback-dispatch path.

ci:
config:
rollback:
repository_dispatch:
types: [rollback-request]
Sub-fieldStatusDescription
repository_dispatchemittedReuses the shared RepositoryDispatchTrigger shape (types), configured the same way as extra_triggers.repository_dispatch.

See Roll back an environment for the operator recipe.

Each of these emits an additional workflow only when its block is present. Omit the block and output is unchanged.

ci:
config:
pr_preview:
enabled: true
comment: true
FieldStatusTypeDefaultDescription
enabledemittedboolfalseEmit the PR-preview companion.
commentemittedboolfalseAlso post or update a sticky preview comment.

Emits a pull-request workflow that runs cascade verify and fails the check when committed workflows fall out of sync with the manifest.

FieldStatusTypeDefaultDescription
enabledemittedboolfalseEmit .github/workflows/cascade-drift-check.yaml (read-only, contents: read).
commentemittedboolfalseAlso emit the fork-safe comment companion (cascade-drift-comment.yaml).

The check job triggers on pull_request and is read-only; the comment companion triggers on workflow_run in the base-repo context with a scoped pull-requests: write token and derives the target PR only from trusted run metadata. When you set comment: true, consider pin_mode: sha to remove the floating-tag exposure on the one write-scoped job.

Emits the fork-safe cascade reconcile lane that adopts an external governed-pin change into action_pins and regenerates.

FieldStatusTypeDefaultDescription
enabledemittedboolfalseEmit the reconcile detector and companion workflows.
sourceemittedstringdependabotThe change-source adapter the companion recognizes.
commitemittedstringappendAdoption commit routing. append pushes onto the triggering PR branch; followup opens a separate PR (prefer this if you automerge on green).

Reports deployment status through the GitHub Deployments API from the finalize job.

FieldStatusTypeDefaultDescription
enabledemittedboolfalseCreate a Deployment and report status. Adds deployments: write to top-level permissions only when enabled.
keep_prior_activeemittedboolfalseSet auto_inactive: false so GitHub leaves prior deployments Active.

Every Deployments API step carries an if: ${{ github.server_url == 'https://github.com' }} guard, so on act or gitea the steps are skipped. Pair with environment_config.<env>.environment_url so the status links to the running environment.

FieldStatusTypeDefaultDescription
enabledemittedboolfalseEmit .github/workflows/cascade-validate.yaml, a pull_request check that runs cascade parse-config and fails on an invalid manifest.

The check validates cascade’s own configuration only, requests contents: read alone, and has no dry-run or comment side effects.

FieldStatusTypeDefaultDescription
enabledemittedboolfalseEmit .github/workflows/cascade-merge-queue.yaml, a merge_group-triggered lane that runs cascade parse-config and a dry-run cascade orchestrate setup against the merge-group candidate.

The lane is read-only. This block owns the lane behavior; the raw merge_group trigger is expressible separately under extra_triggers.merge_group, and the two are intentionally distinct.

A components: block declares several independently versioned components in one repository. When it is present, the top-level config becomes the shared default set every component inherits, and each entry overrides those defaults where it sets a value. A manifest with no components: block is one implicit component spanning the whole repository and generates byte-identical output, so components are strictly additive. See Split a repo into components for the operator walkthrough.

ci:
config:
environments: [dev, staging, prod]
components:
api:
path: api/
tag_prefix: api-
builds:
- name: api
workflow: .github/workflows/build-api.yaml
triggers: ["api/**"]
web:
path: web/
tag_prefix: web-
environments: [dev, prod]

components is a map keyed by component name. Each name must be identifier-safe (letters, digits, and underscores). Each entry accepts two required fields plus any inheritable field it overrides.

FieldStatusTypeRequiredDescription
pathemitted (behavior)stringYesThe subtree this component owns. Relative, with no .. segments. Scopes the component’s version commit walk and its default push-paths trigger.
tag_prefixemitted (behavior)stringYesThe component’s version-tag prefix. Must be distinct from every other component’s prefix so their tag namespaces never collide.

A component inherits every shared top-level field and may override the ones below where an override is meaningful. An unset field takes the shared top-level value.

tag_grammar, environments, release_trigger, allow_breaking_changes, validate, builds, deploys, publish, external, notify, release, changelog, runs_on, job_timeout_minutes, dispatch_inputs, extra_triggers, pr_preview, validate_check, rollback, deployments, environment_config, triggers, release_token, and release_token_app.

concurrency.cancel_in_progress is inheritable, but concurrency.group is not: the orchestrate, promote, and rollback groups are derived per component so runs never serialize across components. Setting a component concurrency.group is a parse error.

These fields are set once at the top level and cannot be overridden per component, because they describe the repository or the single writer of shared state rather than one component: schema_version, trunk_branch, cli_version, cli_version_sha, state_token, state_token_app, manifest_file, manifest_key, action_folder, git, drift_check, reconcile, pin_mode, action_pins, telemetry, and merge_queue. Setting any of them under a component is a parse error, as is any unknown field.

cascade parse-config rejects a components: block that breaks isolation:

  • Each component must set path (relative, no ..) and tag_prefix.
  • Component names must be identifier-safe.
  • Two components must not share a tag_prefix; a collision is a parse error, not a silently shared namespace.
  • A top-level concurrency.group must not be set when components are declared, and a component may not set its own concurrency.group.
  • A repository-wide field or an unknown field set under a component is rejected.

Per-component versioning, state (state.components.<name>.<env>), and tag namespaces are covered in Per-component versioning.

run_policy, on_failure, and retries apply to validate, each builds entry, and each deploys entry.

run_policyBehavior
defaultSkip if any dependency was skipped.
alwaysRun if triggered, even if dependencies skipped.
forceAlways run, ignore triggers and dependencies.
on_failureBehavior
abortFail the entire workflow.
continueLet other callbacks proceed.

retries is the number of retry attempts on failure (0-3).

Triggers use glob patterns:

PatternMatches
src/**All files under src/ recursively.
*.goGo files in the root directory.
**/*.yamlYAML files anywhere in the repo.
DockerfileExact file match.
cdk/*.tsTypeScript files directly in cdk/ (not recursive).

* matches any characters except /, ** matches any path segments, and ? matches a single character.

Inputs flow from static inputs to per-environment env_inputs, with the environment-specific value winning:

deploys:
- name: services
inputs:
cluster: default-cluster
region: us-east-1
env_inputs:
dev:
cluster: dev-cluster
prod:
region: us-west-2

For dev: { cluster: "dev-cluster", region: "us-east-1" }. For prod: { cluster: "default-cluster", region: "us-west-2" }.

These fields parse and pass schema validation but never appear in generated YAML. They are kept in the schema so a manifest that sets them stays valid and forward-compatible, but they change nothing today.

FieldWhereWhy it is not emitted
runs_ontop-level and per-callbackGitHub Actions forbids runs-on on a reusable-workflow uses: caller job, so cascade-owned jobs are hardcoded ubuntu-latest. Per-environment runner overrides are blocked by the same structural limit.
concurrency (per-callback)builds[], deploys[], publishGitHub Actions forbids a concurrency: block on a reusable-workflow caller job. Top-level concurrency is emitted; the per-callback form is not.
timeout_minutes (per-callback)builds[], deploys[], publishThe timeout belongs inside the called workflow, not the caller job. Use top-level job_timeout_minutes to bound the cascade-owned jobs.

Reserved fields parse but have zero generator consumption today. They reserve a stable shape so a future capability can land without a schema break.

FieldWhereNote
telemetrytop-levelenabled, adapter, plus reserved webhook and job_summary. No generator consumption.
rollout.type / rollout.canary / rollout.blue_greendeploys[]Reserved rollout sub-blocks (rollout.fail_fast and rollout.max_parallel are emitted; the rest are inert).
release.version_overridesreleaseReserved pointer to version-intent override files.
deploy_targetdeploys[]Reserved shape for the GitOps mirror pattern.

The full reserved-shapes catalog, including the canary sub-fields (steps, analysis, percent, bake_time, promote_callback, rollback_callback), lives in Versioning and schema.

The ci.state block tracks deployment state per environment plus a synthetic release slot. cascade manages it automatically; do not hand-edit.

ci:
state:
dev:
sha: "abc123def456"
version: "v1.2.0-rc.3"
committed_at: "2026-01-15T10:30:00Z"
committed_by: "github-actions[bot]"
builds:
app:
sha: "abc123def456"
artifact_id: "sha256:def456..."
tags:
image_tag: "abc123-1736923500"
deploys:
infra:
sha: "abc123def456"
deployed_at: "2026-01-15T10:30:00Z"
release:
sha: "def789abc012"
version: "v1.1.0"
latest_release:
version: "v1.1.0"
sha: "def789abc012"
Environment-level fieldDescription
shaCommit SHA promoted into this environment.
versionSemantic version tag (for example v1.2.3-rc.0).
committed_at / committed_byISO 8601 timestamp and actor for the promotion.
builds / deploys / externalPer-callback tracking (auto-populated).

The implicit release slot tracks the most recently published (non-draft) GitHub release. Promotions to the last environment cross the release boundary first, where the breaking-change gate runs and the publish callback fires. Per-build state carries artifact_id (the canonical identifier passed to publish) and tags (the build’s other declared outputs). Per-deploy state enables diff-based change detection, so only deployables with actual file changes are redeployed.

cascade parse-config enforces the semantic rules the schema alone cannot:

  • schema_version should be 1. Omitting it emits a warning.
  • Environment, build, and deploy names must be identifier-safe (letters, digits, underscores). The generator-owned names environment and dry_run are reserved and cannot be used as dispatch_inputs.
  • pin_mode must be tag or sha; run_policy must be default, always, or force; on_failure must be abort or continue; retries must be 0-3.
  • A repository cannot set both external (primary) and notify (satellite).
  • A per-callback permissions block is the complete permission set for that caller job and replaces the workflow default rather than merging.
  • cli_version_sha takes effect only under pin_mode: sha.
  • tag_grammar.prerelease_token must not be empty. tag_grammar.prefix, prerelease_token, prerelease_separator, and dryrun_token must not contain whitespace, control characters, or a git-ref-unsafe character (any of /, ~, ^, :, ?, *, [, or a backslash). The resolved dryrun_token must differ from the resolved prerelease_token.
  • Prerequisite: Getting started walks you from install to a first pipeline.
  • Next: Callback contract documents the inputs and outputs the workflows referenced by validate, builds, deploys, and publish must honor.