githubnext / githubnext/ado-aw
refactor(compile): split agentic-pipeline shape out of standalone_ir.rs; move Agent-job condition contributions into Declarations
- 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