Skip to content

epic: local pipeline simulator #310

Description

@joshua-temple

Summary

Give cascade users a way to answer "what would happen if I ran this?" locally, before they
touch a real pipeline. cascade already generates the workflows that drive promotion,
hotfix, rollback, and release. The orchestration decisions those workflows make are
implemented in cascade's own Go packages. A simulator surfaces those decisions locally so a
user can preview the outcome of an action against their real manifest and current state.

Two fidelity tiers

Tier 1 - cascade simulate (lead with this; light, in-process, no containers). Given
the manifest, the current ci.state, and a hypothetical action (promote A to B, hotfix a
commit set into an environment, rollback, release), run the SAME orchestration logic the
generated workflows invoke, in-process, and print the resulting state diff (before to
after) plus the ordered sequence of effects. This extends the existing --dry-run into a
full what-if that reports the computed outcome rather than just suppressing mutation. It
validates the ORCHESTRATION (state transitions, gating, divergence, rejoin), NOT the user's
real build or deploy scripts. Fast, no Docker, runnable anywhere.

Tier 2 - high-fidelity local integration (later, heavier). Productize cascade's
existing e2e harness (e2e/harness/: act + gitea + testcontainers) as a user-facing
integration simulator. The user supplies their manifest and deploy stubs; cascade runs the
GENERATED workflows locally through act against a gitea instance, with deploys stubbed. This
exercises the real generated YAML end to end. It requires Docker and is aimed at power users
and CI, not the everyday inner loop.

Scope boundary (applies to both tiers)

The simulator validates cascade's ORCHESTRATION, not the user's deploy scripts. Builds and
deploys are stubbed or recorded. In Tier 1 they never execute; in Tier 2 they run as inert
stub workflows. A green simulation means "cascade would sequence and transition state
correctly," not "your deploy succeeds." This boundary is stated in every issue and in the
docs page so users do not mistake the simulator for a deploy test.

Grounding in the current code

  • Existing dry-run that Tier 1 extends: internal/globals/globals.go (SetDryRun/DryRun),
    internal/promote/command.go and internal/promote/promote.go (--dry-run guards),
    internal/hotfix/plan.go + internal/hotfix/finalize.go (WithDryRun,
    WithFinalizeDryRun), internal/rollback/command.go (report(..., dryRun)).
  • Orchestration logic Tier 1 reuses (not reimplements):
    • internal/promote: Promoter (NewPromoter, Promote) in promote.go, Finalizer
      in finalize.go, rejoin in rejoin.go.
    • internal/hotfix: Planner (PlanChain, multi-commit/multi-env) in plan.go and
      chain.go, Finalizer in finalize.go, divergence handling.
    • internal/rollback: Rollbacker (New, Plan, Apply) in rollback.go, history
      in history.go.
    • internal/release: Manager in release.go.
  • State model: internal/config/types.go - EnvState (with SHA, Version, Builds,
    Deploys, External, Ref, BaseSHA, Patches, Previous, IsDiverged()),
    DeployState, BuildState, EnvStateSnapshot.
  • E2E harness Tier 2 productizes: e2e/harness/ (act.go, gitea.go, harness.go,
    scenario.go, runner.go), scenario fixtures in e2e/scenarios/*.yaml, deploy stubbing
    via generateStubWorkflow in harness.go.
  • Root command wiring for a new simulate subcommand: cmd/cascade/main.go.

Composition with the visualizer

When the env state-machine visualizer lands, cascade simulate can render the simulated
PATH on the visual graph (before to after, with the traversed edges highlighted). That work
depends on the visualizer view-model and follows the visualizer core (visualizer epic
#300). Tracked here as a nice-to-have, not a blocker for Tier 1.

Child issues (dependency order)

  1. feat(simulate): what-if engine that prints a state diff and effect sequence #311 Tier 1 core - cascade simulate what-if engine (state diff + effect sequence)
  2. feat(simulate): simulate promote, hotfix, rollback, and release outcomes #312 Tier 1 actions - simulate promote / hotfix / rollback / release (depends on feat(simulate): what-if engine that prints a state diff and effect sequence #311)
  3. feat(simulate): deploy-stub model and the orchestration-not-deploys boundary #313 Deploy-stub / record model and the orchestration-not-deploys boundary (depends on feat(simulate): what-if engine that prints a state diff and effect sequence #311)
  4. docs: local pipeline simulation (cascade simulate) and its scope #314 Docs - local simulation page and the orchestration-not-deploys boundary (depends on feat(simulate): what-if engine that prints a state diff and effect sequence #311, feat(simulate): simulate promote, hotfix, rollback, and release outcomes #312; ideally after feat(simulate): local integration simulator over the generated workflows (act + gitea) #315)
  5. feat(simulate): local integration simulator over the generated workflows (act + gitea) #315 Tier 2 - productize the e2e harness as a local integration simulator (depends on feat(simulate): what-if engine that prints a state diff and effect sequence #311; Docker required)

Plus a nice-to-have tracked only here (promote to a standalone issue once the visualizer
view-model exists): visualizer composition - render the simulated path on the state-machine
graph (depends on the Tier 1 core, the actions, AND the visualizer epic #300).

Recommended sequencing: core, then actions and deploy-stub in parallel, then Tier 2 and docs.

Acceptance for the epic

  • cascade simulate exists and previews promote, hotfix, rollback, and release outcomes
    with no GitHub calls and no containers.
  • The orchestration-not-deploys boundary is enforced in code and documented.
  • The Tier 2 integration path is available for users who supply Docker and deploy stubs.
  • Docs explain both tiers and the boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dxDeveloper experienceepicsimulatorLocal pipeline simulator feature

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions