Add conditional stages to TaskPipeline
What type of PR is this?
/kind api
What this PR does / why we need it:
Adds conditional stages to TaskPipeline, as discussed on #1761: rather than
a new TaskRouter CRD, routing becomes a pipeline whose first stage decides
and whose later stages are gated on that decision.
- name: incident
conditions:
- input: '{{index .Stages "route" 0 "Results" "response" | b64dec}}'
operator: In # In | NotIn
values: [incident]
spec.stages[].conditions(0–8, ANDed). Eachinputis a Go template
rendered with.Stages, trimmed, and compared exactly againstvalues.- Conditions are evaluated when the stage would otherwise start. A stage whose
conditions are not met isSkipped: it creates no Tasks, reportsTotal: 0,
shows up as an empty list under.Stages.<name>, and counts as complete for
the next stage and for pipeline success. The skip is recorded in status and
never re-evaluated. - A condition input that cannot be rendered fails the stage after the existing
output-capture grace period. It never silently skips. - Pipeline templates gain
b64dec, since the agent's free-form answer only
reaches templates as the base64responseresult.
This is the first of three stacked PRs:
- This PR: conditional stages.
- TaskSpawner
pipelineTemplate, so a Slack message can start a TaskPipeline. - Propagate the initiator's Slack reporting onto stage Tasks, so results return to the thread.
Which issue(s) this PR is related to:
Part of #1760 (replaces the approach in #1761).
Special notes for your reviewer:
- Ordering is unchanged. Stages still run one at a time in declaration
order. Mutually exclusive branches therefore run only the selected stage.
Non-exclusive branches serialize; running stages as a DAG would be a
separate change. Skippedis a stage-only phase. It is aTaskPhasevalue used only in
PipelineStageStatus.phase, which has no enum marker, so the schema is
unchanged. Tasks never report it.- Values are a closed set. An agent answer can only select a stage whose
condition lists that value, so a prompt-injected request costs at most the
wrong branch on the operator's own menu.docs/reference.mdsays this. - Why a list, not a single condition.
conditionsis a list from the
start, because changing a single field to a list later would break existing
manifests.
Testing:
make verifyandmake testare clean.- New unit tests cover:
- the routed happy path (
Inselects one branch, the others areSkipped, and the pipeline succeeds); - the
NotInfallback; - ANDed conditions;
- a skip staying final after results change;
- render failure after the grace period;
- a skipped leading stage;
- template validation;
b64decerrors.
- the routed happy path (
- New integration specs cover operator, values and input validation, plus a
dry-run of the new example. - A new e2e case routes on a real agent reply.
Does this PR introduce a user-facing change?
TaskPipeline stages accept `conditions`, which gate a stage on earlier stages'
results using `In`/`NotIn` comparisons. A stage whose conditions are not met is
reported as `Skipped` and does not block later stages. Pipeline templates gain
a `b64dec` function for reading the base64-encoded `response` result.
🤖 Generated with Claude Code
Summary by cubic
Adds conditional stages to TaskPipeline, so one pipeline can route between branches at runtime based on earlier stages' results — replacing the TaskRouter CRD approach in #1761 as part of #1760.
A stage declares 0–8 conditions, each rendering a Go template input against .Stages and comparing the trimmed result against values with In or NotIn. A stage whose conditions aren't met is reported Skipped: it creates no Tasks, counts as complete for later stages and pipeline success, and is never re-evaluated. A condition input that cannot be rendered fails the stage after the existing output-capture grace period rather than silently skipping. Pipeline templates gain b64dec, since agent responses only reach templates as the base64 response result.
Behavior
- Stages still run one at a time in declaration order, so mutually exclusive branches run only the selected stage and non-exclusive branches serialize.
Skippedis aTaskPhasevalue used only in stage status, so the CRD schema is unchanged and Tasks never report it.- Condition values are a closed set, so a prompt-injected reply can only select a branch the operator listed.
Notes
- This is the first of three stacked PRs; the next adds TaskSpawner
pipelineTemplate, and the last propagates initiator reporting to stage Tasks. - New unit, integration, and e2e coverage;
make verifyandmake testpass.