feat(compile): runtime prompt loading via {{#runtime-import}} markers#625
feat(compile): runtime prompt loading via {{#runtime-import}} markers#625jamesadevine wants to merge 2 commits into
Conversation
🔍 Rust PR ReviewSummary: Has a critical cross-job VM isolation bug that will cause Findings🐛 Bugs / Logic Issues[CRITICAL]
Azure DevOps jobs run on separate, isolated VMs — - bash: |
set -eo pipefail
node '/tmp/ado-aw-scripts/ado-script/dist/import/index.js' /tmp/awf-tools/agent-prompt.md... The existing The automated tests don't catch this because they only verify that the compiled YAML contains the string [Minor] const expanded = original.replace(MARKER, (_whole, optional, rawPath) => {
if (!existsSync(absPath)) {
hadError ??= `file not found: ${rawPath}`;
return ""; // ← replaces required marker with empty string
}
// ...
});
// ...
if (hadError) { fail(hadError); }
writeFileSync(target, expanded, "utf8"); // ← already wrote above ← wait, no:Actually the
|
0116de3 to
8304c60
Compare
🔍 Rust PR ReviewSummary: Looks good — well-designed feature with strong test coverage. Three minor findings, none blocking. Findings
|
Adopts gh-aw's {{#runtime-import path}} marker model and the inlined-imports front-matter toggle. Agent prompt bodies (and the Stage-2 threat-analysis prompt) are now loaded at pipeline runtime by default; body edits no longer require ado-aw compile.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8304c60 to
97a2339
Compare
|
Thanks for the careful review — the critical cross-job VM isolation finding was spot on. Force-pushed the fix. What changed in this revision
Test pinning4 new placement tests in
On Net file shape
|
|
@copilot resolve the merge conflicts in this pull request |
…runtime-prompt # Conflicts: # docs/filter-ir.md Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com>
Resolved by merging |
Summary
Adopts gh-aw's
{{#runtime-import path}}marker model and theinlined-importsfront-matter toggle. Agent prompt bodies are now loaded at pipeline runtime by default, so edits to the markdown body no longer requireado-aw compile.Behaviour
inlined-imports: false(new default) — the agent body is loaded at pipeline runtime via a{{#runtime-import …}}marker resolved by a newimport.jsado-script bundle. The compiled YAML contains the marker (and the resolver step), not the body text.inlined-imports: true— legacy behaviour preserved bytes-for-bytes for the simple case; author-written{{#runtime-import shared/snippet.md}}markers are resolved at compile time so the body is fully self-contained.{{#runtime-import path}}(required) and{{#runtime-import? path}}(optional, skip-if-missing) work inside any agent's markdown body, with the same semantics as gh-aw.The Stage-2 threat-analysis prompt is not runtime-imported. It's a tooling-shipped template that's
include_str!'d into theado-awbinary and inlined into the emitted YAML at compile time, matching gh-aw's pattern (theirthreat_detection.mdships with the setup action and is read directly from disk — no marker, no resolver).Consolidated single extension (addresses reviewer feedback)
One always-on
AdoScriptExtensionowns all ado-script wiring. It exposes two features through the existing trait hooks — no new template markers, noScriptAssetsregistry:setup_steps()prepare_steps()ADO jobs use isolated VMs —
/tmpis not shared. The bundle is therefore downloaded once per consuming job. When both features are active, install + download steps appear in both Setup and Agent. That's correct architecture given ADO's topology, not waste.filters:inlined-importstruefalsetruefalseImplementation
src/compile/extensions/ado_script.rs(~470 lines). One internal helperinstall_and_download_steps()produces the install+download YAML; bothsetup_steps()andprepare_steps()call it. The Rust source has one place for the install/download YAML; the emitted YAML carries it once per consuming job.resolve_imports_inline()(compile-time resolver forinlined-imports: true) lives in the same module.src/compile/extensions/trigger_filters.rs,src/compile/extensions/runtime_prompt.rs,src/compile/script_assets.rs, thescript_assets: ScriptAssetsfield onCompileContextand its four constructor sites, the prepend block atcommon.rs:2123-2131, and the{{ agent_prompt_resolver_steps }}template marker (all four base templates).compile_sharednow detects whenextra_replacementsalready binds{{ setup_job }}(the 1ES path) and skips its own redundantgenerate_setup_jobinvocation. Each extension'ssetup_steps()is now invoked exactly once per pipeline.NodeTool@0displayName updated from"Install Node.js 20.x for gate evaluator"to"Install Node.js 20.x"(the bundle now serves both gate.js and import.js).docs/ado-script.md(per-job download model),docs/runtime-imports.md,docs/filter-ir.md,docs/template-markers.md,AGENTS.md.Test plan
Per-job placement tests (pin the reviewer-found bug)
4 new tests in
tests/compiler_tests.rsthat split the emitted YAML by job block and assert exactly which job(s) containDownload ado-aw scripts:test_gate_only_pipeline_downloads_bundle_in_setup_job_not_agenttest_imports_only_pipeline_downloads_bundle_in_agent_job_not_setuptest_both_features_active_downloads_bundle_in_both_jobstest_neither_feature_active_emits_no_node_or_download_anywhereWithout the consolidation, tests (2) and (3) fail with
"Agent job is missing the script bundle download"— exactly the cross-job VM isolation bug the reviewer caught.Automated
cargo buildclean.cargo test --bin ado-aw: 1595 unit tests pass.cargo test --tests: 108 compiler_tests + every integration suite green.cargo clippy --all-targets --all-featuresclean (only pre-existing warnings).cd scripts/ado-script && npm test: 199 tests across 26 files.cd scripts/ado-script && npm run test:smoke: 3/3 pass.cd scripts/ado-script && npm run typecheckclean.Manual
Hand-inspected emitted YAML for the four (gate × imports) combinations:
All four match the design.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com