Skip to content

Feature coverage matrix

The How cascade is tested page explains the two validation layers and the reconcile gate that makes the live layer trustworthy. This page is the detailed companion: a feature-by-feature table that traces each cascade capability to the exact place it is exercised.

For every feature the table records four things: the hermetic act plus gitea scenario that exercises it (under e2e/scenarios/** in this repository), the live-fleet probe that exercises it (the example repository and the probe in its scenario-suite.yaml), the unit coverage when that is the deliberate layer, and one line on what the chosen layer proves. A blank cell means the feature is not covered at that layer by design, not by omission. The “why both layers” section below explains those choices.

The two layers are not redundant. Each reaches conditions the other cannot, so a feature is placed in the layer where it can actually be exercised.

  • The act plus gitea harness is hermetic, fast, and deterministic. It owns the whole environment, so it can assert emitted structure byte for byte and can synthesize conditions on demand: a cherry-pick guaranteed to conflict, a state-write race, a merge-queue lane with no configured queue. It catches logic and generation regressions on every pull request without touching github.com.
  • The live fleet proves real GitHub behavior the harness cannot synthesize. A real release object moving from draft to prerelease to published, real release-candidate tag reaping, the Contents API state-write path, real cross-repo dispatch, real concurrency cancellation, and a hotfix whose cherry-pick base is read back from recorded manifest state on a real ref.

A concrete case where the live fleet caught what the harness masked

Section titled “A concrete case where the live fleet caught what the harness masked”

Two behaviors looked green in the hermetic harness yet were wrong on real GitHub:

  • Hotfix base read from recorded state. A hotfix to a pinned environment creates an env/<name> branch at the environment’s recorded base_sha and cherry-picks the fix onto it. The harness localizes and commits manifest state itself, so the base-from-recorded-state path was satisfied artificially and the divergence between the recorded base and the real ref never surfaced. It surfaced only when the multi-environment hotfix flow ran against a real repository, where the recorded base and the live branch tip are independent facts.
  • No-change skip. A re-dispatch with no watched-path change must take the no-change skip path. The harness could report success while the live skip behaved differently. The 4env probe_concurrency step exercises the real skip on real GitHub so the path is asserted where it actually runs.

A generated token-source regression in the setup step is a third example: it cleared only under real installation tokens on the fleet, never in the token-free harness.

Featureact plus gitea scenarioLive-fleet probe (repo)UnitWhat the layer proves
Orchestrate trunk build to release candidate01, 02, 03, 04, 34-extra-orchestrate-triggersevery repo, orchestrate-on-merge (all 11)internal/orchestrateA trunk merge mints an RC draft and writes state, across every topology, on real Actions
Default promotion (env to next env)04, promote/cascade-deploy-enabledpromote-staging (2env, 3env, primary)internal/promoteOne promotion step copies source state into the target on a real release object
Cascade-mode promotion (atomic multi-step)04-cascade-promotionlifecycle dev to prod (4env)internal/promoteThe full ladder advances through intermediates and publishes at the top
Standalone release lane (draft, prerelease, publish)05-publish-callback, 37, 38dispatch prerelease then release (single-env); release-onlyinternal/releaseA real release transitions draft to prerelease to published with RC reaping
Hotfix clean applyhotfix/hotfix-clean-apply, hotfix-multi-commit-clean, hotfix-multi-env-clean, hotfix-rejoinhotfix plan, apply, PR merge, finalize (3env)internal/hotfixA pinned-env fix lands, diverges state, and rejoins on real branches and PRs
Hotfix cherry-pick conflict and halthotfix/hotfix-conflict-resolution, hotfix-multi-env-conflict-haltprobe_hotfix_conflict (4env)internal/hotfixA guaranteed conflict raises the conflict label and halts the downstream lane
Rollback to prior version or SHArollback/* (8 scenarios)probe_rollback (4env), rollback-check (2env)internal/rollbackAn env rewinds, is marked diverged, and the ring snapshot advances
External rollback via repository_dispatchrepository_dispatch rollback, state revert asserted (rollback-dispatch)internal/rollbackA real repository_dispatch payload drives the automated rollback entry point and the target env’s state is read back reverted
Drift check and comment22-verify-drift, 27-verify-orphan, 28-drift-checkprobe_drift (4env)internal/verify, internal/generateGenerated-vs-committed drift is detected and surfaced on a real run
Validate gate14-validate-check, 17-validate-callbackprobe_validate (4env); pre-build validate gate (3env)internal/generateA validate callback gates the build before it proceeds
Merge queue15-merge-queueprobe_merge_queue (4env)internal/generateThe merge-queue lane is emitted and runs (harness covers the no-configured-queue case)
Pull-request preview16-pr-previewpr-preview-check (2env)internal/generateThe preview run fires on a PR and posts its comment
External update and notify (cross-repo)21-cross-repo-callback, multi-repo/*external-update from artifact-a and artifact-b, concurrent no-loss (primary)internal/externalA satellite deploy writes the primary’s shared manifest with no lost update
Manage-release verbs (create, update, lock, prerelease, publish, delete)05-publish-callbackmanage-release-verbs lock to prerelease, orphan delete (primary); single-env stagesinternal/releaseEach release verb mutates the real release object as specified
State write (Contents API) and retry-on-conflict08-state-push-retrystate read-back after every step (all multi-step repos)internal/statewrite, internal/promoteState commits land and survive a concurrent-writer race
Featureact plus gitea scenarioLive-fleet probe (repo)UnitWhat the layer proves
No-change skip06-no-change-skipprobe_concurrency step 12 (4env)internal/changesA re-dispatch with no watched change skips, on real Actions
Concurrency cancellation07-orchestrate-concurrencyprobe_concurrency cancel (4env)internal/generateThe older run in a per-component group is cancelled and the survivor concludes
Breaking-change gate37-release-breaking-gate, 38-promote-breaking-gate-release-buildrelease-gates (primary)internal/promote, internal/releaseA breaking transition is refused without the explicit allow flag
Promote from diverged env blockedrollback/rollback-marks-diverged-blocks-promoterollback-check diverged-blocks-promote (2env)internal/promotePromotion from a diverged source is refused (registered as an expected failure)
Allow-downgrade and prod guard20-promote-allow-downgraderelease-gates (primary)internal/promote/downgrade.goA downgrade needs the flag, and prod needs it even when a lower env does not
Promote with missing sourcepromote/promote-fails-missing-sourceinternal/promoteA promotion with no built source fails fast
Build failure stops promotionerrors/build-failure-stops-promotioninternal/orchestrateA failed build halts the chain rather than promoting a broken artifact
Withheld-secret callback refusalorchestrate/secrets-default-nonewithheld-negative stage 2 (callbacks)internal/generateA callee denied its secret fails, registered as an expected failure
Featureact plus gitea scenarioLive-fleet probe (repo)UnitWhat the layer proves
Reusable-workflow callbacks (cross-repo @ref)21-cross-repo-callbackcross-repo build consolidated (primary), build callback (artifact-a, artifact-b)internal/generateA real @ref callee runs and its artifact consolidates into state
Inline callbacks (run and shell)orchestrate/secrets-opt-incallback postures (callbacks, 3env)internal/generateInline build and deploy callbacks run with their declared posture
Per-callback secrets opt-inorchestrate/secrets-opt-in, secrets-default-nonesecret opt-in posture at callee (callbacks)internal/generateA secret reaches only the callee that opted in
Least-privilege permissionsorchestrate/least-privilege-permissionsleast-priv posture (callbacks); gen-time wiring (3env)internal/generatePermissions are scoped to the job that needs them, not the top level
OIDC id-token propagationorchestrate/callback-permissions-oidcOIDC posture at callee (callbacks); gen-time id-token: write scoped (3env)internal/generateid-token: write propagates to the caller job without leaking workflow-wide
Callback dependency ordering (depends_on)11-job-timeouts-and-optional-depsneeds ordering (callbacks); base to app order (3env gen-time)internal/generateA dependent callback starts only after its prerequisite concludes
Callback retry wrapperretry-wrapper jobs present (callbacks); retry shim jobs (3env gen-time)internal/generateThe retry jobs are emitted and wired for retries: N
Signed auto-commit identity (auto_commits)03-three-env-repoauto_commits author and message (3env)internal/promote/auto_commit_sha*.goThe state commit carries the configured author and message

These are about what the generator emits. Several are asserted at the emission layer on purpose, because the bytes are the contract and a live run would add no signal beyond what the structural assertion already proves.

Featureact plus gitea scenarioLive-fleet probe (repo)UnitWhat the layer proves
Action pins (sha mode, default tag, per-action override)35, 36, 39internal/generateThe pin mode lands the correct immutable SHA or mutable tag in the emitted bytes
Reserved shapes (canary and blue-green, gitops target, telemetry sink, version overrides)23, 24, 25, 26internal/config, internal/generateA parsed-but-not-yet-generated block round-trips cleanly through verify
Dispatch inputs13-dispatch-inputs, 20-promote-allow-downgradedispatch-inputs-check reason threading (2env)internal/generateA dispatch input reaches consolidated state
Environment config emit29-environment-config-emitinternal/environmentsThe per-environment config file is emitted for the operator to apply
Custom changelog and contributors19-custom-changelogchangelog assembly (release-only)internal/changelogThe changelog and contributor section assemble from the commit range
Owned-job timeouts and optional dependencies11-job-timeouts-and-optional-depsinternal/generateTimeouts and optional needs land on the right jobs
Notify overridesnotify overrides reach dispatch (artifact-a, artifact-b)internal/generateConfigured notify.deploy_name and notify.environment override the defaults
Matrix builds02-two-env-repoper-leg matrix artifacts (2env)internal/generateEach matrix leg produces its artifact
Native deployments API calls31-native-deploymentsinternal/generateThe deployment create, in-progress, and status calls are emitted (objects are GitHub-side)
App token source and mint steps30-app-token-source, orchestrate/22-release-token-bare-secret-name, orchestrate/23-release-token-defaults-to-state-tokeninternal/generateThe token-source wiring is emitted and defaults correctly (minting is GitHub-side)

Each example repository is validated as its own live pipeline rather than inferred from a general case.

TopologyLive-fleet repositoryact plus gitea scenario
Single environment plus standalone release lanecascade-example-single-env09-single-env-repo
Two environments, matrix, promotion, PR previewcascade-example-2env02-two-env-repo
Three environments, validate gate, env-branch handling, signed identitycascade-example-3env03-three-env-repo
Four environments, cascade mode, breaking gate, hotfix, rollbackcascade-example-4env04-cascade-promotion
Release-only (no deploy environments)cascade-example-release-onlynone specific (release lane via 37, 38)
No-environment library shapecovered in harness only by design01-no-env-repo
Primary plus artifact satellites (cross-repo graph)cascade-example-primary, cascade-example-artifact-a, cascade-example-artifact-bmulti-repo/*, 21-cross-repo-callback
Per-callback secrets, permissions, and OIDC posture across reusable and inline callbackscascade-example-callbacksorchestrate/secrets-opt-in, orchestrate/callback-permissions-oidc
External rollback entry point (repository_dispatch)cascade-example-rollback-dispatchrollback/*

The no-environment library shape is covered in the act plus gitea harness; a live cascade-example-no-env suite also asserts that orchestrate goes straight from a trunk merge to a minted RC with no deploy job in the run.

Read-only commands, unit and CLI by design

Section titled “Read-only commands, unit and CLI by design”

These commands make no GitHub or container calls and are fully deterministic. Placing them on the live fleet would add runtime with no behavior to assert, so they are covered at the unit and CLI tier on purpose. This is a deliberate choice, not a coverage gap.

FeatureLayerCoverageWhat it proves
cascade simulate (what-if engine and four subcommands)unit plus CLIinternal/simulate/*_test.go, cmd/cascade/main_test.goThe orchestration state machine replays in record-only mode and renders a deterministic diff and effect sequence
cascade graph (Mermaid render, three granularities)unit plus CLIinternal/graph/graph_test.go, cmd/cascade/main_test.goThe manifest projects to a valid Mermaid diagram for jobs, stages, and env views
internal/visualize (view model, themes, emitter)unitinternal/visualize/*_test.goThe pure render turns a view model and theme into Mermaid source deterministically
cascade init and scaffoldunit plus CLIinternal/initcmd, internal/scaffoldThe scaffold self-checks through the real generator before any file is written
cascade verify (drift)unit plus every harness scenariointernal/verifyCommitted workflows match manifest-regenerated bytes; drift exits non-zero
cascade plan (diff preview)unit plus CLI33-plan-diff, internal/planThe per-file unified diff is produced without writing files
parse-config, schema, next-version, detect-changes, generate-changelogunitper-package testsPure logic: parsing, version calculation, change detection, changelog assembly
cascade status and status consistencyunit plus harness27-verify-orphan, internal/statusState is reported and orphan env branches are flagged, and deleted on the remote with --fix
branch-protection and environments emitunit plus CLIinternal/branchprotection, internal/environmentsThe JSON body and env config are emitted for the operator (applying them is GitHub-side)

Some behavior depends on GitHub platform features and real cloud outcomes that neither layer fakes. These are validated by design and inspection, asserting the part cascade owns (the emitted structure and the orchestration around the call) and treating the platform-enforced outcome as a contract validated by review.

  • GitHub Environment protection (required reviewers, wait timers): cascade emits the environment: reference; the approval gate is GitHub enforcing your rule.
  • Native Deployment objects: the create, in-progress, and status calls are asserted at emission; the resulting objects are GitHub-side.
  • The Verified signed-commit badge: cascade drives a signed state commit; the badge is awarded by GitHub against your key.
  • GitHub App token minting: the mint steps are asserted at emission; an installation token can only be minted against github.com.
  • Real cloud deploy outcomes: cascade orchestrates your build and deploy callbacks; what they do against your cloud is yours to test.

Cascade does not claim full live coverage of every feature. It claims full coverage across the layers, with a documented ceiling. Every lifecycle and guard behavior that can run live runs live in the fleet; every emission and synthesized-condition behavior is asserted in the act plus gitea harness; every piece of pure logic is covered by unit tests; and the platform ceiling is validated by design.