Problem
The cascade generator hardcodes mutable action tags in every emitted workflow:
setup-cli@latest — generator.go:444,1026
actions/checkout@v4 — generator.go:438
actions/github-script@v7 — generator.go:935
actions/download-artifact@v4 — generator.go:1097
@latest is a supply-chain and reproducibility hole: a compromised or unintended latest tag is silently adopted by every downstream run. @v4-style tags are better but still mutable — an enterprise org enforcing allowed_actions: sha-pinned policy cannot adopt cascade's generated output at all today.
What to add
Introduce a manifest-level action_pins: map (or a pin_mode field) that controls how action refs are emitted.
Schema sketch:
# manifest.yaml (top-level)
action_pins:
# explicit SHA overrides for any action the generator uses
setup-cli: "sha256:abcdef..." # optional; disables auto-resolution
actions/checkout: "sha256:..."
# ... etc
pin_mode: tag # tag (default) | sha
# sha = generator resolves each ref to its current commit SHA
# at generation time and pins it
When pin_mode: sha, the generator resolves each action ref via the GitHub API (or a lockfile) at cascade generate time and emits the pinned SHA with a # @vX trailing comment for readability. The resolved SHAs are persisted in a lockfile (cascade.lock) so the workflow is reproducible without an API call on every generate.
When pin_mode: tag (default), the generator emits the explicit version tags already known (e.g. @v4) rather than @latest, eliminating the mutable-tag problem without requiring SHA resolution.
The action_pins: map allows individual overrides regardless of pin_mode — useful when an org maintains a fork of an action.
Acceptance criteria
Problem
The cascade generator hardcodes mutable action tags in every emitted workflow:
setup-cli@latest—generator.go:444,1026actions/checkout@v4—generator.go:438actions/github-script@v7—generator.go:935actions/download-artifact@v4—generator.go:1097@latestis a supply-chain and reproducibility hole: a compromised or unintendedlatesttag is silently adopted by every downstream run.@v4-style tags are better but still mutable — an enterprise org enforcingallowed_actions: sha-pinnedpolicy cannot adopt cascade's generated output at all today.What to add
Introduce a manifest-level
action_pins:map (or apin_modefield) that controls how action refs are emitted.Schema sketch:
When
pin_mode: sha, the generator resolves each action ref via the GitHub API (or a lockfile) atcascade generatetime and emits the pinned SHA with a# @vXtrailing comment for readability. The resolved SHAs are persisted in a lockfile (cascade.lock) so the workflow is reproducible without an API call on every generate.When
pin_mode: tag(default), the generator emits the explicit version tags already known (e.g.@v4) rather than@latest, eliminating the mutable-tag problem without requiring SHA resolution.The
action_pins:map allows individual overrides regardless ofpin_mode— useful when an org maintains a fork of an action.Acceptance criteria
@latestfor any action ref (tag mode emits explicit tags).pin_mode: sharesolves and emits full commit SHAs at generation time with a trailing comment.cascade.lockfile records resolved SHAs so re-generation is deterministic without network access.action_pins:map entries override the resolved ref for a named action.pin_modevalues produce a clear validation error.pin_mode: shagenerates a workflow where everyuses:line under cascade-controlled steps contains a 40-char hex SHA; runningcascade generatea second time against the lockfile produces byte-identical output.action_pinscontinue to generate valid workflows (non-breaking default is explicit tag, not SHA).