11---
22title : ado-script
3- description : Bundled TypeScript runtime helpers for gate evaluation and import resolution
3+ description : Bundled TypeScript runtime helpers for gate evaluation, import resolution, and PR execution context
44---
55
66import { Aside } from ' @astrojs/starlight/components' ;
77
88` ado-script ` is the umbrella name for the TypeScript workspace at
99[ ` scripts/ado-script/ ` ] ( https://github.com/githubnext/ado-aw/tree/main/scripts/ado-script ) .
10- It produces small, ncc-bundled Node programs that the ** compiler injects into every emitted
11- pipeline** as runtime helpers. Today it produces ` gate.js ` , the
12- trigger-filter gate evaluator, and ` import.js ` , the runtime prompt
13- resolver described in [ Runtime Imports] ( /ado-aw/reference/runtime-imports/ ) .
10+ It produces small, ncc-bundled Node programs that the ** compiler injects into emitted
11+ pipelines** as runtime helpers. Today it produces three bundles:
12+
13+ - ** ` gate.js ` ** — trigger-filter gate evaluator (Setup job)
14+ - ** ` import.js ` ** — runtime prompt resolver described in [ Runtime Imports] ( /ado-aw/reference/runtime-imports/ ) (Agent job)
15+ - ** ` exec-context-pr.js ` ** — PR execution-context precompute described in [ Execution Context] ( /ado-aw/reference/execution-context/ ) (Agent job, PR builds only)
1416
1517<Aside type = " caution" title = " Internal-only" >
1618 ` ado-script ` is not a user-facing front-matter feature. Authors never write an ` ado-script: ` block in their agent
@@ -68,6 +70,63 @@ binary and inlined into the emitted YAML at compile time, matching
6870gh-aw's pattern (their ` threat_detection.md ` ships with the setup
6971action and is read directly from disk — no marker, no resolver).
7072
73+ ## What ` exec-context-pr.js ` does
74+
75+ ` exec-context-pr.js ` is a single-shot Node program injected into the Agent
76+ job's prepare phase ** only on PR-triggered builds** . It resolves the PR
77+ merge-base and stages artefacts the agent can use to reason about the diff:
78+
79+ ```
80+ aw-context/
81+ └── pr/
82+ ├── base.sha # target merge-base SHA (40-char hex)
83+ ├── head.sha # PR head SHA (40-char hex)
84+ └── error.txt # present only on failure; one-line reason
85+ ```
86+
87+ It also appends a tailored prompt fragment to ` /tmp/awf-tools/agent-prompt.md `
88+ (the file assembled by the "Prepare agent prompt" base-YAML step). On success
89+ the fragment tells the agent its PR number, project, repository, and provides
90+ pre-filled example ADO MCP tool call arguments. On failure it surfaces a
91+ graceful degradation message so the agent can still proceed without panicking.
92+
93+ ### Merge-base resolution
94+
95+ Two paths, both computing the same "merge-base of target tip and PR head":
96+
97+ 1 . ** Synthetic merge commit** — ADO's default PR checkout produces a merge
98+ commit (` HEAD ` has ≥ 2 parents). ` HEAD^1 ` is the target tip; ` HEAD^2 ` is
99+ the PR head. The bundle runs ` git merge-base HEAD^1 HEAD^2 ` .
100+ 2 . ** Progressive deepening** — when ` HEAD ` is a normal commit (shallow clone
101+ without a merge commit), the bundle fetches the target branch at increasing
102+ depths (` --depth=200 ` , ` 500 ` , ` 2000 ` , ` --unshallow ` ) until
103+ ` git merge-base origin/<target> HEAD ` resolves.
104+
105+ ### Trust boundary
106+
107+ ` SYSTEM_ACCESSTOKEN ` is mapped only into this step's ` env: ` block — never into
108+ the agent step's env. The bearer is passed to spawned ` git ` child processes via
109+ ` GIT_CONFIG_COUNT ` / ` GIT_CONFIG_KEY_0 ` / ` GIT_CONFIG_VALUE_0 ` env vars (the
110+ ` http.extraheader ` pattern), so it never appears in argv or in ` .git/config ` .
111+
112+ ### Env-var contract
113+
114+ | Env var | ADO source | Purpose |
115+ | ---| ---| ---|
116+ | ` SYSTEM_ACCESSTOKEN ` | ` $(System.AccessToken) ` | Bearer for ` git fetch ` of the target branch |
117+ | ` SYSTEM_PULLREQUEST_PULLREQUESTID ` | ` $(System.PullRequest.PullRequestId) ` | PR number (validated: digits only) |
118+ | ` SYSTEM_PULLREQUEST_TARGETBRANCH ` | ` $(System.PullRequest.TargetBranch) ` | Target branch ref (e.g. ` refs/heads/main ` ) |
119+ | ` SYSTEM_TEAMPROJECT ` | ` $(System.TeamProject) ` | ADO project name (validated: alphanumeric + ` . _ - ` ) |
120+ | ` BUILD_REPOSITORY_NAME ` | ` $(Build.Repository.Name) ` | Repository name (validated: alphanumeric + ` . _ - ` ) |
121+ | ` BUILD_SOURCESDIRECTORY ` | ` $(Build.SourcesDirectory) ` | Workspace root — where ` aw-context/pr/ ` is staged |
122+
123+ All four identifier env vars are validated against strict allowlist regexes
124+ before any value is interpolated into a git refspec or the agent prompt. A
125+ validation failure writes ` aw-context/pr/error.txt ` and a failure-fragment to
126+ the agent prompt, then exits 0 so the rest of the pipeline can continue.
127+
128+
129+
71130## End-to-end data flow
72131
73132```
@@ -188,17 +247,24 @@ scripts/ado-script/
188247│ │ ├── facts.ts # fact acquisition (env + REST)
189248│ │ ├── predicates.ts # 11 predicate evaluators + validatePredicateTree + glob ReDoS hardening
190249│ │ └── selfcancel.ts # best-effort build cancellation
191- │ └── import/ # import.js entry point + runtime prompt resolver
192- │ ├── index.ts # main(): expand runtime-import markers in place
193- │ └── __tests__/ # marker, path-resolution, and single-pass coverage
250+ │ ├── import/ # import.js entry point + runtime prompt resolver
251+ │ │ ├── index.ts # main(): expand runtime-import markers in place
252+ │ │ └── __tests__/ # marker, path-resolution, and single-pass coverage
253+ │ └── exec-context-pr/ # exec-context-pr.js entry point + PR context modules
254+ │ ├── index.ts # main(): validate IDs → merge-base → stage artefacts → append prompt fragment
255+ │ ├── validate.ts # strict allowlist regexes for all 4 PR identifier env vars
256+ │ ├── merge-base.ts # synthetic-merge + progressive-deepening resolution
257+ │ ├── git.ts # git runner helpers + bearerEnv() for token isolation
258+ │ └── prompt.ts # successFragment() / failureFragment() for agent-prompt.md
194259├── test/ # End-to-end smoke tests
195260├── gate.js # ncc bundle output (gitignored)
196- └── import.js # ncc bundle output (gitignored)
261+ ├── import.js # ncc bundle output (gitignored)
262+ └── exec-context-pr.js # ncc bundle output (gitignored)
197263```
198264
199265The release workflow (` .github/workflows/release.yml ` ) runs
200- ` npm ci && npm run build ` , then zips ` scripts/ado-script/gate.js ` and
201- ` scripts/ado-script/import.js ` into
266+ ` npm ci && npm run build ` , then zips ` scripts/ado-script/gate.js ` ,
267+ ` scripts/ado-script/import.js ` , and ` scripts/ado-script/exec-context-pr.js ` into
202268the ` ado-script.zip ` release asset. Pipelines download that asset at
203269runtime by URL pinned to the compiler's ` CARGO_PKG_VERSION ` , verify
204270its SHA-256 against the ` checksums.txt ` asset, then extract.
@@ -239,7 +305,7 @@ cargo run -- export-gate-schema --output schema/gate-spec.schema.json
239305
240306` AdoScriptExtension `
241307(` src/compile/extensions/ado_script.rs ` ) is the always-on single
242- extension that owns all ` ado-script ` wiring. It has two independent
308+ extension that owns all ` ado-script ` wiring. It has three independent
243309features, each emitted ** into the job that actually consumes the
244310bundle** :
245311
@@ -259,37 +325,51 @@ three step strings into the Setup job:
259325 runs the gate with ` GATE_SPEC ` and the env-var contract documented
260326 above.
261327
262- ### Agent job (runtime-import resolver)
328+ ### Agent job (runtime-import resolver and PR context)
329+
330+ The Agent job's ` prepare_steps() ` fires when ** either** ` import.js ` or
331+ ` exec-context-pr.js ` is active. It always returns install + download first, then
332+ appends the relevant invocation steps.
263333
264- When ` inlined-imports: false ` (the default), ` prepare_steps() ` returns
265- the same install + download pair plus the resolver invocation, into
266- the Agent job's existing ` {{ prepare_steps }} ` block:
334+ ** ` import.js ` invocation** — active when ` inlined-imports: false ` (the default):
267335
268- 1 . ** ` NodeTool@0 ` ** — same shape as above.
269- 2 . ** ` curl ` download + verify + extract** — same artefact, same
270- verification.
336+ 1 . ** ` NodeTool@0 ` ** — installs Node 20.x LTS, capped at ` timeoutInMinutes: 5 ` .
337+ 2 . ** ` curl ` download + verify + extract** — same artefact and verification as the Setup job.
2713383 . ** ` bash: node '/tmp/ado-aw-scripts/ado-script/import.js' ` ** —
272339 expands ` {{#runtime-import …}} ` markers in
273340 ` /tmp/awf-tools/agent-prompt.md ` in place. See
274341 [ Runtime Imports] ( /ado-aw/reference/runtime-imports/ ) for marker syntax.
275342
343+ ** ` exec-context-pr.js ` invocation** — emitted by ` ExecContextExtension `
344+ (` src/compile/extensions/exec_context/pr.rs ` , ` Tool ` phase) when ` on.pr ` is
345+ configured and ` execution-context.pr.enabled ` is not ` false ` :
346+
347+ 4 . ** ` bash: node '/tmp/ado-aw-scripts/ado-script/exec-context-pr.js' ` ** —
348+ resolves the merge-base and stages ` aw-context/pr/{base,head}.sha ` , then
349+ appends a prompt fragment. Gated by
350+ ` condition: eq(variables['Build.Reason'], 'PullRequest') ` so it is a no-op
351+ on non-PR builds.
352+
276353### Per-job download (NOT a duplication bug)
277354
278355ADO jobs use ** isolated VMs** — ` /tmp ` is not shared between jobs.
279356The ` ado-script.zip ` bundle therefore has to be downloaded once per
280- job that consumes it. When both features are active (a pipeline with
281- both ` filters: ` and ` inlined-imports: false ` ), install + download
282- steps appear in ** both** Setup and Agent. That's correct architecture
283- given ADO's topology, not waste.
357+ job that consumes it. When both the Setup gate and Agent resolver/PR-context
358+ are active, install + download steps appear in ** both** Setup and Agent.
359+ That's correct architecture given ADO's topology, not waste.
284360
285361### What gets emitted, by case
286362
287- | ` filters: ` | ` inlined-imports ` | Setup-job steps | Agent-job extra steps |
288- | ---| ---| ---| ---|
289- | inactive | ` true ` | (none) | (none) |
290- | inactive | ` false ` | (no Setup job) | install + download + resolver |
291- | active | ` true ` | install + download + gate | (none) |
292- | active | ` false ` | install + download + gate | install + download + resolver |
363+ | ` filters: ` | ` inlined-imports ` | ` on.pr ` w/ exec-context | Setup-job steps | Agent-job extra steps |
364+ | ---| ---| ---| ---| ---|
365+ | inactive | ` true ` | no | (none) | (none) |
366+ | inactive | ` true ` | yes | (none) | install + download + exec-context-pr |
367+ | inactive | ` false ` | no | (no Setup job) | install + download + resolver |
368+ | inactive | ` false ` | yes | (no Setup job) | install + download + resolver + exec-context-pr |
369+ | active | ` true ` | no | install + download + gate | (none) |
370+ | active | ` true ` | yes | install + download + gate | install + download + exec-context-pr |
371+ | active | ` false ` | no | install + download + gate | install + download + resolver |
372+ | active | ` false ` | yes | install + download + gate | install + download + resolver + exec-context-pr |
293373
294374The IR-to-bash codegen that produces the gate step is
295375` compile_gate_step_external ` in ` src/compile/filter_ir.rs ` .
@@ -325,6 +405,9 @@ The IR-to-bash codegen that produces the gate step is
325405
326406### Add a new bundle (e.g. ` poll.js ` )
327407
408+ The existing ` exec-context-pr.js ` bundle is a working example of this pattern — see
409+ ` scripts/ado-script/src/exec-context-pr/ ` and ` src/compile/extensions/exec_context/pr.rs ` .
410+
3284111 . Create ` src/poll/index.ts ` and supporting modules under
329412 ` scripts/ado-script/src/poll/ ` . Reuse anything in ` src/shared/ ` .
3304132 . Add a build script to ` package.json ` :
@@ -391,4 +474,6 @@ If a future bundle blows the budget:
391474## See also
392475
393476- [ Filter IR] ( /ado-aw/reference/filter-ir/ ) — the IR consumed by ` gate.js ` .
477+ - [ Runtime Imports] ( /ado-aw/reference/runtime-imports/ ) — author-facing marker syntax for ` import.js ` .
478+ - [ Execution Context] ( /ado-aw/reference/execution-context/ ) — user-facing configuration for ` exec-context-pr.js ` .
394479- [ Extending the Compiler] ( /ado-aw/guides/extending/ ) — generic compiler-extension guide.
0 commit comments