feat(ir): validate front-matter task steps against typed builders#1096
Conversation
🔍 Rust PR ReviewSummary: Good design and clean implementation overall, but there's one real bug with integer-typed inputs and a silent data-loss risk in the public API surface that should be addressed before callers wire this up. Findings🐛 Bugs / Logic Issues
✅ What Looks Good
|
Add a parse direction to the typed task builders: parse_task_step() deserializes an authored ADO task-step mapping and validates it by reusing the builder structs as the schema. Derive Deserialize (keyed on ADO input names, deny_unknown_fields) on CopyFiles and the Docker@2 command variants, plus a flexible bool deserializer accepting both rue and "true". Partial coverage is safe and additive: parse_task_step returns Ok(Some(TaskStep)) for a recognized+valid step, Ok(None) for anything not modeled (unmapped task or non-task step) so the caller keeps the original YAML as today's opaque passthrough, and Err only for a recognized task with invalid inputs (missing required, unknown key, bad value, or an input for the wrong command). Wired up for CopyFiles@2 and Docker@2 as a proof of concept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
c30698d to
f1a095d
Compare
🔍 Rust PR ReviewSummary: Looks good overall — the three-outcome design is clean and the test suite is solid. Two issues worth addressing before merging. Findings🐛 Bugs / Logic Issues
|
Extend the typed-builder validation from a CopyFiles@2/Docker@2 proof of concept to all 46 built-in ADO task builders, and wire it into the compile path as advisory, warn-only validation that never fails a compile and never alters emitted YAML. validate_task_step (in tasks/parse.rs) reworked to return Option<Result<(),String>> (never an unrecoverable error); a VALIDATORS registry maps each ADO task id to a validator. Flat builders validate by serde deserialization (parse, don't validate); command/mode-dispatch tasks supply a bespoke validate_inputs that dispatches on their discriminator while preserving deny_unknown_fields. build_pipeline_context runs one pass over every user setup/steps/post_steps/teardown step (all four targets) and emits Warning: on the existing channel. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…minators Addresses two code-review findings in the task-step validation: 1. AzureCLI@2 was registered as a flat validate_by_deserialize, but its ScriptLocation field is an externally-tagged enum that only deserializes from a nested map, whereas ADO authors (and into_step) emit the flat 'scriptLocation: inlineScript' + sibling 'inlineScript:' shape — so every valid AzureCLI@2 step was flagged. Now validated via a dedicated flat schema (azure_cli::validate_inputs). 2. Docker@2/DotNetCoreCLI@2/Npm@1/NuGetCommand@2/UniversalPackages@1 treated an omitted 'command' as an error, but ADO defaults it (buildAndPush/build/install/restore/download). A missing command now selects the ADO default variant, consistent with the other dispatch tasks and the warn-only 'never flag what compiled before' contract. Adds AzureCLI round-trip + unknown-input tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move advisory task-step validation off the compile-time stderr channel and into the structured lint channel, so authoring agents can read per-step feedback (code + task id + which step list) through 'ado-aw lint' --json and the lint_workflow MCP tool — the surfaces an agent self-optimizing by extracting repeated actions into task steps actually consumes.
- parse.rs: add validate_front_matter_task_steps returning structured TaskStepFinding; keep validate_task_step as the per-step primitive (never errors).
- inspect::lint: add lint_front_matter_tasks emitting task-input-invalid Warning findings with location {job: <list>, step: <task id>}; build_lint merges them with the structural rules.
- Remove warn_on_invalid_task_steps from build_pipeline_context: compile no longer warns (it also re-runs in-pipeline for integrity, where the noise is unwanted) and never alters emitted YAML.
- Tests: expand round-trips to all 13 command/mode-dispatch builders + required-enum flat builders (guards enum tokens, would have caught the AzureCLI bug); add ado-aw lint integration test (warning, exit 0); update the compile test to assert silence.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Looks good overall — clean "parse, don't validate" design with a few small issues worth fixing. Findings🐛 Bugs / Logic Issues
|
- Integer-authored ADO string inputs (retryCount, delayBetweenRetries, socketTimeout, retryCountOnDownloadFails) no longer false-positive: add de_opt_str_or_int (accepts a bare YAML number and stringifies, mirroring how ADO coerces) and apply it in copy_files/download_secure_file/use_node. Authors naturally write these as integers. - azure_cli: drop the dead/misleading Deserialize derive from AzureCli and ScriptLocation. AzureCLI@2 validates via the bespoke flat AzureCliInputs schema (registered as validate_inputs), so the externally-tagged enum form never matched the ADO flat shape; document that ScriptLocation is construction-only. - invalid-task-input-agent.md fixture docstring corrected: compile stays silent; findings surface via ado-aw lint, not a compile warning. - VALIDATORS: document the O(n) lookup + when to switch to binary search. - tests: integer-authored retryCount validates OK; Docker inputs: ~ (null) defaults gracefully; add trailing newline to inspect_integration.rs. Obsolete review points (targeted the pre-rewrite parse_task_step/parse_docker): validate_task_step no longer returns a normalized TaskStep (nothing dropped) and is now wired into the lint channel. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed the automated Rust PR Review feedback (latest reviews) in 52d3783: Bugs fixed
Suggestions applied
Obsolete points (targeted the pre-rewrite
|
🔍 Rust PR ReviewSummary: Solid implementation with good test coverage — one case-sensitivity false-positive risk and one maintenance hazard in the two-pass validators worth fixing before merge. Findings
|
…ft guards - de_opt_bool_flex now accepts string booleans case-insensitively (True/False/TRUE). serde_yaml (YAML 1.2) parses capitalized scalars as strings, not bools, so CleanTargetFolder: True reached the string arm and was wrongly rejected — a false-positive for a step ADO accepts. - VSTest@2 / PublishBuildArtifacts@1 two-pass validators: add drift-guard tests that set every common input and assert validation passes across all selectors/locations. If a common field is added to the *CommonInputs struct but forgotten in remove_common_inputs, the leftover key would trip the variant's deny_unknown_fields and the test now fails. - Also apply de_opt_str_or_int to PublishBuildArtifacts FilePath ParallelCount (integer-authored numeric string input), consistent with the earlier retryCount fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed the latest Rust PR Review suggestions in 3819f4b:
|
🔍 Rust PR ReviewSummary: Well-designed feature with one moderate concern and a couple of minor nits — no blocking issues. Findings
|
…view nits - create-ado-agentic-workflow.md: add a 'Lint authored task steps' step and a best-practice note. Since the compiler passes raw task: steps through unchecked and task-input validation is lint-only, agents must run 'ado-aw lint --json' (or the lint_workflow MCP tool) and fix task-input-invalid findings — the compiler never surfaces them. Emphasises reading the JSON findings rather than the (always-0) exit code. Address the latest Rust PR Review suggestions (documentation-only, no behaviour change): - lint.rs: document the LintLocation convention for task-input-invalid findings (stage=None, job=step-list name, step=task id) so graph-correlating consumers branch on code. - common.rs: note that de_opt_str_or_int accepts a bare f64, a deliberate false-negative consistent with preferring gaps over noise. - parse.rs: comment why the task re-extraction unwrap_or_default() cannot fail. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Latest Rust PR Review (no blocking issues) addressed in 0246921 — all three were documentation clarifications, applied as such (no behaviour change):
Separately, added lint guidance to
|
🔍 Rust PR ReviewSummary: Looks good — well-structured approach with one concrete coverage gap and two minor design observations worth addressing. Findings
|
…w nits - Add roundtrip_azure_powershell_file_dispatch — the file (FilePath) mode was untested, leaving ScriptPath/ScriptArguments unguarded against serde-rename / into_step drift; completes the all-dispatch-builders round-trip claim. - azure_powershell.rs: document the AzurePowerShellVersion::Preferred empty-string sentinel and why the placeholder never reaches emitted YAML. - vstest.rs: document why VsTestCommonInputs deliberately omits deny_unknown_fields (validate_common runs on the full map, which still holds selector-specific inputs; typos are still named by the selector variant's deny_unknown_fields). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Latest review addressed in 07b8dcc:
|
🔍 Rust PR ReviewSummary: Solid implementation — the parse-don't-validate approach is idiomatic and the round-trip test suite is an excellent guard against the primary failure mode. Two design-level concerns worth noting; no outright bugs found. Findings
|
Integrates the NodeTool@0 typed builder with the front-matter task-step advisory validation added in #1096: the builder now derives Deserialize keyed on ADO input names (deny_unknown_fields), and is registered in parse.rs VALIDATORS. Internal representation flattened from a VersionSource enum to the ADO versionSpec/versionSource/versionFilePath inputs so it doubles as the serde schema, matching the UseNode@1 convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for NodeTool@0 Adds NodeTool (the legacy Node.js tool installer) to the typed IR task catalog. The builder supports both version-spec mode (NodeTool::new) and version-file mode (NodeTool::from_file) matching the two versionSource values. All optional inputs are typed setters; bool inputs use bool_input. NodeTool@0 appears 7 times in the safe-outputs test lock files and is therefore a high-signal addition to the typed task coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire NodeTool@0 builder into task-step validation Integrates the NodeTool@0 typed builder with the front-matter task-step advisory validation added in #1096: the builder now derives Deserialize keyed on ADO input names (deny_unknown_fields), and is registered in parse.rs VALIDATORS. Internal representation flattened from a VersionSource enum to the ADO versionSpec/versionSource/versionFilePath inputs so it doubles as the serde schema, matching the UseNode@1 convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
Integrates the SonarQubePublish@8 typed builder with the front-matter task-step advisory validation added in #1096: derives Deserialize keyed on the ADO pollingTimeoutSec input (deny_unknown_fields, flex str/int) and registers it in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for SonarQubePublish@8 Add SonarQubePublish builder struct to complete the SonarQube pipeline trio (SonarQubePrepare → SonarQubeAnalyze → SonarQubePublish). The task has a single input (pollingTimeoutSec, ADO default 300) so new() requires no arguments; polling_timeout_sec() is provided as an optional setter for callers who need a non-default timeout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire SonarQubePublish@8 builder into task-step validation Integrates the SonarQubePublish@8 typed builder with the front-matter task-step advisory validation added in #1096: derives Deserialize keyed on the ADO pollingTimeoutSec input (deny_unknown_fields, flex str/int) and registers it in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
Integrates the SonarQubeAnalyze@8 typed builder with the front-matter task-step advisory validation added in #1096: the builder and its JdkVersion enum derive Deserialize keyed on ADO input/token names (deny_unknown_fields), and it is registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for SonarQubeAnalyze@8
Add a typed builder struct for the `SonarQubeAnalyze@8` ADO task —
the second step in the SonarQube three-task workflow
(Prepare → Analyze → Publish).
The task has one optional input (`jdkversion`) with a typed enum
(`JdkVersion::{JavaHome,JavaHome17X64,JavaHome21X64}`) so callers can
express the JDK source explicitly rather than relying on a raw string.
Omitting the setter leaves the input unset, letting the ADO server use
its default (`JAVA_HOME_17_X64`).
- `src/compile/ir/tasks/sonar_qube_analyze.rs`: new `SonarQubeAnalyze`
builder, `JdkVersion` enum, `Default` impl, 8 unit tests
- `src/compile/ir/tasks/mod.rs`: `pub mod sonar_qube_analyze;` (alphabetical)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): wire SonarQubeAnalyze@8 builder into task-step validation
Integrates the SonarQubeAnalyze@8 typed builder with the front-matter task-step advisory validation added in #1096: the builder and its JdkVersion enum derive Deserialize keyed on ADO input/token names (deny_unknown_fields), and it is registered in parse.rs VALIDATORS.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: James Devine <devinejames@microsoft.com>
Integrates the SonarQubePrepare@8 typed builder with the front-matter task-step advisory validation added in #1096. Because this is a mode-dispatch task (scannerMode dotnet/cli/other, with a configMode file/manual sub-mode under cli), it supplies a bespoke validate_inputs that dispatches on the discriminators and validates each mode against a deny_unknown_fields schema, so an input supplied for the wrong mode is reported. Registered in parse.rs VALIDATORS. Mode tokens and defaults (scannerMode=dotnet, configMode=file) and the msBuildVersion/cliVersion aliases match the SonarSource v8 task.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for SonarQubePrepare@8 Add SonarQubePrepare builder in src/compile/ir/tasks/sonar_qube_prepare.rs. SonarQubePrepare@8 is a mode-dispatch task: scannerMode selects between dotnet, cli (file or manual sub-mode), and other (Maven/Gradle). Each mode carries its own typed data so invalid mode/input combinations are unrepresentable at compile time. Constructors: - SonarQubePrepare::dotnet(endpoint, DotNetMode::new(project_key)) - SonarQubePrepare::cli(endpoint, CliMode::File(CliFileMode::new())) - SonarQubePrepare::cli(endpoint, CliMode::Manual(CliManualMode::new(key))) - SonarQubePrepare::other(endpoint) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire SonarQubePrepare@8 builder into task-step validation Integrates the SonarQubePrepare@8 typed builder with the front-matter task-step advisory validation added in #1096. Because this is a mode-dispatch task (scannerMode dotnet/cli/other, with a configMode file/manual sub-mode under cli), it supplies a bespoke validate_inputs that dispatches on the discriminators and validates each mode against a deny_unknown_fields schema, so an input supplied for the wrong mode is reported. Registered in parse.rs VALIDATORS. Mode tokens and defaults (scannerMode=dotnet, configMode=file) and the msBuildVersion/cliVersion aliases match the SonarSource v8 task.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
Integrates the AzureFunctionApp@2 typed builder with the front-matter task-step advisory validation added in #1096: the builder and its FunctionAppType / FunctionDeploymentMethod enums derive Deserialize keyed on ADO input/token names (deny_unknown_fields, flex bool), and it is registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for AzureFunctionApp@2 Introduces `AzureFunctionApp` builder struct for the `AzureFunctionApp@2` ADO task in `src/compile/ir/tasks/azure_function_app.rs`. - `FunctionAppType` enum: `Windows` / `Linux` (maps to `functionApp` / `functionAppLinux` ADO tokens) - `FunctionDeploymentMethod` enum: `Auto` / `ZipDeploy` / `RunFromPackage` - Builder struct with four required positional params (`azure_subscription`, `app_type`, `app_name`, `package`) and optional typed setters for `flex_consumption`, `deploy_to_slot_or_ase`, `resource_group_name`, `slot_name`, `runtime_stack`, `app_settings`, `deployment_method`, and `with_display_name` - Eight unit tests covering required inputs, enum round-trips, slot deployment, runtime stack, flex consumption flag, app settings, and display-name override - `pub mod azure_function_app` declaration added to `tasks/mod.rs` in alphabetical order Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire AzureFunctionApp@2 builder into task-step validation Integrates the AzureFunctionApp@2 typed builder with the front-matter task-step advisory validation added in #1096: the builder and its FunctionAppType / FunctionDeploymentMethod enums derive Deserialize keyed on ADO input/token names (deny_unknown_fields, flex bool), and it is registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
Integrates the BicepDeploy@0 typed builder with the front-matter task-step advisory validation added in #1096. BicepDeploy dispatches on two orthogonal discriminators (scope and type), so it supplies a bespoke validate_inputs: scope-specific required inputs are checked and removed, then the remainder is validated against a per-type deny_unknown_fields schema, reporting inputs used for the wrong scope or type. The token enums derive Deserialize; defaults (type=deployment, scope=resourceGroup) and the full input set (including description/bicepVersion/maskedOutputs/whatIfExcludeChangeTypes) match the BicepDeployV0 task.json. Registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for BicepDeploy@0 Introduce a typed builder struct for BicepDeploy@0 in src/compile/ir/tasks/bicep_deploy.rs. The builder models the command/mode-dispatch shape of the task: - BicepDeploymentType enum (Deployment | DeploymentStack) selects the ADO 'type' input and carries the per-type optional inputs. - BicepScope enum (ResourceGroup | Subscription | ManagementGroup | Tenant) carries the scope-specific required inputs so only valid input combinations are representable. - Typed enums for all constrained inputs: BicepOperation, BicepStackOperation, BicepUnmanageAction, BicepDenySettingsMode, BicepEnvironment, BicepValidationLevel. - Convenience constructors deploy_to_resource_group() and deploy_to_subscription() for the two most common cases. - 10 unit tests covering all four scopes, both deployment types, operation variants, and optional-input emission. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire BicepDeploy@0 builder into task-step validation Integrates the BicepDeploy@0 typed builder with the front-matter task-step advisory validation added in #1096. BicepDeploy dispatches on two orthogonal discriminators (scope and type), so it supplies a bespoke validate_inputs: scope-specific required inputs are checked and removed, then the remainder is validated against a per-type deny_unknown_fields schema, reporting inputs used for the wrong scope or type. The token enums derive Deserialize; defaults (type=deployment, scope=resourceGroup) and the full input set (including description/bicepVersion/maskedOutputs/whatIfExcludeChangeTypes) match the BicepDeployV0 task.json. Registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
…p validation Integrates the AzureResourceManagerTemplateDeployment@3 typed builder with the front-matter task-step advisory validation added in #1096. The task dispatches on deploymentScope and (for Resource Group) on action, so it supplies a bespoke validate_inputs: scope/action-specific required inputs are checked and removed, then the remainder is validated against a deny_unknown_fields template-deploy schema (empty for DeleteRG), reporting inputs used for the wrong scope/action. ArmDeploymentMode derives Deserialize; defaults (scope=Resource Group, action=Create Or Update Resource Group) and input set match the AzureResourceManagerTemplateDeploymentV3 task.json. Registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t@3 (#1232) * feat(ir): add typed builder for AzureResourceManagerTemplateDeployment@3 Add a command-dispatch typed builder struct for `AzureResourceManagerTemplateDeployment@3`, covering all four deployment scopes (Resource Group, Subscription, Management Group, Tenant) and the Resource Group delete action. - `ArmTemplateDeployment`: main builder wrapping `ArmDeploymentCommand` - `ArmDeploymentCommand`: enum with five variants, each carrying a scope-specific struct so invalid scope/input combos are unrepresentable - `ArmDeploymentMode`: typed enum for `Incremental` / `Complete` / `Validation` deployment modes - `ArmTemplateSource`: typed enum for `Linked artifact` vs `URL of the file` with a shared `with_parameters_file` setter - Per-scope structs (`ResourceGroupDeploy`, `ResourceGroupDelete`, `SubscriptionDeploy`, `ManagementGroupDeploy`, `TenantDeploy`) with typed optional setters for shared deploy options - 7 unit tests covering all variants, both template sources, and optional inputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire AzureResourceManagerTemplateDeployment@3 into task-step validation Integrates the AzureResourceManagerTemplateDeployment@3 typed builder with the front-matter task-step advisory validation added in #1096. The task dispatches on deploymentScope and (for Resource Group) on action, so it supplies a bespoke validate_inputs: scope/action-specific required inputs are checked and removed, then the remainder is validated against a deny_unknown_fields template-deploy schema (empty for DeleteRG), reporting inputs used for the wrong scope/action. ArmDeploymentMode derives Deserialize; defaults (scope=Resource Group, action=Create Or Update Resource Group) and input set match the AzureResourceManagerTemplateDeploymentV3 task.json. Registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
Integrates the KubernetesManifest@1 typed builder with the front-matter task-step advisory validation added in #1096. KubernetesManifest dispatches on action (deploy/bake/createSecret/delete/patch/promote/scale/reject), so it supplies a bespoke validate_inputs: the shared connection/namespace inputs are validated and removed first, then the per-action inputs are validated against a deny_unknown_fields schema, reporting inputs used for the wrong action. All eight token enums derive Deserialize; numeric-ish inputs accept string or integer forms. Registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(ir): add typed builder for KubernetesManifest@1 Add a typed builder struct for `KubernetesManifest@1` to the ado-aw IR. This is a command-dispatch task with 8 action variants (deploy, bake, createSecret, delete, patch, promote, scale, reject). Each action carries its own per-action inputs; shared connection and namespace inputs live on the KubernetesManifest builder itself. - src/compile/ir/tasks/kubernetes_manifest.rs: new command-dispatch builder with ConnectionType, DeploymentStrategy, TrafficSplitMethod, RenderType, ResourceKind, MergeStrategy, SecretType, PatchTarget enums plus per-action structs and 14 unit tests - src/compile/ir/tasks/mod.rs: pub mod kubernetes_manifest declaration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ir): wire KubernetesManifest@1 builder into task-step validation Integrates the KubernetesManifest@1 typed builder with the front-matter task-step advisory validation added in #1096. KubernetesManifest dispatches on action (deploy/bake/createSecret/delete/patch/promote/scale/reject), so it supplies a bespoke validate_inputs: the shared connection/namespace inputs are validated and removed first, then the per-action inputs are validated against a deny_unknown_fields schema, reporting inputs used for the wrong action. All eight token enums derive Deserialize; numeric-ish inputs accept string or integer forms. Registered in parse.rs VALIDATORS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: James Devine <devinejames@microsoft.com>
Summary
Adds advisory validation of authored front-matter task steps, built on the typed task builders in
src/compile/ir/tasks/. The builders are used in reverse as a schema (parse, don't validate): each derivesserde::Deserializekeyed on its real ADO input names withdeny_unknown_fields, so a clean deserialization is the validation and a serde error is the diagnostic.The validation is warning-only — it never fails anything and never alters emitted YAML.
Motivation: feedback for self-optimizing agents
This is primarily for agents that self-optimize by extracting repeated actions into reusable ADO task steps. Such an agent needs a tight, structured correctness signal on the steps it synthesises. So the findings are surfaced through the lint channel the agent already consumes —
ado-aw lint(--json) and thelint_workflowMCP tool — not as compile-time stderr noise.How it works
validate_task_step(&Value) -> Option<Result<(), String>>(intasks/parse.rs) looks the step'stask:up in aVALIDATORSregistry.None= unmodeled task / non-task step (passed through untouched);Some(Ok)= valid;Some(Err)= recognized task with invalid inputs. It never returns an unrecoverable error.validate_front_matter_task_steps(...)runs it over the four authored step lists (setup/steps/post-steps/teardown), returning structuredTaskStepFindings.inspect::lint::lint_front_matter_tasksmaps each to atask-input-invalidWarningLintFindingwithlocation { job: <list>, step: <task id> };build_lintmerges these with the existing structural rules soado-aw lint/lint_workflowreport them. Warnings do not faillint(exit 0).Coverage
All 46 built-in task builders:
validate_by_deserialize::<T>— field renames mirror the exact ADO input keys (into_step()is the source of truth), enum inputs deserialize from theiras_ado_str()tokens, bool inputs accepttrueand"true"(de_opt_bool_flex), and integer-authored string inputs (e.g.retryCount: 3) accept a bare number (de_opt_str_or_int) to match how ADO coerces them.Docker@2,DotNetCoreCLI@2,NuGetCommand@2,Npm@1,UniversalPackages@1,PowerShell@2,AzurePowerShell@5,PythonScript@0,VSTest@2,GitHubRelease@1,AzureFileCopy@6,PublishBuildArtifacts@1,JavaToolInstaller@0,AzureCLI@2) supply a bespokevalidate_inputsthat pops the discriminator — defaulting it to the ADO default variant when omitted — and deserializes the rest into the matching variant struct. Explicit per-variant dispatch (not a serde tagged enum) preservesdeny_unknown_fields.Adding a task = derive
Deserializeon the builder + oneVALIDATORSline.Why lint, not compile
compilealso re-runs in-pipeline (the Stage-1 integrity recompile); a stderr warning there is noise the agent never reads.lint/lint_workflow, so consolidating there closes the self-optimization loop.Avoiding false positives
Because the audience is an automated producer, a false positive is worse than a gap (it trains the agent to ignore warnings). Guards:
assert_roundtrips):into_step()output is fed back throughvalidate_task_stepand must re-validate, for all 13 command/mode-dispatch builders plus required-enum flat builders. This catches serde-rename /as_ado_str()drift — the exact class of bug theAzureCLI@2fix addressed.None-means-unvalidated: unmodeled tasks and non-task steps are never flagged, so coverage is strictly additive.Test plan
cargo build,cargo clippy --all-targets --all-features,cargo test— all green.tasks::parse: valid/invalid cases, integer-authored inputs, null (inputs: ~) dispatch, no-duplicate-task-ids registry check, and the round-trip suite.inspect::lint: unit tests for thetask-input-invalidrule (warning + location) and the no-false-positive case.inspect_integration:ado-aw lint --jsonon an invalid-input fixture reportstask-input-invalidatwarningseverity and exits 0.compiler_tests: the invalid-input fixture compiles successfully, emits the step verbatim, andcompilestays silent (validation belongs to lint).Review feedback addressed
retryCount/delayBetweenRetries/socketTimeout/retryCountOnDownloadFails) no longer false-positive.Deserializederive fromAzureCli/ScriptLocation(validated via the flatAzureCliInputsschema instead).VALIDATORSO(n) lookup, and added a trailing newline toinspect_integration.rs.