Skip to content

Execution Context

The execution-context plugin is an always-on compiler extension that stages a focused set of per-run context signals on disk before the agent starts. For PR-triggered builds, it fetches the target branch (with a bearer token it keeps strictly in its own step) and resolves the merge-base SHA — the part the agent cannot safely do inside its own sandbox — then hands the agent the pre-populated SHAs and a tailored prompt fragment so the agent can run git diff, git log, and git show directly without re-fetching anything.

ADO’s default checkout: self is shallow (fetchDepth: 1), does not fetch the PR target branch, and does not persist the OAuth bearer into .git/config for later reuse. Every PR-reviewer agent has historically re-invented the same ~120 lines of bash to work around this.

The execution-context plugin owns that step centrally — but only the part the agent cannot do itself:

  • Fetches the PR target branch with progressive deepening until git merge-base resolves. Requires the bearer; cannot happen inside the AWF sandbox.
  • Writes base.sha and head.sha so the agent can reuse them across many git diff invocations without repeating the resolution.
  • Appends a prompt fragment listing ready-to-use git commands and ADO MCP tool calls (with PR id / project / repo pre-filled).

The agent does its own diff, show, log, and stat work. All git objects are already local once the precompute step finishes.

All contributors are scoped by a runtime ADO condition: so they run only on the matching trigger type — no unnecessary REST calls or git fetches fire on every build.

ContributorActivates whenDefaultOutput layout
pron.pr is configuredONaw-context/pr/
pr.checkspr contributor is active AND pr.checks.enabled: trueOFF (opt-in)aw-context/pr/checks/
workitempr contributor is activeONaw-context/workitem/
manualAny parameters: block is declaredONaw-context/manual/
pipelineon.pipeline is configuredONaw-context/pipeline/
ci-pushon push trigger AND ci-push.enabled: trueOFF (opt-in)aw-context/ci-push/
scheduleon.schedule is configured AND schedule.enabled: trueOFF (opt-in)aw-context/schedule/
reporepo.enabled: trueOFF (opt-in)aw-context/repo/
execution-context:
enabled: true # master switch; default: true
pr:
enabled: true # default: true when on.pr is configured
checks:
enabled: false # default: false; opt in to stage pr/checks/*.json
workitem:
enabled: true # default: true when pr contributor is active
max-items: 5 # cap on linked WIs staged (default: 5)
max-body-kb: 32 # cap per prose body field, in KB (default: 32)
manual:
enabled: true # default: true when any parameters: block is declared
include-email: false # whether to stage requestor email (default: false)
pipeline:
enabled: true # default: true when on.pipeline is configured
ci-push:
enabled: false # default: false; opt in for "since last green build" diff context
schedule:
enabled: false # default: false; opt in for "since last run" diff context
repo:
enabled: false # default: false; opt in for repo identity context
conventions: false # whether to probe CODEOWNERS/CONTRIBUTING.md/AGENTS.md (default: false)

All keys are optional. When the execution-context: block is omitted entirely, defaults activate per trigger — PR, workitem, manual, and pipeline contributors turn on automatically when their respective trigger is configured.

enabled (bool, default true) — master switch. When false, no contributor runs and no aw-context/ directory is staged.

pr.enabled (bool, default true when on.pr is configured) — whether to activate the PR contributor. Set false to opt out.

Setting pr.enabled: false also suppresses the automatic git commands in the agent’s bash allow-list (see Bash allow-list below).

pr.checks.enabled (bool, default false) — opt in to stage Build Validation run results under aw-context/pr/checks/. Requires the pr contributor to be active. Useful for remediation agents that need to identify which checks are failing before proposing a fix.

workitem.enabled (bool, default true when the pr contributor is active) — whether to fetch and stage linked work item details for each PR. Disable to skip WI fetching (e.g. when your pipeline’s WIs contain sensitive content you do not want in the agent’s prompt window).

workitem.max-items (integer, default 5) — maximum number of linked WIs staged. Additional WIs are listed in aw-context/workitem/truncated.txt (names only) so the agent knows they exist.

workitem.max-body-kb (integer, default 32) — maximum kilobytes per prose body field (description, acceptance criteria, repro steps). Bodies larger than the cap are truncated with a trailing [truncated] marker.

manual.enabled (bool, default true when any parameters: block is declared) — whether to stage manual-run context. Set false to opt out of requestor-identity and parameter-snapshot staging.

manual.include-email (bool, default false) — whether to include $(Build.RequestedForEmail) in the staged metadata and prompt fragment. Off by default as a hygiene posture — the address is already visible in the ADO build UI, and most agents do not need it.

pipeline.enabled (bool, default true when on.pipeline is configured) — whether to stage upstream-build metadata when the pipeline fires as a resource trigger. Set false to opt out.

ci-push.enabled (bool, default false) — opt in to stage “since last successful build on this branch” diff context for CI push builds (IndividualCI / BatchedCI build reasons). OFF by default because the REST lookup and git fetch deepening add startup latency most push-CI agents do not need.

schedule.enabled (bool, default false) — opt in to stage diff context for scheduled builds. Same REST-and-deepening cost as ci-push; OFF by default because many scheduled agents are operational (report generators, health checks) and do not need diff context.

repo.enabled (bool, default false) — opt in to stage repository identity context (current branch, SHA, last release tag, commits-since-tag). Pure git — no REST, no bearer. OFF by default to avoid prompt-clutter regression for agents that already receive sufficient repo identity from the PR or CI-push contributor.

repo.conventions (bool, default false) — when repo.enabled: true, also probe for common convention files (CODEOWNERS, .github/CODEOWNERS, CONTRIBUTING.md, .editorconfig, AGENTS.md) and stage aw-context/repo/conventions.json with presence flags and the first 50 lines of each file found.

Every active contributor stages files under $(Build.SourcesDirectory)/aw-context/ — the agent’s working directory. Each contributor occupies its own subdirectory and runs under a runtime condition: so unmatched trigger types produce no files.

aw-context/
pr/
base.sha # merge-base SHA (40-char hex, no trailing newline)
head.sha # PR head SHA (40-char hex, no trailing newline)
error.txt # present only on failure (one-line reason)

base.sha is the common ancestor of the PR head and the PR target branch — git merge-base in both the synthetic-merge-commit and progressive-deepening paths. This makes git diff $BASE..$HEAD produce the same change set regardless of how ADO checked out the workspace.

On failure, base.sha and head.sha are not written. The bundle appends a failure prompt fragment explaining the reason and reminding the agent to surface the failure via report_incomplete or fall back to the ADO API.

pr.checks — build validation run results

Section titled “pr.checks — build validation run results”
aw-context/
pr/
checks/
failing.json # Build Validation runs that did not succeed
succeeded.json # Build Validation runs that succeeded
error.txt # REST failure (only on error)

Requires pr.checks.enabled: true. Useful for remediation agents that need to identify which CI checks failed before proposing a fix.

aw-context/
workitem/
ids.txt # newline-delimited list of WI ids
<id>/
summary.json # id, type, title, state, area-path, assigned-to, tags
description.md # System.Description (HTML→plain text, untrusted sentinel)
acceptance.md # AcceptanceCriteria (untrusted sentinel)
repro.md # ReproSteps for Bug type (untrusted sentinel)
comments.json # discussion history (untrusted sentinels)
links.json # relations summary
attachments.json # attachment metadata (name, size, url); bytes NOT downloaded
truncated.txt # present when linked WI count exceeded max-items
errors.txt # per-id fetch failures (if any)
error.txt # present only on total failure

Prose body fields (description, acceptance criteria, repro, comments) are wrapped in <<<AW-UNTRUSTED: / AW-UNTRUSTED>>> sentinels — a visible signal to the agent (and to Stage-2 detection) that the content crosses a trust boundary and may contain arbitrary user-authored text.

The prompt fragment interpolates only short structured fields (id, title, type, state) as natural English. Prose stays in files.

aw-context/
manual/
requested-for # $(Build.RequestedFor) display name
requested-for-email # $(Build.RequestedForEmail) — only when include-email: true
parameters.json # snapshot of user-declared parameter values (pretty-printed JSON)

Activated on Build.Reason == Manual builds when any parameters: block is declared. The clearMemory auto-injected parameter is not included in parameters.json (it is not in front_matter.parameters). Parameter values come from user input at queue time and are sanitised by the bundle before any prompt interpolation.

pipeline — upstream-pipeline-completion context

Section titled “pipeline — upstream-pipeline-completion context”
aw-context/
pipeline/
upstream-build-id # numeric Build ID of the triggering upstream run
upstream-source-sha # Build.sourceVersion of the upstream
upstream-source-branch # Build.sourceBranch of the upstream
upstream-status # succeeded / failed / partiallySucceeded / canceled
upstream-definition # upstream pipeline definition name
upstream-artifacts.json # artifact INDEX (names + download URLs; bytes NOT downloaded)
error.txt # present only on failure

Activated on Build.Reason == ResourceTrigger builds when on.pipeline is configured. Bearer required for the ADO Build REST API lookup. The prompt fragment appends a ## Pipeline-completion context section.

aw-context/
ci-push/
current-sha # Build.SourceVersion
previous-sha # SHA of the last successful build on this branch
base.sha # git merge-base between previous and current
commits.txt # git log previous..current --oneline
changed-files.txt # git diff --name-status previous..current
error.txt # present on failure (no prior build, depth exhausted, REST error)

Activated on IndividualCI / BatchedCI builds when ci-push.enabled: true. Bearer required for both the ADO Build REST API lookup (finding the last successful build) and git fetch deepening. Adds the same 7 read-only git commands to the bash allow-list as the pr contributor.

Same artifact layout as ci-push, staged under aw-context/schedule/:

aw-context/
schedule/
current-sha
previous-sha
base.sha
commits.txt
changed-files.txt
error.txt

Activated on Build.Reason == Schedule builds when schedule.enabled: true. Bearer required. Adds git read commands to the bash allow-list.

aw-context/
repo/
branch # short branch name (refs/heads/ prefix stripped)
sha # Build.SourceVersion (current commit SHA)
last-release-tag # git describe --tags --abbrev=0 (empty when no tags exist)
commits-since-tag.txt # git log <tag>..HEAD --oneline (empty when no tag)
conventions.json # convention-file probe — only when conventions: true

Activated when repo.enabled: true on any build reason. Pure git — no REST, no bearer. Adds git, git log, git rev-parse, git describe to the bash allow-list.

conventions.json is a small JSON object with presence flags and the first 50 lines of each convention file found (CODEOWNERS, .github/CODEOWNERS, CONTRIBUTING.md, .editorconfig, AGENTS.md). Only staged when repo.conventions: true.

Short, well-structured values (PR id, project, repo, requestor name, upstream definition name) are interpolated directly into prompt fragments as natural English and as literal arguments in example ADO MCP tool calls. Files are reserved for long opaque values (40-char SHAs, JSON blobs) the agent reuses across many commands.

Several contributors automatically extend the agent’s bash allow-list with read-only git commands. The set contributed by each:

ContributorCommands added
prgit, git diff, git log, git show, git status, git rev-parse, git symbolic-ref
ci-pushsame 7 as pr
schedulesame 7 as pr
repogit, git log, git rev-parse, git describe
workitem, manual, pipeline, pr.checksnone
tools.bash settingBehaviour
bash: omitted (allow-all)Extension is a no-op — all commands are already permitted.
bash: ["..."] (explicit list)Each contributor’s commands are appended to your list.
pr.enabled: falseThe PR contributor’s 7 git commands are not added.

The step invokes node /tmp/ado-aw-scripts/ado-script/exec-context-pr.js with SYSTEM_ACCESSTOKEN plus the five SYSTEM_* / BUILD_* identifier env vars. The bundle:

  1. Reads ADO-injected env varsSystem.PullRequest.*, System.TeamProject, Build.Repository.Name. No manual ref discovery needed.

  2. Validates identifiers with strict allowlist regexes (PR_ID ⊆ digits; PROJECT/REPO ⊆ alphanumeric + ._-; PROJECT additionally allows space; PR_TARGET_BRANCH ⊆ alphanumeric + ._/-). Writes error.txt and the failure prompt fragment on failure.

  3. Detects the merge-commit shape. If HEAD has ≥ 3 tokens in git rev-list --parents HEAD (ADO’s synthetic merge commit for PR builds), uses HEAD^2 as the PR head and computes git merge-base HEAD^1 HEAD^2 — no target-branch fetch needed.

  4. Otherwise fetches the PR target branch with progressive deepening: --depth=200, 500, 2000, then --unshallow. After each fetch, attempts git merge-base origin/<target> HEAD and proceeds to the next depth if it cannot resolve yet.

  5. Writes base.sha and head.sha on success and appends the success prompt fragment to /tmp/awf-tools/agent-prompt.md.

  6. Appends a failure prompt fragment (writes error.txt) if identifier validation or merge-base resolution exhausts the depth budget.

The bundle exits 0 on every soft failure (the agent surfaces problems via the prompt). Only true infra failures (e.g. the workspace root is not writable) produce exit 1, propagated by set -euo pipefail as a hard pipeline failure.

The step is gated by condition: eq(variables['Build.Reason'], 'PullRequest'), so it is a no-op on manual or scheduled runs of a PR-triggered pipeline.

The previous implementation embedded ~190 lines of bash heredoc into the emitted YAML, with only end-to-end shellcheck for coverage. The TypeScript bundle gains:

  • Unit-test coverage — 32 vitest tests across validate.ts, git.ts, merge-base.ts, prompt.ts, plus 3 end-to-end smoke tests against a synthetic merge git repo.
  • Tighter trust boundary — the bearer lives only in the Node process’s env and is injected into spawned git children via GIT_CONFIG_* env vars (see Trust boundary below), never into the wrapping bash shell.
  • Smaller emitted YAMLpr.rs shrinks from ~320 lines to ~145; the emitted step body is 4 lines instead of ~190.

See ado-script for the bundle architecture and release asset layout.

The PR contributor must fetch the PR target branch but doing so requires an OAuth bearer. ado-aw preserves the Stage 1 read-only invariant:

MechanismDecision
Override checkout: self with persistCredentials: trueRejected. Writes the bearer into .git/config inside the workspace, which is then mounted into the AWF sandbox.
Override checkout: self with fetchDepth: 0Rejected. Unnecessary — the precompute fetches exactly the refs it needs.
In-step SYSTEM_ACCESSTOKEN + GIT_CONFIG_* bearer envAdopted. The token is mapped only into the exec-context-pr.js step’s env: block. The bundle’s git.ts::bearerEnv injects GIT_CONFIG_COUNT / GIT_CONFIG_KEY_0 / GIT_CONFIG_VALUE_0 into the spawned git child’s env only — never into the Node process’s global process.env, never via git -c on argv. After the Node process exits, the bearer is gone.

The compile-time test test_execution_context_pr_does_not_leak_system_accesstoken walks the generated YAML and asserts that SYSTEM_ACCESSTOKEN appears only in the precompute step’s env: block, never in the agent step’s.

If you have a steps: block that manually fetches the target branch and resolves merge-base: delete that block, ensure on.pr is configured, and read aw-context/pr/{base,head}.sha directly from the agent. The prompt fragment is appended automatically.

  • Non-self checkouts. v1 diffs only the self checkout. Additional repositories in repos: do not receive their own aw-context/ entries.
  • Workspace alias. When workspace: points to a non-self alias, aw-context/ is still relative to $(Build.SourcesDirectory) — the pipeline’s working directory, not the alias directory.
  • Ordering. The precompute step runs in the Agent job after the checkout: self step and after “Prepare agent prompt” (so it can append to the prompt file), but before the agent itself (so the agent sees the appended fragment).
  • Identifiers in the prompt, SHAs on disk. Short values (PR id, project, repo) are embedded in the prompt heredoc as natural English; long opaque 40-char SHAs go to disk where BASE=$(cat aw-context/pr/base.sha) is the natural shell idiom.
  • Extension: src/compile/extensions/exec_context/mod.rs (ExtensionPhase::Tool)
  • Contributor trait + enum: src/compile/extensions/exec_context/contributor.rs
  • PR contributor: src/compile/extensions/exec_context/pr.rs
  • PR checks contributor: src/compile/extensions/exec_context/pr_checks.rs
  • Work item contributor: src/compile/extensions/exec_context/workitem.rs
  • Manual contributor: src/compile/extensions/exec_context/manual.rs
  • Pipeline contributor: src/compile/extensions/exec_context/pipeline.rs
  • CI-push contributor: src/compile/extensions/exec_context/ci_push.rs
  • Schedule contributor: src/compile/extensions/exec_context/schedule.rs
  • Repo contributor: src/compile/extensions/exec_context/repo.rs
  • Front-matter types: ExecutionContextConfig and per-contributor *Config structs in src/compile/types.rs
  • Compile tests: tests/compiler_tests.rs (search test_execution_context_*)
  • Bash lint fixture: execution-context-agent.md