Skip to content

Commit 245c2d8

Browse files
docs(site): document exec-context-pr.js bundle in ado-script reference (#908)
Add the missing third ado-script bundle to the reference page. The exec-context-pr.js bundle has been shipping in ado-script.zip since it was introduced for execution-context PR support, but the ado-script.mdx docs only mentioned gate.js and import.js. Changes: - Update intro paragraph to list all three bundles - Add '## What exec-context-pr.js does' section with merge-base resolution strategy, trust-boundary notes, and env-var contract table - Update workspace layout tree to include exec-context-pr/ directory and exec-context-pr.js build output - Update release-workflow paragraph to list exec-context-pr.js - Rename 'Agent job' wiring section to reflect two consumers (import.js and exec-context-pr.js) - Expand 'What gets emitted' table with on.pr/exec-context column - Add exec-context-pr.js as a real working example in 'Add a new bundle' - Add Execution Context to See also Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9fdbeb5 commit 245c2d8

1 file changed

Lines changed: 114 additions & 29 deletions

File tree

site/src/content/docs/reference/ado-script.mdx

Lines changed: 114 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
---
22
title: 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

66
import { 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
6870
gh-aw's pattern (their `threat_detection.md` ships with the setup
6971
action 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

199265
The 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
202268
the `ado-script.zip` release asset. Pipelines download that asset at
203269
runtime by URL pinned to the compiler's `CARGO_PKG_VERSION`, verify
204270
its 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
243309
features, each emitted **into the job that actually consumes the
244310
bundle**:
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.
271338
3. **`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

278355
ADO jobs use **isolated VMs**`/tmp` is not shared between jobs.
279356
The `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

294374
The 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+
328411
1. Create `src/poll/index.ts` and supporting modules under
329412
`scripts/ado-script/src/poll/`. Reuse anything in `src/shared/`.
330413
2. 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

Comments
 (0)