Skip to content

feat: support reusable cross-repository custom safe-output jobs #1473

Description

@krubenok

Submission requirements

  • I generated this issue with an agent that used .github/agents/ado-aw.agent.md.
  • I reviewed the generated issue and confirm it is being filed directly in githubnext/ado-aw.

Problem summary

Workflow authors need to publish custom, secret-bearing safe-output integrations once in a shared repository and consume them from many ado-aw workflows.

A typical author wants an agent to propose a typed operation, have Detection inspect that proposal, and let a separate privileged job perform the side effect with credentials the agent never receives. Today this requires either adding a built-in Rust safe output to ado-aw or hand-authoring raw pipeline steps. Neither option provides a reusable, typed, cross-repository extension point with the existing Agent → Detection → approval → SafeOutputs trust boundary.

Upstream github/gh-aw provides precedent through safe-outputs.jobs, safe-outputs.scripts, and safe-outputs.actions. Its custom jobs generate agent-callable MCP tools from typed input schemas, consume the agent-output artifact only after threat detection, isolate job secrets from the agent, support shared component imports, and encourage immutable source pinning. Azure DevOps should adopt the security properties, but not mechanically copy GitHub Actions concepts that lack an ADO equivalent.

Current ado-aw limitations include:

  • Safe-output MCP routes, proposal/result types, executor dispatch, budgets, and validation are statically compiled into Rust.
  • Unknown safe-outputs: keys fail compilation, so consumers cannot register an external tool schema.
  • Raw setup:, steps:, post-steps:, and teardown: are reusable only by copy/paste and do not become gated agent-callable tools.
  • repos: can emit type, name, and ref, but cannot currently emit the repository resource endpoint required for type: githubenterprise.
  • target: job and target: stage omit top-level repository resources, so the parent pipeline must declare and authorize shared repositories.
  • Audit understands built-in proposal and execution records, but has no external executor contract or provenance record.

Personas

  • A platform maintainer publishes reviewed safe-output integrations with schemas, execution steps, policy, and documentation in one shared repository.
  • A workflow author imports a pinned component, supplies non-secret configuration, and enables the generated tool without copying executor code.
  • A pipeline administrator binds service connections, variable groups, or secret variables in the consumer pipeline while keeping those values unavailable to Agent and Detection.
  • A reviewer/auditor can identify exactly which component revision ran, what was proposed, whether Detection and approval cleared it, and what the executor reported.

Reproduction details

A neutral shared component might look like:

---
import-schema:
  destination:
    type: string
    required: true

safe-outputs:
  jobs:
    send-notification:
      description: Send a structured notification to the configured destination.
      max: 3
      inputs:
        title:
          type: string
          required: true
          max-length: 120
        body:
          type: string
          required: true
          max-length: 4000
        severity:
          type: choice
          options: [info, warning, critical]
          required: true
      env:
        NOTIFICATION_DESTINATION: ${{ ado.aw.import-inputs.destination }}
        NOTIFICATION_CREDENTIAL: $(SHARED_NOTIFICATION_CREDENTIAL)
      steps:
        - template: ado-aw/safe-outputs/send-notification.steps.yml@sharedSafeOutputs
          parameters:
            proposalFile: $(ADO_AW_SAFE_OUTPUT_PROPOSALS)
            resultFile: $(ADO_AW_SAFE_OUTPUT_RESULTS)
            staged: $(ADO_AW_SAFE_OUTPUTS_STAGED)
---

A consumer should be able to import an immutable revision and opt into approval:

---
name: Release observer
description: Analyze a release and propose notifications when operator attention is needed.

imports:
  - uses: platform/shared-agentic-components/ado-aw/send-notification.md@0123456789abcdef0123456789abcdef01234567
    repository:
      type: githubenterprise
      endpoint: shared-components-connection
    with:
      destination: release-operations

safe-outputs:
  send-notification:
    require-approval:
      approvers: [release-reviewers]
      on-timeout: reject
---

Analyze the release evidence. Propose `send-notification` only when a person should act.

The exact syntax is open for design, but compilation should fail unless the remote source resolves to an immutable full commit SHA and the imported manifest is valid. For target: standalone and target: 1es, ado-aw can emit the required repository resource, including endpoint. For target: job and target: stage, the generated template should declare a typed parameter/contract and a precise header diagnostic requiring the parent pipeline to define and authorize the pinned resource.

The desired execution path is:

  1. The compiler resolves and validates the pinned component manifest and merges its tool declaration without allowing silent name collisions.
  2. The SafeOutputs MCP server exposes a generated tool with a closed JSON Schema (additionalProperties: false).
  3. Agent writes the normal proposal NDJSON; it never receives executor secrets or the privileged repository/service-connection credential.
  4. Detection examines the same proposal artifact and fails closed on missing, malformed, or unsafe analysis.
  5. Optional require-approval uses the existing proposal-aware ManualReview behavior.
  6. A dedicated custom safe-output job downloads only the analyzed artifact, validates the selected proposals again, executes the pinned component steps, and emits the standard execution-record contract.
  7. Audit correlates proposal, detection, approval, component provenance, and per-item execution results.

Proposed next step

Implement an MVP limited to importable safe-outputs.jobs.

MVP behavior

  • Add a shared-component manifest format with import-schema plus safe-outputs.jobs.<name>.
  • Support typed inputs (string, number, boolean, and bounded choice initially), required fields, descriptions, positive max, and conservative size limits.
  • Generate an MCP tool schema with additionalProperties: false; reject invalid names, unsupported schema constructs, duplicate normalized names, and collisions with built-in tools.
  • Resolve cross-repository imports at compile time, record source provenance, and require a full 40-character commit SHA. Never execute branch- or tag-floating component code.
  • Add endpoint to repository-resource types and lowering for GitHub/GitHub Enterprise service connections. Require the endpoint for repository types that need it.
  • Compile each custom safe-output job after Detection. If approval applies, use the existing automatic/reviewed partitioning and proposal-aware ManualReview gate.
  • Inject only compiler-owned paths and mode flags into the job, for example ADO_AW_SAFE_OUTPUT_PROPOSALS, ADO_AW_SAFE_OUTPUT_RESULTS, and ADO_AW_SAFE_OUTPUTS_STAGED. Do not expose Agent credentials or arbitrary Agent-job environment.
  • Require the executor to append one result record per attempted proposal using a versioned schema containing tool name, proposal index/id, status, sanitized message, optional data, component source SHA, and attempt metadata.
  • Treat missing/malformed result records, unknown proposal types, budget overflow, or executor failure as failed execution rather than success-shaped fallback.
  • Preserve existing per-tool max semantics. Failed attempts consume budget so retries cannot become an unbounded side-effect loop.
  • Default to no automatic retry for non-idempotent writes. A component may declare a small retry policy only with an explicit idempotency-key contract; retry transient failures only, with bounded exponential backoff.
  • Support staged/dry-run mode as a first-class compiler-provided contract. Staged mode must validate and report intended effects without performing network writes.
  • Extend the safe-output summary and ado-aw audit to show imported tool provenance, proposal counts, detection/approval state, execution status, and sanitized result data across automatic and reviewed jobs.
  • Include component source, resolved SHA, manifest digest, generated schema digest, and executor job identity in artifacts/telemetry. Never log secret values.
  • Verify downloaded/checked-out content against the resolved SHA before execution. Repository/service-connection authorization remains an explicit pipeline-administrator action; the compiler must not broaden access automatically.

Security and supply-chain invariants

  • Agent and Detection never receive custom executor secrets.
  • No custom executor runs before Detection returns safe.
  • Approval-gated proposals never execute before approval and fail closed on rejection/timeout.
  • Every executed component is immutable, provenance-recorded, and schema-validated.
  • Import merge semantics are deterministic: imports apply in declaration order for additive content, but duplicate tool names and secret/env collisions are hard errors; the consumer may configure a tool but may not replace imported executor steps.
  • Secrets may be referenced only from the privileged custom job through approved variable groups/service connections. Import inputs are non-secret and must not be interpolated into shell or YAML unsafely.
  • Raw component steps receive the same advisory task validation as current inline steps, plus strict checks for forbidden secret projection, unsupported checkout/resource mutation, unsafe template expressions, and unpinned external task/container dependencies where enforceable.
  • Apply explicit limits for imports, nesting, manifest size, tools per workflow, proposals per tool, input lengths, total proposal artifact size, job timeout, and result artifact size.

Scope

  • Cross-repository, immutable shared manifests.
  • Generated MCP tool schemas.
  • One isolated Azure Pipelines job per custom safe-output definition or an equivalent compiler-controlled job boundary.
  • Existing Detection, require-approval, safe-output summaries, execution artifacts, Conclusion, and audit integration.
  • Standalone, 1ES, job-template, and stage-template compilation with target-appropriate resource ownership.

Non-goals for the MVP

  • Running arbitrary remote code inside the Agent job.
  • Giving custom components direct access to the agent's read token, MCP gateway credential, or workspace by default.
  • Dynamic runtime imports or floating refs.
  • A general-purpose Azure Pipelines package manager.
  • In-process custom scripts, arbitrary task wrappers, marketplace publication, or automatic component updates.
  • Replacing deterministic post-run reporters; those summarize completed work and are distinct from agent-callable safe outputs that can create new side effects.

Acceptance criteria

  • A shared repository can publish a valid custom safe-output job manifest with typed agent inputs and non-secret import parameters.
  • A consumer can import it by full commit SHA and compile all four targets.
  • GitHub Enterprise repository resources support a validated endpoint; template targets clearly require the parent resource declaration.
  • The generated MCP tool appears only when imported/configured and rejects extra, missing, mistyped, or oversized inputs.
  • Name, schema, environment, and import conflicts fail deterministically at compile time.
  • Agent and Detection jobs contain none of the custom executor's secret bindings.
  • The custom job cannot run unless Detection is safe; reviewed tools additionally require successful manual approval.
  • Automatic and reviewed custom proposals preserve current split-execution behavior.
  • Staged mode performs no external write and produces an auditable planned result.
  • Per-tool limits, bounded timeout, result validation, and no-retry-by-default behavior are enforced.
  • Proposal, detection, approval, component provenance, execution records, Conclusion, and ado-aw audit correlate successfully.
  • Tests cover manifest parsing, cross-repo pin validation, endpoint lowering, schema generation, merge conflicts, graph ordering, secret isolation, detection rejection, approval rejection/timeout, staged mode, executor failure, malformed results, budgets, template-target resource guidance, and audit correlation.

Follow-up work may evaluate trusted in-process scripts for simple deterministic handlers, typed wrappers around pinned Azure DevOps tasks/templates, component discovery/update tooling, signed release metadata, policy allowlists, and a curated component catalog. Those should build on the job-based security and artifact contracts rather than weakening the job boundary.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions