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:
| Status | Meaning |
|---|---|
| emitted | Changes generated workflow output. |
| validated-only | Parsed and schema-checked, but never appears in generated YAML. |
| reserved | Parses, but has no generator consumption today, so cascade lint rejects any manifest that sets it. |
File shape and schema support
Section titled “File shape and schema support”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.
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
schema_version |
emitted | int | 1 when omitted |
Manifest schema generation. Omitting it emits a CLI warning; set schema_version: 1 in every manifest. |
manifest_file |
emitted | string | .github/manifest.yaml |
Path to the manifest file. |
manifest_key |
emitted | string | ci |
Top-level wrapper key inside the file. |
action_folder |
emitted | string | manage-release |
Folder 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.
Value validation
Section titled “Value validation”Every manifest value that cascade splices into generated output (a YAML key or scalar, a shell string, a github-script literal, a cron entry, a git invocation, or a ${{ secrets.* }} expression) is shape-validated by cascade lint and at generation time, so a value the emitted context cannot carry safely is rejected up front instead of producing a broken or silently altered workflow. The notable shapes:
- Names that key job IDs (environments, builds, deploys, components, dispatch inputs) allow letters, digits, hyphens, and underscores.
- Cron entries must be five-field expressions in the GitHub Actions cron grammar (digits, names like
MONorJAN,*,,,-,/). repository_dispatchandworkflow_runevent types allow letters, digits, dots, hyphens, and underscores.- Workflow display names, trigger globs, and operator input values are single-quote-escaped at emit, so apostrophes are fine; only line breaks are rejected.
- Values spliced into double-quoted shell strings (
git.user_name,git.user_email,manifest_file) reject",$, backticks, and backslashes; apostrophes stay allowed. - Secret references (
gpg_key_id,gpg_key_secret, per-callbacksecrets:map entries, environmentsecrets/variables) must be valid GitHub Actions secret names. tag_grammarcomponents are restricted to letters, digits,.,_, and-, and a prefix may not begin with a hyphen (it reachesgit taglookups where a leading hyphen parses as a flag). This applies to component prefixes too.environment_urlmust be an http(s) URL without quotes or whitespace;$in a query string is fine (the emitted shell single-quotes it).- Token fields (
release_token,state_token,notify.token) andconcurrency.groupare emitted as unquoted YAML scalars and reject line breaks,:, and#. trunk_branchandexternal[].refare restricted to git-ref-safe characters (letters, digits,.,/,_,-, no leading hyphen).
The same rules apply to every component’s resolved configuration, so a per-component override cannot bypass them.
Editor support
Section titled “Editor support”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 lint remains the authority for semantic and cross-field rules.
The schema is published at:
https://stablekernel.github.io/cascade/manifest.schema.jsonPrint 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.jsonci: config: schema_version: 1 trunk_branch: mainOr 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" }}Top-level identity
Section titled “Top-level identity”The two fields that define the pipeline shape.
| Field | Status | Type | Required | Default | Description |
|---|---|---|---|---|---|
trunk_branch |
emitted | string | Yes | - | The trunk branch the orchestrate workflow runs on. |
environments |
emitted | list of strings or objects | No | - | The promotion ladder. Each entry is a bare name or an object carrying that environment’s name, optional role, and inline settings. Omit for a no-environment library or CLI project. |
ci: config: schema_version: 1 trunk_branch: main environments: - dev # bare string: an environment with no extra settings - name: staging wait_timer: 5 - name: prod gha_environment: production environment_url: "https://app.example.com" cli_version: v0.16.2Each entry is one of two forms:
- a bare string (
- dev), sugar for an environment with no inline settings; - an object (
- {name: dev, ...}) carryingname, an optionalrole, and the inline per-environment settings (below).
The list order defines the promotion ladder, so a plain string list behaves exactly as before.
Trigger configuration
Section titled “Trigger configuration”| Field | Status | Type | Default | Description |
|---|---|---|---|---|
triggers |
emitted | list | - | Global path patterns that activate orchestration. See Trigger patterns. |
release_trigger |
emitted | string | push |
How orchestrate fires. push keeps push-on-trunk plus workflow_dispatch; dispatch drops push: so releases run only on manual workflow_dispatch. |
The version tag prefix lives on tag_grammar.prefix and defaults to v.
Workflow-level trigger types beyond push are set under extra_triggers.
tag_grammar
Section titled “tag_grammar”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: trunk_branch: main tag_grammar: prefix: v prerelease_token: rc prerelease_separator: "." dryrun_token: dryrun strict_prefix: false| Field | Status | Type | Default | Description |
|---|---|---|---|---|
prefix |
emitted | string | v |
Literal prefix cascade puts on every new tag. |
prerelease_token |
emitted | string | rc |
Token that marks a release-candidate tag. |
prerelease_separator |
emitted | string | . |
Separator between the token and its number. . yields rc.4; an empty string yields rc4. |
dryrun_token |
emitted | string | dryrun |
Token that marks a rehearsal tag. |
strict_prefix |
emitted | bool | false | When false, reads accept any alphabetic prefix so historical and foreign-cased tags still parse. When true, reads require the exact configured prefix. |
The prefix. tag_grammar.prefix is the one place the version tag prefix is set. When
tag_grammar is absent, or the block omits prefix, the prefix defaults to v, so an
existing repository that never configured a grammar keeps its vX.Y.Z tags unchanged.
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 Tag grammar for the concept in one place,
including the default -rc.N behavior and how this block fits an existing tagging convention,
and Versioning and schema for the hotfix version grammar and
the full reserved-shapes catalog.
CLI pinning
Section titled “CLI pinning”These fields pin the cascade CLI and third-party actions the generated workflows install.
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
cli_version |
emitted | string | latest | cascade CLI version the generated workflows install via setup-cli. Use latest, a prerelease channel, or a specific vX.Y.Z. |
cli_version_sha |
emitted | string | - | 40-hex commit SHA that cli_version resolves to. Under pin_mode: sha, the generated setup-cli ref pins to this commit. |
pin_mode |
emitted | string | tag |
Third-party action pin policy: tag emits <action>@<major-tag>; sha emits <action>@<commit-sha> with the version as a trailing comment. |
action_pins |
emitted | map | - | Per-action ref overrides keyed by action path (for example actions/checkout), applied regardless of pin_mode. |
cli_version values
Section titled “cli_version values”| Value | Behavior |
|---|---|
latest |
Most recent stable release (default). |
beta |
Newest prerelease build. |
vX.Y.Z |
A specific version (for example v0.16.1). Pin for reproducibility. |
cli_version_sha
Section titled “cli_version_sha”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@17599d2f891ac41996936302b4f7b6d7e1359844 # v0.16.0The 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>^{}'.
Action pinning
Section titled “Action pinning”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:
| Value | Behavior |
|---|---|
tag |
Default. Emits <action>@<major-tag> (for example actions/checkout@v4). Never @latest. |
sha |
Emits <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: trunk_branch: main pin_mode: sha action_pins: actions/checkout: 0123456789abcdef0123456789abcdef01234567That 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.
Tokens and authentication
Section titled “Tokens and authentication”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.
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
release_token |
emitted | string | state_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_token |
emitted | string | ${{ secrets.GITHUB_TOKEN }} |
Token expression for writing manifest state to the trunk branch. |
release_token_app |
emitted | object | - | GitHub App identity that mints a release token at run time (app_id, private_key). |
state_token_app |
emitted | object | - | 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: trunk_branch: main release_token: RELEASE_PAT state_token: STATE_PATA 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: trunk_branch: main 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_KEYAdd 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: trunk_branch: main git: mode: custom user_name: deploy-bot user_email: bot@example.com gpg_key_id: GPG_KEY_ID gpg_key_secret: GPG_PRIVATE_KEY| Field | Status | Type | Default | Description |
|---|---|---|---|---|
mode |
emitted | string | default |
default, custom, or external. |
user_name |
emitted | string | github-actions[bot] | Git user.name (when mode: custom). |
user_email |
emitted | string | github-actions[bot]@users.noreply.github.com | Git user.email. |
gpg_key_id |
emitted | string | - | Secret name holding the GPG key ID. |
gpg_key_secret |
emitted | string | - | 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.
user_name and user_email are spliced into double-quoted shell strings in the emitted workflows, so they reject ", $, backticks, and backslashes (apostrophes are fine). gpg_key_id and gpg_key_secret are secret NAMES spliced into ${{ secrets.<name> }} expressions and must match the GitHub secret-name grammar.
validate
Section titled “validate”Optional pre-build validation callback.
ci: config: trunk_branch: main 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| Field | Status | Type | Default | Description |
|---|---|---|---|---|
workflow |
emitted | string | - | Path to the validation workflow. |
supports_dry_run |
emitted | bool | false | Whether the callback handles the dry_run input. |
triggers |
emitted | list | - | File patterns that trigger validation. |
inputs |
emitted | map | {} | Static inputs passed to the workflow. |
env_inputs |
emitted | map | {} | Per-environment input overrides. |
run_policy |
emitted | string | default |
Execution policy. See Policy fields. |
on_failure |
emitted | string | abort |
Failure handling. See Policy fields. |
retries |
emitted | int | 0 | Retry attempts (0-3). Trunk runs only. See Policy fields. |
builds
Section titled “builds”Builds produce artifacts (container images, binaries, and the like). builds is a list.
ci: config: trunk_branch: main environments: [prod] 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| Field | Status | Type | Required | Description |
|---|---|---|---|---|
name |
emitted | string | Yes | Unique build identifier. |
workflow |
emitted | string | Yes | Path to the build workflow. |
triggers |
emitted | list | No | Glob patterns that trigger this build. |
depends_on |
emitted | list | No | Other callbacks to wait for (hard dependency). |
optional_depends_on |
emitted (ordering) | list | No | Soft dependency: orders this job after the named jobs when they exist, without failing generation if a name is absent. The counterpart to depends_on. |
inputs |
emitted | map | No | Static inputs to the workflow. |
env_inputs |
emitted | map | No | Per-environment input overrides. |
permissions |
emitted | map | No | Job-level permissions: for this callback’s caller job. See Permissions. |
matrix |
emitted | object | No | Build fan-out. See matrix. |
state_tags |
emitted (behavior) | list | No | State-capture tags recorded for this build (the field behind the callback contract’s State Capture). |
auto_commits |
emitted (behavior) | bool | No | When true, cascade captures the HEAD sha the callback advanced to (runtime env AUTO_COMMITS_HEAD_SHA), for callbacks that commit during their run. |
run_policy |
emitted | string | No | Execution policy. See Policy fields. |
on_failure |
emitted | string | No | Failure handling. See Policy fields. |
retries |
emitted | int | No | Retry attempts (0-3). |
artifact_upload |
emitted | object | No | GitHub Actions artifact passthrough between jobs in one orchestrate run: upload is a path glob uploaded via actions/upload-artifact (named build-{name}), and downloads names upstream builds whose artifacts to fetch first. Distinct from the release artifacts list, which attaches assets to a published release. |
artifacts |
emitted | list | No | Release assets attached to the GitHub release the orchestrate run manages. See artifacts. |
runs_on |
validated-only | object | No | Parsed and validated, never emitted. See Validated-only fields. |
concurrency |
validated-only | object | No | Parsed and validated, never emitted. See Validated-only fields. |
timeout_minutes |
validated-only | int | No | Parsed 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.
artifact_upload (the singular GitHub Actions passthrough) and artifacts (the plural list of release assets) are two different fields with two different meanings; set the one you need and do not confuse them.
matrix
Section titled “matrix”matrix (builds only) fans a build across a cross-product of dimensions.
| Sub-field | Status | Type | Description |
|---|---|---|---|
dimensions |
emitted | map | The cross-product axes (for example os: [linux, darwin], arch: [amd64, arm64]). Each key must start with a letter or underscore and contain only letters, digits, hyphens, and underscores (the identifier set GitHub Actions accepts for matrix keys and matrix.<key> references), and each axis must list at least one value; validation rejects anything else. |
max_parallel |
emitted | int | Caps concurrent matrix legs (0 uses the GitHub Actions default). |
fail_fast |
emitted | bool | Whether a failing leg cancels the rest. Unset applies the GitHub Actions default (true for matrix builds). |
artifacts
Section titled “artifacts”artifacts (builds only) lists release assets to attach to the GitHub release the orchestrate run manages. The build workflow uploads each asset as a GitHub Actions artifact named release-{build-name}-{artifact-name}; the finalize job downloads every release-* artifact into release-artifacts/ and uploads each configured path to the release with gh release upload.
builds: - name: app workflow: .github/workflows/build.yaml artifacts: - name: binaries path: dist/*.tar.gz - name: checksums path: dist/checksums.txt required: false| Sub-field | Status | Type | Description |
|---|---|---|---|
name |
emitted | string | Artifact identifier, combined with the build name into the release-{build-name}-{artifact-name} upload name. Letters, digits, dots, hyphens, and underscores only. |
path |
emitted | string | Glob pattern for the files to upload (for example dist/*.tar.gz), resolved under release-artifacts/. Letters, digits, dots, slashes, hyphens, underscores, and the glob characters *, ?, [, ]; a leading hyphen is rejected. |
required |
emitted | bool | Whether a missing artifact fails the release. Defaults to true; set required: false to downgrade a missing asset to a warning. |
Permissions
Section titled “Permissions”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: writeThis 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
Section titled “deploys”Deploys target environments. deploys is a list and shares most fields with builds.
ci: config: trunk_branch: main 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| Field | Status | Type | Required | Description |
|---|---|---|---|---|
name |
emitted | string | Yes | Unique deploy identifier. |
workflow |
emitted | string | Yes | Path to the deploy workflow. |
triggers |
emitted | list | No | Glob patterns that trigger this deploy. |
depends_on |
emitted | list | No | Other callbacks to wait for. |
optional_depends_on |
emitted (ordering) | list | No | Soft dependency, as for builds. |
supports_dry_run |
emitted | bool | No | Whether the callback handles dry_run. |
inputs |
emitted | map | No | Static inputs. |
env_inputs |
emitted | map | No | Per-environment overrides. |
permissions |
emitted | map | No | Job-level permissions: for the caller job. See Permissions. |
rollout |
partial | object | No | Deploy rollout strategy. See rollout. |
state_tags |
emitted (behavior) | list | No | State-capture tags recorded for this deploy. |
auto_commits |
emitted (behavior) | bool | No | Captures the advanced HEAD sha, as for builds. |
run_policy |
emitted | string | No | Execution policy. |
on_failure |
emitted | string | No | Failure handling. |
retries |
emitted | int | No | Retry attempts (0-3). |
artifact_upload |
emitted | object | No | GitHub Actions artifact passthrough between jobs in one orchestrate run, as for builds (upload/downloads). |
runs_on |
validated-only | object | No | Parsed and validated, never emitted. |
concurrency |
validated-only | object | No | Parsed and validated, never emitted. |
timeout_minutes |
validated-only | int | No | Parsed and validated, never emitted. |
Deploy types
Section titled “Deploy types”Deploys are classified by their configuration:
| Type | Configuration | When it runs |
|---|---|---|
| Trigger-based | Has triggers |
When matching files change. |
| Build-linked | Has depends_on referencing one or more builds |
When any referenced build runs. |
| Unconstrained | No triggers or depends_on |
Always runs. |
Build-linked deploys inherit the referenced builds’ triggers for change detection during
promotions: the deploy is promoted when any referenced build’s triggers match the changes.
Each build’s trigger list is evaluated on its own, so a ! exclusion in one build’s
triggers never suppresses a match from another. A referenced build with no triggers
always runs, and so does any deploy linked to it.
rollout
Section titled “rollout”rollout (deploys only) tunes the deploy job’s strategy: block. Its status is partial: two sub-fields are emitted, the rest are reserved.
| Sub-field | Status | Type | Description |
|---|---|---|---|
max_parallel |
emitted | int | Caps concurrent rollout waves. Emitted into the deploy job’s strategy: block. |
fail_fast |
emitted | bool | Whether a failing wave cancels the rest. Emitted into the strategy: block. Unset differs from an explicit false. |
type |
reserved | string | default, rolling, canary, or blue_green. Parsed, not yet wired to generation. |
canary |
reserved | object | Canary sub-block (steps, analysis, percent, bake_time, promote_callback, rollback_callback). Inert today. |
blue_green |
reserved | object | Blue/green sub-block. Inert today. |
See Versioning and schema for the full reserved rollout shape.
publish
Section titled “publish”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: trunk_branch: main publish: workflow: .github/workflows/publish.yaml| Field | Status | Type | Required | Description |
|---|---|---|---|---|
workflow |
emitted | string | Yes | Path 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 and notify
Section titled “external and notify”external (primary repos) and notify (satellite repos) coordinate deployments across repositories. A repository cannot set both.
external
Section titled “external”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: trunk_branch: main 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| Field | Status | Type | Required | Description |
|---|---|---|---|---|
repo |
emitted | string | Yes | External repository (for example org/cdk-infra). |
ref |
emitted | string | No | Branch or tag reference (default: trunk_branch). |
deploys |
emitted | list | Yes | Deployables from this repo. |
deploys[].name |
emitted | string | Yes | Unique deploy identifier. |
deploys[].workflow |
emitted | string | Yes | Workflow path (local, or org/repo/.github/workflows/x.yaml@ref for external). |
deploys[].triggers |
emitted | list | No | File patterns for change detection. |
deploys[].on_update.deploy.workflow |
emitted | string | No | Reusable 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.
notify
Section titled “notify”For satellite repos that report deployments back to a primary.
ci: config: trunk_branch: main notify: repo: org/my-backend workflow: external-update.yaml token: PRIMARY_REPO_TOKEN deploy_name: artifact-a environment: staging| Field | Status | Type | Required | Default | Description |
|---|---|---|---|---|---|
repo |
emitted | string | Yes | - | Primary repository to notify. |
workflow |
emitted | string | No | external-update.yaml |
Workflow name. |
token |
emitted | string | No | PRIMARY_REPO_TOKEN |
Secret name for cross-repo dispatch. |
deploy_name |
emitted | string | No | first local deploy, then first build | Deploy name to dispatch, when the primary recognizes this satellite under a different name. |
environment |
emitted | string | No | first local environment, then dev |
Environment 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.
release_build and changelog
Section titled “release_build and changelog”ci: config: trunk_branch: main release_build: disabled: false workflow: .github/workflows/release-assets.yaml changelog: disabled: false contributors: truerelease_build
Section titled “release_build”release_build configures the post-publish release-build dispatch: the workflow cascade runs against the release tag once a release is published, for example to build and attach binaries. It is distinct from the similarly-named release_token (the secret backing release operations), release_trigger (how the orchestrate workflow fires), and latest_release (the cascade-managed record of the most recent published release).
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
disabled |
emitted | bool | false | Disable cascade release management. |
tag |
emitted | string | - | callback.output reference for an external release tool. |
workflow |
emitted | string | - | Release workflow dispatched against a release tag to build and attach binaries. |
version_overrides |
reserved | object | - | 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.
changelog
Section titled “changelog”| Field | Status | Type | Default | Description |
|---|---|---|---|---|
disabled |
emitted | bool | false | Disable changelog generation entirely. |
workflow |
emitted | string | - | Path to a custom changelog workflow. |
contributors |
emitted | bool | false | Include contributor attribution via the GitHub API. |
Omit the section to use the built-in conventional commit parser.
allow_breaking_changes
Section titled “allow_breaking_changes”| Field | Status | Type | Default | Description |
|---|---|---|---|---|
allow_breaking_changes |
emitted | bool | false | Disable 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: trunk_branch: main allow_breaking_changes: trueLeave it unset (the default) to keep the gate on. A manifest that omits the field generates byte-identical workflows to before.
Per-environment settings
Section titled “Per-environment settings”Per-environment settings live inline on each environments entry in its
object form, keyed by that entry rather than in a separate map. They are consumed for
native GitHub Environment support and deployment URLs.
ci: config: trunk_branch: main environments: - name: production role: release gha_environment: production environment_url: "https://app.example.com" required_reviewers: [octocat] wait_timer: 10 branch_policy: protected| Sub-field | Status | Description |
|---|---|---|
name |
emitted | The environment name (required on an object entry). Defines this entry’s rung on the ladder. |
role |
emitted | Optional explicit promotion stage (prerelease or release), overriding the positional default. |
gha_environment |
emitted | Maps the cascade environment to a real GitHub Environment (native deployments, environment_url). |
environment_url |
emitted | URL reported on the Deployment status for that environment. Must be an http(s) URL without quotes or whitespace; the emitted shell single-quotes it, so a $ in a query string stays literal. |
required_reviewers |
emitted (via environments command) |
Reviewers the environments command applies to the GitHub Environment. |
wait_timer |
emitted (via environments command) |
Wait timer the environments command applies. |
branch_policy |
emitted (via environments command) |
Branch policy the environments command applies (with branch_patterns and tag_patterns when custom). |
secrets / variables |
emitted (via environments command) |
Expected env-scoped secret and variable NAMES (names only, never values). |
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.
Workflow-level fields
Section titled “Workflow-level fields”Fields that shape the cascade-owned workflows as a whole rather than a single callback.
concurrency
Section titled “concurrency”Top-level concurrency block emitted onto the orchestrate, promote, hotfix, rollback, release, and external-update workflows.
ci: config: trunk_branch: main concurrency: group: cascade-${{ github.ref }} cancel_in_progress: false| Sub-field | Status | Type | Description |
|---|---|---|---|
group |
emitted | string | The concurrency group expression. Composed with a per-workflow namespace at emission, never shared across workflows. |
cancel_in_progress |
emitted | bool | Whether a new run cancels an in-progress run in the same group. Applies to orchestrate; the state-mutating workflows always queue. |
The group is namespaced per workflow
Section titled “The group is namespaced per workflow”A GitHub concurrency group is repo-global: it is matched across every workflow in
the repository, not scoped to the workflow that declares it. Two workflows sharing
a group interfere even with cancel_in_progress: false, because GitHub keeps only
the latest queued run in a group and cancels the rest. A single group emitted onto
every cascade workflow would therefore let a queued promote and a queued release
silently cancel each other.
So group is a base expression, not the literal emitted key. Each workflow
composes it with its own namespace:
| Workflow | Emitted group for the example above |
|---|---|
| orchestrate | cascade-${{ github.ref }}-orchestrate |
| promote | cascade-${{ github.ref }}-promote |
| rollback | cascade-${{ github.ref }}-rollback |
| release | cascade-${{ github.ref }}-release |
| external-update | cascade-${{ github.ref }}-external-${{ inputs.deploy_name }} |
The external-update key keeps its deploy_name axis so two upstream components
updating the same downstream repo do not collapse onto one lane.
When group is omitted, each workflow keeps its own default: orchestrate-${{ github.ref }}
on orchestrate, the bare workflow name on promote, rollback, and release, and
cascade-external-${{ inputs.deploy_name }}-${{ github.ref }} on external-update.
When components are declared, the group is derived per component and cannot be
set at all.
cancel_in_progress applies to orchestrate
Section titled “cancel_in_progress applies to orchestrate”cancel_in_progress defaults to true on orchestrate. Set it to false to queue
those runs instead.
Orchestrate is the one cascade workflow that may safely cancel mid-write, because its lane is keyed per ref: the run that supersedes a cancelled one recomputes and rewrites the same per-ref state from the newer commit, so the write is redone rather than lost. A cancelled run never resumes, so this is a property of the work being idempotent per ref, not of anything recovering the interrupted write.
That reasoning does not extend to the workflows that mutate durable state, so
cancel_in_progress is not honored on them: promote, rollback, release,
external-update, and hotfix always emit cancel-in-progress: false. Each carries a
distinct payload (a specific environment, version, tag, or downstream manifest)
that no superseding run redoes, so cancelling one mid-write loses that work
permanently and leaves the state half-applied. Requesting cancel_in_progress: true
does not override this.
Omitting cancel_in_progress is distinct from setting it to false: an omitted
value leaves each workflow on its own default, so setting only group does not
change orchestrate’s cancellation behavior.
job_timeout_minutes
Section titled “job_timeout_minutes”ci: config: trunk_branch: main job_timeout_minutes: 30| Field | Status | Type | Default | Description |
|---|---|---|---|---|
job_timeout_minutes |
emitted | int | 30 | Sets timeout-minutes on cascade-owned jobs. |
extra_triggers
Section titled “extra_triggers”Non-push trigger types wired onto the generated workflows.
ci: config: trunk_branch: main extra_triggers: schedule: - cron: "0 7 * * *" repository_dispatch: types: [deploy-request]| Sub-field | Status | Description |
|---|---|---|
schedule |
emitted | List of cron schedule entries. Each entry has one required key, cron, validated as a five-field GitHub Actions cron expression. |
repository_dispatch |
emitted | Wires the repository_dispatch trigger; types lists the event types (letters, digits, dots, hyphens, underscores). |
workflow_run |
emitted | Wires the workflow_run trigger. types obey the event-type grammar; workflows entries are single-quote-escaped at emit (apostrophes are fine, line breaks are rejected). |
merge_group |
rejected | Not allowed. extra_triggers attaches to the side-effecting orchestrate workflow, which cuts release tags, publishes releases, and runs deploys while writing state, so a speculative merge-queue build could publish a real release from a candidate commit. cascade rejects extra_triggers.merge_group at validate. To gate pull requests inside a merge queue, set merge_queue.enabled, which emits a read-only validation lane. |
Migrating from a manifest that set extra_triggers.merge_group: remove that entry and set merge_queue.enabled: true instead. The read-only merge-queue lane runs cascade lint and a dry-run cascade orchestrate setup against the queued candidate without cutting tags, publishing releases, or writing state.
rollback
Section titled “rollback”Opts the rollback workflow into a repository_dispatch trigger, driving the rollback-dispatch path.
ci: config: trunk_branch: main rollback: repository_dispatch: types: [rollback-request]| Sub-field | Status | Description |
|---|---|---|
repository_dispatch |
emitted | Reuses the shared RepositoryDispatchTrigger shape (types), configured the same way as extra_triggers.repository_dispatch. |
See Roll back an environment for the operator recipe.
Companion workflows (opt-in)
Section titled “Companion workflows (opt-in)”Each of these emits an additional workflow only when its block is present. Omit the block and output is unchanged.
pr_preview
Section titled “pr_preview”ci: config: trunk_branch: main pr_preview: enabled: true comment: true| Field | Status | Type | Default | Description |
|---|---|---|---|---|
enabled |
emitted | bool | false | Emit the PR-preview companion. |
comment |
emitted | bool | false | Also post or update a sticky preview comment. |
drift_check
Section titled “drift_check”Emits a pull-request workflow that runs cascade verify and fails the check when committed workflows fall out of sync with the manifest.
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
enabled |
emitted | bool | false | Emit .github/workflows/cascade-drift-check.yaml (read-only, contents: read). |
comment |
emitted | bool | false | Also 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.
reconcile
Section titled “reconcile”Emits the fork-safe cascade reconcile lane that adopts an external governed-pin change into action_pins and regenerates.
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
enabled |
emitted | bool | false | Emit the reconcile detector and companion workflows. |
source |
emitted | string | dependabot |
The change-source adapter the companion recognizes. |
commit |
emitted | string | append |
Adoption commit routing. append pushes onto the triggering PR branch; followup opens a separate PR (prefer this if you automerge on green). |
deployments
Section titled “deployments”Reports deployment status through the GitHub Deployments API from the finalize job.
| Field | Status | Type | Default | Description |
|---|---|---|---|---|
enabled |
emitted | bool | false | Create a Deployment and report status. Adds deployments: write to top-level permissions only when enabled. |
keep_prior_active |
emitted | bool | false | Set 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 the environment_url field on that environment’s environments entry so the status links to the running environment.
validate_check
Section titled “validate_check”| Field | Status | Type | Default | Description |
|---|---|---|---|---|
enabled |
emitted | bool | false | Emit .github/workflows/cascade-validate.yaml, a pull_request check that runs cascade lint 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.
merge_queue
Section titled “merge_queue”| Field | Status | Type | Default | Description |
|---|---|---|---|---|
enabled |
emitted | bool | false | Emit .github/workflows/cascade-merge-queue.yaml, a merge_group-triggered lane that runs cascade lint and a dry-run cascade orchestrate setup against the merge-group candidate. |
The lane is read-only, which is exactly what a merge queue needs: it validates the queued candidate without cutting tags, publishing releases, or writing state. This is the supported way to participate in a merge queue. Attaching the raw merge_group event to the side-effecting orchestrate workflow through extra_triggers.merge_group is rejected at validate, because a speculative merge-queue build could otherwise publish a real release from a candidate commit.
components
Section titled “components”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: trunk_branch: main environments: [dev, staging, prod] components: api: path: api/ tag_grammar: prefix: api- builds: - name: api workflow: .github/workflows/build-api.yaml triggers: ["api/**"] web: path: web/ tag_grammar: 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.
| Field | Status | Type | Required | Description |
|---|---|---|---|---|
path |
emitted (behavior) | string | Yes | The subtree this component owns. Relative, with no .. segments. Scopes the component’s version commit walk and its default push-paths trigger. |
tag_grammar.prefix |
emitted (behavior) | string | Yes | The component’s version-tag prefix, set inside the component’s tag_grammar block. Must be distinct from every other component’s prefix so their tag namespaces never collide. |
extra_paths |
emitted (behavior) | list | No | Additional globs beyond path that both fire this component’s orchestrate workflow and count toward its version bump. Use it when a component depends on a shared library or a root build file outside its own subtree. See Shared paths. |
Inheritable overrides
Section titled “Inheritable overrides”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,
triggers, release_token, and release_token_app.
Inheritance is a deep merge. When a component overrides a block, it merges field-by-field into the inherited default rather than replacing it wholesale, so a partial override never drops the shared siblings it did not mention:
- A nested block merges recursively. A component that sets only
tag_grammar.prefixkeeps the inheritedprerelease_token,prerelease_separator, anddryrun_token. A component that sets onlydeployments.keep_prior_activekeeps the inheriteddeployments.enabled. - A keyed map such as
dispatch_inputsmerges by key, and the entry under each key merges field-by-field. A component that configures only one input key keeps the shared entries under the other keys. - A scalar or a list replaces. The
environmentslist whole-replaces the shared ladder outright, inline per-environment settings and all: a component that narrows toenvironments: [prod]declares its own settings on that entry rather than inheriting the sharedprodblock. A scalar such asrelease_triggeroverrides the shared value. An explicit opt-out is honored: a component that sets an inherited boolean back tofalse(for exampledeployments.enabled: falseunder a shareddeployments.enabled: true), or an inherited number to0, overrides the shared value rather than inheriting it. - A few exclusive blocks whole-replace instead of field-merging, because their
fields are not independently composable:
secrets(eitherinheritor an explicit allow-list, never both),runs_on(a label, a list, or a{group, labels}object, one form only), and therelease_token_app/state_token_appcredential blocks. A component that sets one of these replaces the inherited block outright. This keeps a component narrowingsecrets: inheritto an explicit allow-list from being silently broadened back to inherit-all.
The one exception to replace-a-list is the additive path set: a component’s
extra_paths unions with the top-level shared_paths rather than replacing it.
See Shared paths.
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.
Repository-wide fields
Section titled “Repository-wide fields”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, merge_queue, and shared_paths. Setting any of them
under a component is a parse error, as is any unknown field.
Shared paths
Section titled “Shared paths”A change to a shared dependency, a common library, a proto package, or a root
build file, lives outside every component’s own path. Without help it fires no
component and bumps no version, so a busy shared subtree ships nothing. Two fields
close that gap by widening a component’s effective path set beyond its path:
extra_pathson a component adds globs that only that component depends on.shared_pathsat the top level adds globs that every component depends on. It is sugar for repeating the same glob in every component’sextra_paths, and applies only when acomponents:block is present.
Each glob in a component’s effective set (its path, its extra_paths, and the
top-level shared_paths) reaches all three places a path matters: the emitted
push paths filter that fires the workflow, the per-callback change detection that
decides which builds and deploys run, and the commit range that computes the
version bump. A breaking (feat!: or fix!:) commit under a shared path bumps the
major of exactly the components that declare it, and leaves the rest untouched.
ci: config: trunk_branch: main environments: [dev, prod] shared_paths: - libs/common/** # every component depends on this components: api: path: services/api tag_grammar: prefix: api- extra_paths: - libs/proto/** # only api depends on the proto package web: path: services/web tag_grammar: prefix: web-Here a commit under libs/common/ bumps both api and web; a commit under
libs/proto/ bumps only api. When neither field is set the effective path set is
just each component’s path, byte-identical to before the fields existed.
Validation rules
Section titled “Validation rules”cascade lint rejects a components: block that breaks isolation:
- Each component must set
path(relative, no..) andtag_grammar.prefix. - Component names must be identifier-safe.
- Two components must not share a
tag_grammar.prefix; a collision is a parse error, not a silently shared namespace. - A top-level
concurrency.groupmust not be set when components are declared, and a component may not set its ownconcurrency.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.
Shared policy and pattern reference
Section titled “Shared policy and pattern reference”Policy fields
Section titled “Policy fields”run_policy, on_failure, and retries apply to validate, each builds entry, and each deploys entry.
run_policy |
Behavior |
|---|---|
default |
Skip if any dependency was skipped. |
always |
Run if triggered, even if dependencies skipped. |
force |
Always run, ignore triggers and dependencies. |
on_failure |
Behavior |
|---|---|
abort |
Fail the entire workflow. The default, and the only supported value. |
on_failure: continue is rejected at config validation. Every callback is emitted as a reusable-workflow call (jobs.<id>.uses), and GitHub Actions forbids continue-on-error on such a job, so a tolerated failure cannot be expressed without emitting a workflow GitHub rejects at parse. Tolerate a failure inside the reusable workflow itself instead, so the callback concludes successfully.
retries is the number of retry attempts on failure (0-3).
Trigger patterns
Section titled “Trigger patterns”Triggers use the glob grammar of the GitHub Actions paths filter:
| Pattern | Matches |
|---|---|
src/** |
All files under src/ recursively. |
*.go |
Go files in the root directory. |
**/*.yaml |
YAML files anywhere in the repo. |
Dockerfile |
Exact file match. |
cdk/*.ts |
TypeScript files directly in cdk/ (not recursive). |
!src/vendor/** |
Exclusion: excludes matching files included by an earlier pattern. |
* matches zero or more characters but never /; ** matches zero or more of
any character, including / (so **.js matches src/js/app.js, and a leading
**/ also matches files at the repository root); ? matches zero or one of
the preceding character (*.jsx? matches both page.js and page.jsx); +
matches one or more of the preceding character; and [] matches one character
from a bracketed set or range over a-z, A-Z, and 0-9.
A leading ! marks a pattern as an exclusion, and pattern order matters,
exactly as it does in the GitHub Actions paths: filter that cascade generates
from the same list. Patterns evaluate in order and the last match wins: a
matching exclusion after a positive match excludes the file, and a matching
positive pattern after an exclusion includes it again. So
["src/**", "!src/vendor/**", "src/vendor/keep/**"] triggers on any source
change except the vendored subtree, while still triggering for
src/vendor/keep/. An exclusion listed before every positive pattern excludes
nothing. CLI-side change detection (orchestrate setup and promotion preflight)
evaluates trigger lists with these same rules, so it predicts what the emitted
workflow filter will do.
A list containing only exclusions cannot be a paths: filter (GitHub requires
at least one non-! entry), so cascade emits it as paths-ignore, which means
the same thing: any changed file that is not excluded triggers.
When several callbacks declare different trigger lists, the orchestrate
workflow’s single push filter is their union, normalized so the workflow fires
whenever any callback’s own list would trigger: identical lists collapse to one
list emitted in manifest order, positive patterns union in declaration order,
and a ! exclusion stays in the emitted filter only when every
trigger-declaring callback shares the same list. A callback-scoped exclusion is
otherwise dropped from the workflow-level filter (it would suppress a sibling
callback’s build under last-match-wins); per-callback change detection still
applies it, so the affected callback is skipped inside the run instead.
Input inheritance
Section titled “Input inheritance”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-2For dev: { cluster: "dev-cluster", region: "us-east-1" }. For prod: { cluster: "default-cluster", region: "us-west-2" }.
Validated-only fields
Section titled “Validated-only fields”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.
| Field | Where | Why it is not emitted |
|---|---|---|
runs_on |
top-level and per-callback | GitHub 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[], publish |
GitHub 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[], publish |
The timeout belongs inside the called workflow, not the caller job; it is never emitted, so cascade lint rejects it. Use top-level job_timeout_minutes to bound the cascade-owned jobs. |
Reserved fields
Section titled “Reserved fields”Reserved fields parse but have zero generator consumption today. They reserve a stable shape so a future capability can land without a schema break. Because a manifest that sets one is silently inert, cascade lint treats reserved-field use as an error: remove the field (or, in the demo, keep it commented) until the capability is wired.
| Field | Where | Note |
|---|---|---|
telemetry |
top-level | enabled, adapter, plus reserved webhook and job_summary. No generator consumption. |
rollout.type / rollout.canary / rollout.blue_green |
deploys[] |
Reserved rollout sub-blocks (rollout.fail_fast and rollout.max_parallel are emitted and stay valid; the rest are inert and rejected). |
release_build.version_overrides |
release_build |
Reserved pointer to version-intent override files. |
deploy_target |
deploys[] |
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.
State section (managed)
Section titled “State section (managed)”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 field | Description |
|---|---|
sha |
Commit SHA promoted into this environment. |
version |
Semantic version tag (for example v1.2.3-rc.0). |
committed_at / committed_by |
ISO 8601 timestamp and actor for the promotion. |
builds / deploys / external |
Per-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.
Validation rules
Section titled “Validation rules”cascade lint enforces the semantic rules the schema alone cannot:
schema_versionshould be1. Omitting it emits a warning.- Environment, build, and deploy names must be identifier-safe (letters, digits, underscores). Within a section, two names that differ only by hyphen versus underscore are rejected: hyphens become underscores in job IDs and output keys, so those names would emit colliding outputs. The generator-owned names
environmentanddry_runare reserved and cannot be used asdispatch_inputs. pin_modemust betagorsha;run_policymust bedefault,always, orforce;on_failuremust beabort(the only supported value;continueis rejected);retriesmust be 0-3.- A repository cannot set both
external(primary) andnotify(satellite). - A per-callback
permissionsblock is the complete permission set for that caller job and replaces the workflow default rather than merging. cli_version_shatakes effect only underpin_mode: sha.tag_grammar.prerelease_tokenmust not be empty.tag_grammar.prefix,prerelease_token,prerelease_separator, anddryrun_tokenmust not contain whitespace, control characters, or a git-ref-unsafe character (any of/,~,^,:,?,*,[, or a backslash). The resolveddryrun_tokenmust differ from the resolvedprerelease_token.
What to read next
Section titled “What to read next”- 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, andpublishmust honor.
