githubnext / githubnext/ado-aw

refactor(compile): split agentic-pipeline shape out of standalone_ir.rs; move Agent-job condition contributions into Declarations

Open
#987 0 comments 0 reactions 0 assignees View on GitHub
refactor
Dominant language
Rust
Stars
23
Forks
8
Avg merge
4d 9h
Merged PRs (30d)
22

Description

## Background

`src/compile/standalone_ir.rs` is ~2,260 lines and the file name is a lie. Only ~30 lines (`build_standalone_pipeline`) are actually standalone-specific. The other ~2,000 lines define the **canonical agentic pipeline shape** (Setup → Agent → Detection → SafeOutputs → Teardown) and are called by all four targets via `build_canonical_jobs` (a `pub(crate)` builder extracted in [`468359f6`](https://github.com/githubnext/ado-aw/commit/468359f6)).

The most visible symptom is `build_agentic_condition` (lines ~970–1080), which composes the Agent-job condition from signals owned by multiple extensions. It reaches across abstraction boundaries to:

| Reference | Real owner |
|---|---|
| `front_matter.is_synthetic_pr()` / `.pr_filters()` / `.pipeline_filters()` | `types.rs` |
| step ID `"synthPr"` + its declared outputs | `extensions/ado_script.rs` |
| step IDs `"prGate"` / `"pipelineGate"` + their `SHOULD_RUN` output | `filter_ir.rs` |

The typed-condition refactor in `660d2487` enforced those refs at compile time (graph validation rejects renames), but the *knowledge* of "which extensions feed the Agent-job condition and how" still lives in a compile target rather than in the extensions themselves.

The same shape concern applies to:

- `agent_job_variables_hoist` (lines ~899–930) — knows about the `synthPr` step ID and its hoisted outputs.
- `wire_explicit_dependencies` (lines ~1170–1210) — knows the canonical Setup → Agent → Detection → SafeOutputs edge list.
- Every `build_*_job` (Setup / Agent / Detection / SafeOutputs / Teardown) — defines the canonical 5-job shape.

## Why it matters

Every future cross-extension signal (e.g. a new extension that wants to gate the Agent job, or a different default Detection-job shape per target) ends up either:

1. Carving another `Condition::Custom` clause into `build_agentic_condition`, or
2. Adding a new flag to `BuiltPipelineContext` / `StandaloneCtx` and threading it through.

Both options add to the existing fan-in of `standalone_ir.rs` without giving extensions a place to own their own contributions to canonical-job conditions / variables / dependencies.

## Proposed direction

Two related refactors. They could land separately:

### 1. Rename + split `standalone_ir.rs`

Extract a new module — `src/compile/agentic_pipeline.rs` (or `canonical.rs`) — that owns:

- `BuiltPipelineContext` / `build_pipeline_context` / `build_canonical_jobs`
- All per-job builders (`build_setup_job`, `build_agent_job`, `build_detection_job`, `build_safe_outputs_job`, `build_teardown_job`)
- `build_agentic_condition`, `agent_job_variables_hoist`, `wire_explicit_dependencies`
- Helper step builders (`download_compiler_step`, `prepare_agent_prompt_step`, etc.)

`standalone_ir.rs` shrinks back to its ~30-line shape wrapper. `job_ir.rs` / `stage_ir.rs` / `onees_ir.rs` import from `agentic_pipeline` directly instead of through a `standalone_ir::` re-export.

This is a pure file move + import-path update — zero behaviour change.

### 2. Move condition contribution into `Declarations`

Today extensions contribute steps, env, hosts, MCPG entries, etc. — but not *conditions on the canonical jobs they implicitly extend*. Add a new field:

```rust
pub struct Declarations {
...
/// Clauses to AND into the canonical Agent job''s `condition:`.
/// The compiler folds these into a single `Condition::And` before
/// emitting. Lets an extension gate the Agent job declaratively
/// without the agentic-pipeline builder hard-coding knowledge of
/// each extension''s signals.
pub agent_conditions: Vec,

// Possibly also:
// pub detection_conditions: Vec,
// pub safe_outputs_conditions: Vec,
}
```

Then:

- `AdoScriptExtension::declarations()` emits:
- The synth-PR-skip clause (when `front_matter.is_synthetic_pr()`)
- The "real PR or synth-PR requires gate-passed" clause (when `pr_filters` are active)
- The "ResourceTrigger requires pipeline-gate-passed" clause (when `pipeline_filters` are active)
- User `expression:` escape hatches in front matter become a typed `Condition::Custom` contribution from a small new code path (currently wired in `build_agentic_condition`).
- `build_agentic_condition` reduces to `Condition::And(decls.iter().flat_map(|d| d.agent_conditions).collect())`.

After this, the agentic-pipeline builder no longer needs to know which extensions exist or what their step IDs are. New extensions can gate the Agent job without touching `standalone_ir.rs`.

## Out of scope (for now)

- Same treatment for variable-hoist (`agent_job_variables_hoist`). Today only `AdoScriptExtension` contributes; the threshold for adding a `Declarations::agent_variables` field is a second consumer.
- Custom per-job shapes (e.g. an extension that wants to insert a fifth canonical job). The current 5-job shape is hard-coded by design — the Three-Stage Pipeline Model is the security contract and shouldn''t be extensible.

## Acceptance

- `src/compile/standalone_ir.rs` is back to ~30 lines (only the standalone-specific shape wrapper).
- New `src/compile/agentic_pipeline.rs` (or `canonical.rs`) owns the 5-job canonical shape.
- `Declarations` carries `agent_conditions: Vec` (and the canonical-jobs builder folds them).
- `build_agentic_condition` deleted; all condition contributions come from extensions.
- Zero lock-file drift across all 33 fixtures.
- All existing tests pass; new tests cover the new `Declarations` field and the per-extension condition contributions.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with src/compile/standalone_ir.rs, build_canonical_jobs, the per-job builders, and Declarations to map the current condition and dependency flow. Extract the canonical shape into src/compile/agentic_pipeline.rs or canonical.rs, add agent_conditions contributions, and run the existing tests across all 33 fixtures; done means no lock-file drift and all tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
build-system, compilers
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.