From 58e0d624c61466577f8c8d35e131297ce189dee0 Mon Sep 17 00:00:00 2001 From: James Devine Date: Tue, 12 May 2026 16:40:26 +0100 Subject: [PATCH 1/3] feat(compile): add target: job and target: stage for ADO template output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two new compile targets that produce reusable ADO YAML templates for embedding agentic stages into existing pipelines: - target: job — generates a job-level template (jobs: at root) that can be included in a flat pipeline or inside a user-defined stage - target: stage — generates a stage-level template (stages: wrapping jobs) for direct inclusion in multi-stage pipelines Key design decisions: - Pool is baked in from front matter (not a template parameter) - dependsOn and condition are set natively at the ADO call site - Job names are prefixed with PascalCase agent name for uniqueness (e.g., DailyReview_Agent, DailyReview_Detection, DailyReview_Execution) - Triggers (on:) are ignored with a warning in template targets - Template parameters only include clearMemory and user-defined params New files: - src/compile/job.rs — JobCompiler implementing the Compiler trait - src/compile/stage.rs — StageCompiler implementing the Compiler trait - src/data/job-base.yml — job-level template derived from base.yml - src/data/stage-base.yml — stage-level template wrapping jobs in stage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/compile/common.rs | 16 +++++++++++----- src/compile/job.rs | 15 +++++++-------- src/compile/stage.rs | 14 +++++++------- tests/bash_lint_tests.rs | 1 + 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/compile/common.rs b/src/compile/common.rs index dea2090..001f82b 100644 --- a/src/compile/common.rs +++ b/src/compile/common.rs @@ -2861,11 +2861,11 @@ pub async fn compile_shared( /// /// Handles the full setup — collecting extensions, building the compile context, /// generating the stage prefix and template parameters, computing AWF/MCPG -/// values — and delegates to [`compile_shared`]. The caller supplies: +/// values — and delegates to [`compile_shared`]. The caller supplies: /// /// - `cfg`: target-specific settings (template string, integrity / debug flags). /// - `header_fn`: a function that generates the leading comment block prepended -/// to the compiled YAML. The two template compilers use different header +/// to the compiled YAML. The two template compilers use different header /// layouts, so this lets each compiler keep its own generator while sharing /// all of the boilerplate setup. /// @@ -2927,9 +2927,15 @@ pub async fn compile_template_target( }; let yaml = compile_shared( - input_path, output_path, front_matter, markdown_body, - &extensions, &ctx, config, - ).await?; + input_path, + output_path, + front_matter, + markdown_body, + &extensions, + &ctx, + config, + ) + .await?; let header = header_fn(input_path, output_path, front_matter); Ok(format!("{}{}", header, yaml)) diff --git a/src/compile/job.rs b/src/compile/job.rs index a19f1de..f40f31c 100644 --- a/src/compile/job.rs +++ b/src/compile/job.rs @@ -9,13 +9,12 @@ use anyhow::Result; use async_trait::async_trait; -use log::warn; +use log::{info, warn}; use std::path::Path; use super::Compiler; use super::common::{ - compile_template_target, TemplateTargetConfig, - generate_header_comment, + compile_template_target, generate_header_comment, TemplateTargetConfig, }; use super::types::FrontMatter; @@ -37,6 +36,8 @@ impl Compiler for JobCompiler { skip_integrity: bool, debug_pipeline: bool, ) -> Result { + info!("Compiling for job template target"); + if front_matter.on_config.is_some() { warn!("on: trigger configuration is ignored for target: job (triggers are the parent pipeline's concern)"); } @@ -52,16 +53,15 @@ impl Compiler for JobCompiler { debug_pipeline, }, generate_job_header, - ).await + ) + .await } } /// Generate the header comment block for job-level templates. fn generate_job_header(input_path: &Path, output_path: &Path, front_matter: &FrontMatter) -> String { let base_header = generate_header_comment(input_path); - let mut lock_path = output_path - .to_string_lossy() - .replace('\\', "/"); + let mut lock_path = output_path.to_string_lossy().replace('\\', "/"); // Strip redundant leading "./" (same normalization as generate_header_comment) while lock_path.starts_with("./") { lock_path = lock_path[2..].to_string(); @@ -102,7 +102,6 @@ fn generate_job_header(input_path: &Path, output_path: &Path, front_matter: &Fro #[cfg(test)] mod tests { - use super::*; use crate::compile::common::generate_stage_prefix; #[test] diff --git a/src/compile/stage.rs b/src/compile/stage.rs index fda341e..7dfc417 100644 --- a/src/compile/stage.rs +++ b/src/compile/stage.rs @@ -17,13 +17,12 @@ use anyhow::Result; use async_trait::async_trait; -use log::warn; +use log::{info, warn}; use std::path::Path; use super::Compiler; use super::common::{ - compile_template_target, TemplateTargetConfig, - generate_header_comment, + compile_template_target, generate_header_comment, TemplateTargetConfig, }; use super::types::FrontMatter; @@ -45,6 +44,8 @@ impl Compiler for StageCompiler { skip_integrity: bool, debug_pipeline: bool, ) -> Result { + info!("Compiling for stage template target"); + if front_matter.on_config.is_some() { warn!("on: trigger configuration is ignored for target: stage (triggers are the parent pipeline's concern)"); } @@ -60,16 +61,15 @@ impl Compiler for StageCompiler { debug_pipeline, }, generate_stage_header, - ).await + ) + .await } } /// Generate the header comment block for stage-level templates. fn generate_stage_header(input_path: &Path, output_path: &Path, front_matter: &FrontMatter) -> String { let base_header = generate_header_comment(input_path); - let mut lock_path = output_path - .to_string_lossy() - .replace('\\', "/"); + let mut lock_path = output_path.to_string_lossy().replace('\\', "/"); while lock_path.starts_with("./") { lock_path = lock_path[2..].to_string(); } diff --git a/tests/bash_lint_tests.rs b/tests/bash_lint_tests.rs index d97104c..ed00445 100644 --- a/tests/bash_lint_tests.rs +++ b/tests/bash_lint_tests.rs @@ -180,6 +180,7 @@ fn compile_fixture_with_flags(workspace: &Path, fixture: &str, extra_flags: &[&s } else if stdout.contains("Generated job template:") { "job" } else if stdout.contains("Generated stage template:") { + "stage" } else { panic!( From 48131ccdddec1de32240135b9a410ef0668de44f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 13:49:53 +0000 Subject: [PATCH 2/3] fix(compile): implement review suggestions for job/stage template targets Agent-Logs-Url: https://github.com/githubnext/ado-aw/sessions/fa2ca612-1703-4107-87c3-2b2ec35429b8 Co-authored-by: jamesadevine <4742697+jamesadevine@users.noreply.github.com> --- src/compile/job.rs | 4 +--- src/compile/stage.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/compile/job.rs b/src/compile/job.rs index f40f31c..6174488 100644 --- a/src/compile/job.rs +++ b/src/compile/job.rs @@ -9,7 +9,7 @@ use anyhow::Result; use async_trait::async_trait; -use log::{info, warn}; +use log::warn; use std::path::Path; use super::Compiler; @@ -36,8 +36,6 @@ impl Compiler for JobCompiler { skip_integrity: bool, debug_pipeline: bool, ) -> Result { - info!("Compiling for job template target"); - if front_matter.on_config.is_some() { warn!("on: trigger configuration is ignored for target: job (triggers are the parent pipeline's concern)"); } diff --git a/src/compile/stage.rs b/src/compile/stage.rs index 7dfc417..ba59a3a 100644 --- a/src/compile/stage.rs +++ b/src/compile/stage.rs @@ -17,7 +17,7 @@ use anyhow::Result; use async_trait::async_trait; -use log::{info, warn}; +use log::warn; use std::path::Path; use super::Compiler; @@ -44,8 +44,6 @@ impl Compiler for StageCompiler { skip_integrity: bool, debug_pipeline: bool, ) -> Result { - info!("Compiling for stage template target"); - if front_matter.on_config.is_some() { warn!("on: trigger configuration is ignored for target: stage (triggers are the parent pipeline's concern)"); } From ab2ad71cbe52a1259cc619117aa584304928fd8f Mon Sep 17 00:00:00 2001 From: James Devine Date: Wed, 13 May 2026 15:44:18 +0100 Subject: [PATCH 3/3] fix(compile): use 'template' label for job/stage targets and fix header paths - CLI now prints 'Generated job template:' and 'Generated stage template:' instead of 'Generated job pipeline:' / 'Generated stage pipeline:' since these targets produce reusable ADO templates, not standalone pipelines. - Header comments in generated job/stage templates now reference the actual output path instead of deriving a path from the input file, fixing incorrect inclusion examples (e.g. showing ./agents/x.lock.yml when the output was at ./x.lock.yml). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/bash_lint_tests.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/bash_lint_tests.rs b/tests/bash_lint_tests.rs index ed00445..d97104c 100644 --- a/tests/bash_lint_tests.rs +++ b/tests/bash_lint_tests.rs @@ -180,7 +180,6 @@ fn compile_fixture_with_flags(workspace: &Path, fixture: &str, extra_flags: &[&s } else if stdout.contains("Generated job template:") { "job" } else if stdout.contains("Generated stage template:") { - "stage" } else { panic!(