fix(compile): unify synthetic-PR variable namespace via ado-script#972
Conversation
Move the real-vs-synth PR-identifier merge from broken step-env coalesce expressions into the exec-context-pr-synth.js bundle. ADO does not evaluate `$[ ... ]` runtime expressions inside step env values (only inside variables: and condition: fields), so the previous `SYSTEM_PULLREQUEST_PULLREQUESTID: `$[ coalesce(...) ]` form passed the literal string to bash and broke both Stage PR execution context and Evaluate PR filters (msazuresphere/4x4 build 612528). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Looks good — the root cause diagnosis is correct and the fix is architecturally sound. Two minor issues worth addressing. Findings
|
fix(compile): unify synthetic-PR variable namespace via ado-script
Move the real-vs-synth PR-identifier merge from broken step-env coalesce
expressions into the
exec-context-pr-synth.jsbundle, so everydownstream consumer reads a single canonical
AW_PR_*macro regardlessof build reason.
Root cause (msazuresphere/4x4 build 612528)
ADO
$[ ... ]runtime expressions are evaluated only insidevariables:mappings andcondition:fields — never inside stepenv:values. The previous compiler emitteddirectly inside step env in two places (
exec_context/pr.rs+filter_ir.rs). ADO passed the literal expression string verbatim tobash. Build 612528:
[aw-context] pr context preparation failed: PR identifier validation failed (PR_ID='$[ coalesce(variables['System.PullRequest.PullRequestId'], variables['AW_SYNTHET…' is not a positive integer).Fact 'pr_metadata' failed (Missing ADO env vars (ADO_PROJECT/ADO_REPO_ID/ADO_PR_ID) required for fact 'pr_metadata'); dependent checks skippedComments at
exec_context/pr.rs:149andcompile/common.rs:1127bothclaimed step env supported
$[ ... ]— empirically it does not.Fix
Do the merge inside ado-script, not in step env:
exec-context-pr-synth/index.ts— runs unconditionally now. Onreal PR builds, copies
SYSTEM_PULLREQUEST_*→AW_PR_*. Onsynth-promoted CI builds, discovers + emits
AW_PR_*plusAW_SYNTHETIC_PR="true". On soft skips / GitHub repos, emits emptyAW_PR_*+AW_SYNTHETIC_PR_SKIP="true". Every path emits thecanonical variable set via both
setOutput(cross-job) andsetVar(same-job).ado_script.rs::synthetic_pr_step— removedcondition: ne(Build.Reason, 'PullRequest'); addedSYSTEM_PULLREQUEST_*envpassthrough so the bundle can detect real PR builds.
common.rs::generate_agent_job_variables— hoists renamedcanonical
AW_PR_ID/AW_PR_TARGETBRANCH/AW_PR_SOURCEBRANCH(+ existing
AW_SYNTHETIC_PRflag) fromdependencies.Setup.outputs['synthPr.*'](the legitimate$[ ... ]location).
exec_context/pr.rs+filter_ir.rs— step env now usesplain
$(AW_PR_*)macros; no$[ ... ]in step env. The bashgate collapses from
if [ "$BUILD_REASON" != "PullRequest" ] && [ "$AW_SYNTHETIC_PR" != "true" ]to a singleif [ -z "$AW_PR_ID" ]empty-check.Regression guard
New
assert_no_dollar_bracket_in_step_envtest walks every step'senv:block in compiled YAML and asserts no$[appears. Catchesthis entire bug class going forward.
Test plan
cargo test— 1821 unit tests + 132 compiler integration tests pass(5 previously-failing tests rewritten to assert the new contract:
prepare_step_synth_active_uses_macros_for_hoisted_aw_pr_vars_and_bash_guard,setup_steps_emits_synth_step_when_synthetic_pr_active_without_gate,test_pr_filter_synth_mode_gate_step_uses_same_job_synth_ref,test_execution_context_pr_emits_prepare_step_and_prompt_supplement,test_synthetic_pr_default_emits_full_synth_wiring,test_generate_agent_job_variables_emits_hoisted_synth_outputs).cargo clippy --all-targets --all-features— clean.npm test(scripts/ado-script) — 282 tests pass including therewritten synthPr suite covering real-PR / GitHub-repo / synth /
soft-skip paths.
npm run test:smoke— 6 smoke tests pass.pr-filter-tier1-agent.mdconfirms$[ ... ]appears only inside the Agent job's
variables:block (4 hoists)and never in any step
env:.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com