Motivation
We want authors to flag a runtime template parameter as "feed this value into the agent prompt", so its value can vary per-run (manual queue or queue-build safe output) without re-compiling the pipeline. A first attempt (#430, since reverted) put both the sanitization rules and the prompt-append logic into the Rust compiler, generating bash heredocs and validating defaults at ado-aw compile time. That coupled the security policy to compiler releases, duplicated work the runtime already has to do for queue-build-supplied values, and made the bash hard to test in isolation.
This issue tracks the redo: keep the compiler dumb (just pass values through env vars), and let the ado-script runtime bundle (the same one that ships gate-eval.py today, and prompt.js / gate.js going forward) own validation and prompt assembly.
Proposed design
Front matter (compiler)
Add an opt-in flag on PipelineParameter:
parameters:
- name: focusArea
displayName: "Focus area for this run"
type: string
default: "no specific focus"
prompt-context: true
Compiler responsibilities, and only these:
- Parse the new
prompt-context: bool field (kebab-case, skip_serializing so it never leaks into emitted ADO YAML).
- For each opted-in parameter, emit a step-level
env: mapping ADO_AW_CTX_<UPPER_NAME>: ${{ parameters.<name> }} on the existing "Prepare agent prompt" step.
- Emit one additional env var,
ADO_AW_PROMPT_CONTEXT_SPEC, holding a small base64-encoded JSON spec: an ordered list of { envKey, displayName } entries plus the prompt-file path. (Mirrors the ADO_AW_PROMPT_SPEC pattern used by prompt.js.)
- No bash generation. No
printf heredocs. No validation of default values at compile time — the runtime will validate the same way it validates queue-build-supplied parameter values.
Runtime (ado-script)
Sanitization and injection live in a new module under scripts/ado-script/src/prompt-context/ (TypeScript, NodeNext imports with .js, consistent with prompt/ and gate/). Wired into prompt.js so it runs as part of the existing prompt-render step — no new pipeline step, no extra script download.
Validation rules (applied per value at runtime; reject the run on violation):
- Bounded byte length (≤ 4 KiB) and bounded newline count (≤ 64).
- Reject ADO expression syntax:
${{, $(, $[.
- Reject pipeline logging commands:
##vso[, ##[.
- Reject the compiler's template-marker delimiter
{{.
- Reject CR and non-tab control characters.
displayName (validated when the spec is decoded) rejects ", \, `, $.
On success, append a clearly delimited "Additional Run Context" section to the rendered prompt — one sub-section per non-empty value, using displayName as the header — with a preamble that explicitly tells the agent the section is untrusted user input.
Why ado-script, not the compiler
- Single source of truth for the policy: the same sanitizer that already gates queue-build parameter values can be reused, instead of two implementations drifting.
- Policy updates ship as
scripts.zip releases (already verified via checksums.txt + sha256sum, see src/compile/extensions/trigger_filters.rs:102-109); no compiler release required.
- Testable in TS with the rest of the runtime; the compiler stays free of bash codegen.
- Defaults that look fine statically but resolve through ADO expression evaluation can still be caught (the runtime sees the post-evaluation string).
Acceptance criteria
Out of scope
- Changing how queue-build allowlists or validates parameters (already covered by
allowed-parameters + expression-syntax rejection).
- Surfacing values to anything other than the agent prompt.
References
- Reverted attempt: commit
131c6ed on copilot/optional-context-parameter (revert in 461b443).
- Existing
scripts.zip delivery pattern: src/compile/extensions/trigger_filters.rs:102-109.
- Existing runtime spec-via-base64-env pattern:
prompt.js and ADO_AW_PROMPT_SPEC.
Motivation
We want authors to flag a runtime template parameter as "feed this value into the agent prompt", so its value can vary per-run (manual queue or queue-build safe output) without re-compiling the pipeline. A first attempt (#430, since reverted) put both the sanitization rules and the prompt-append logic into the Rust compiler, generating bash heredocs and validating defaults at
ado-awcompile time. That coupled the security policy to compiler releases, duplicated work the runtime already has to do for queue-build-supplied values, and made the bash hard to test in isolation.This issue tracks the redo: keep the compiler dumb (just pass values through env vars), and let the
ado-scriptruntime bundle (the same one that shipsgate-eval.pytoday, andprompt.js/gate.jsgoing forward) own validation and prompt assembly.Proposed design
Front matter (compiler)
Add an opt-in flag on
PipelineParameter:Compiler responsibilities, and only these:
prompt-context: boolfield (kebab-case,skip_serializingso it never leaks into emitted ADO YAML).env:mappingADO_AW_CTX_<UPPER_NAME>: ${{ parameters.<name> }}on the existing "Prepare agent prompt" step.ADO_AW_PROMPT_CONTEXT_SPEC, holding a small base64-encoded JSON spec: an ordered list of{ envKey, displayName }entries plus the prompt-file path. (Mirrors theADO_AW_PROMPT_SPECpattern used byprompt.js.)printfheredocs. No validation of default values at compile time — the runtime will validate the same way it validates queue-build-supplied parameter values.Runtime (
ado-script)Sanitization and injection live in a new module under
scripts/ado-script/src/prompt-context/(TypeScript, NodeNext imports with.js, consistent withprompt/andgate/). Wired intoprompt.jsso it runs as part of the existing prompt-render step — no new pipeline step, no extra script download.Validation rules (applied per value at runtime; reject the run on violation):
${{,$(,$[.##vso[,##[.{{.displayName(validated when the spec is decoded) rejects",\,`,$.On success, append a clearly delimited "Additional Run Context" section to the rendered prompt — one sub-section per non-empty value, using
displayNameas the header — with a preamble that explicitly tells the agent the section is untrusted user input.Why
ado-script, not the compilerscripts.zipreleases (already verified viachecksums.txt+sha256sum, seesrc/compile/extensions/trigger_filters.rs:102-109); no compiler release required.Acceptance criteria
prompt-context: trueround-trips through front-matter and is stripped from emitted YAML.ADO_AW_CTX_*env vars andADO_AW_PROMPT_CONTEXT_SPECto the "Prepare agent prompt" step, with no other changes.prompt.jsdecodes the spec, validates each value with the rules above, and either appends the section or fails the step with a clear error.docs/parameters.md,docs/front-matter.md, anddocs/ado-script.mddescribe the feature and the runtime-side validation contract.Out of scope
allowed-parameters+ expression-syntax rejection).References
131c6edoncopilot/optional-context-parameter(revert in461b443).scripts.zipdelivery pattern:src/compile/extensions/trigger_filters.rs:102-109.prompt.jsandADO_AW_PROMPT_SPEC.