Problem
ADO only evaluates $[...] runtime expressions inside job-level �ariables: mappings and condition: fields — NOT in step �nv: blocks. Putting one in step env passes the literal expression string verbatim.
This has caused bugs at least twice:
Both were caught by review/tests, but the compiler itself doesn't prevent the mistake.
Proposal
Add an EnvValue::RuntimeExpression variant (or similar) to the typed IR that:
- Accepts $[...] expressions as a distinct type (not Literal or AdoMacro)
- During lowering, automatically hoists the expression to a job-level �ariables: entry with a compiler-generated name
- In the step env, emits a $(generated_name) macro reference pointing to the hoisted variable
This makes it structurally impossible to accidentally put a $[...] expression in step env — the type system forces the correct pattern.
Additional guard (quick win)
As a complementary measure, add a validation pass (or extend the existing �ssert_no_dollar_bracket_in_step_env test helper) that rejects EnvValue::Literal values containing $[ at IR construction time, with an error message pointing to the correct RuntimeExpression variant.
References
Problem
ADO only evaluates $[...] runtime expressions inside job-level �ariables: mappings and condition: fields — NOT in step �nv: blocks. Putting one in step env passes the literal expression string verbatim.
This has caused bugs at least twice:
Both were caught by review/tests, but the compiler itself doesn't prevent the mistake.
Proposal
Add an EnvValue::RuntimeExpression variant (or similar) to the typed IR that:
This makes it structurally impossible to accidentally put a $[...] expression in step env — the type system forces the correct pattern.
Additional guard (quick win)
As a complementary measure, add a validation pass (or extend the existing �ssert_no_dollar_bracket_in_step_env test helper) that rejects EnvValue::Literal values containing $[ at IR construction time, with an error message pointing to the correct RuntimeExpression variant.
References