Problem
The promote workflow resolves inputs and env_inputs into JSON matrix literals during generation. Only three ${{ matrix.* }} substitutions are applied (promote.go:168–315, in resolveDeployInputs and writeMatrixBuildingLogic). A manifest value like ${{ vars.DEPLOY_BUCKET }} or ${{ secrets.API_KEY }} is written as a literal string into the matrix JSON and arrives at the callback as a dead, uninterpolated expression.
The orchestrate path is also unverified: generator.go:589–623 carries environment/sha/dep outputs through with:, but ${{ vars.X }} values in operator-authored with: entries may not survive the passthrough correctly.
The consequence is that per-environment config must live in the manifest as literals rather than in GitHub's Environment Variables settings — where operators actually manage it and where the environment-protection gates (planned separately) control access.
What to add
Document and enforce a clean rule: manifest inputs/env_inputs values that look like GHA expression syntax (${{ ... }}) must be passed through to the emitted with: block verbatim, without literal resolution into the matrix JSON.
On the promote path, values that are pure expressions (the entire value is ${{ ... }}) should remain as expression strings in the matrix payload, allowing GHA's own runtime to evaluate them when the called workflow runs.
Schema sketch:
No new field is required. The change is in the emit semantics: resolveDeployInputs should detect expression-syntax values and skip literal resolution for them, emitting the expression as-is.
A doc/validation note clarifying which expression contexts are valid in inputs values (e.g. ${{ vars.X }}, ${{ matrix.environment }}, ${{ github.sha }}) is part of this work.
Acceptance criteria
Problem
The promote workflow resolves
inputsandenv_inputsinto JSON matrix literals during generation. Only three${{ matrix.* }}substitutions are applied (promote.go:168–315, inresolveDeployInputsandwriteMatrixBuildingLogic). A manifest value like${{ vars.DEPLOY_BUCKET }}or${{ secrets.API_KEY }}is written as a literal string into the matrix JSON and arrives at the callback as a dead, uninterpolated expression.The orchestrate path is also unverified:
generator.go:589–623carries environment/sha/dep outputs throughwith:, but${{ vars.X }}values in operator-authoredwith:entries may not survive the passthrough correctly.The consequence is that per-environment config must live in the manifest as literals rather than in GitHub's Environment Variables settings — where operators actually manage it and where the environment-protection gates (planned separately) control access.
What to add
Document and enforce a clean rule: manifest
inputs/env_inputsvalues that look like GHA expression syntax (${{ ... }}) must be passed through to the emittedwith:block verbatim, without literal resolution into the matrix JSON.On the promote path, values that are pure expressions (the entire value is
${{ ... }}) should remain as expression strings in the matrix payload, allowing GHA's own runtime to evaluate them when the called workflow runs.Schema sketch:
No new field is required. The change is in the emit semantics:
resolveDeployInputsshould detect expression-syntax values and skip literal resolution for them, emitting the expression as-is.A doc/validation note clarifying which expression contexts are valid in
inputsvalues (e.g.${{ vars.X }},${{ matrix.environment }},${{ github.sha }}) is part of this work.Acceptance criteria
env_inputsvalue of${{ vars.BUCKET }}is emitted verbatim in the callbackwith:block, not as the literal string${{ vars.BUCKET }}trapped inside a JSON string inside a matrix literal.${{ matrix.environment }}and other matrix substitutions continue to work as before.cascade validatewarns when a non-expression literal value ininputscontains characters that suggest the operator may have intended an expression (e.g. a barevars.Xwithout${{ }}).env_inputs: {bucket: "${{ vars.DEPLOY_BUCKET }}"}generates a promote workflow where the deploy callback'swith.bucketis${{ vars.DEPLOY_BUCKET }}; a GitHub Actions dry-run on a repo withDEPLOY_BUCKET=my-bucketin Environment Variables resolves it correctly at runtime.