Single-step retry
Vetrix re-runs one failed step or one matrix cell without re-running the whole pipeline. The current release ships the backend foundation: the YAML schema, retry-policy validator, engine decision point, audit discriminators, and the fork MR guardrail. The end-to-end HTTP wiring, UI tile, and downstream DAG re-evaluation execution loop are not yet available; the engine-side contracts described below are stable and will be imported unchanged when those surfaces ship.
YAML syntax
steps:
- name: flaky-test
run: go test ./flaky/...
retry:
max_attempts: 3
on: [failure, timeout] # optional; defaults to [failure]
backoff_seconds: 30 # optional; defaults to 0 (immediate)
Parse-time guarantees (internal/automations/retry_parse_test.go):
max_attemptsmust be ≥ 1 and ≤ 100 (sanity cap).on:entries must be one offailureortimeout.backoff_secondsmust be ≥ 0.- Omitting the block entirely leaves legacy workflows unchanged.
Decision surface
internal/cicd/retry.go ships PlanRetry(req RetryRequest) RetryDecision:
type RetryRequest struct {
StepID uuid.UUID
CurrentAttempt int // retry_count on the row before this call
MaxAttempts int // retry_max on the row
Trigger RetryTrigger // failure | timeout
OnPolicy []RetryTrigger
IsForkPR bool
}
type RetryDecision struct {
Allowed bool
NextAttempt int
Reason string // retry_exhausted | retry_trigger_not_in_on_list
RetryableFromCheckpoint bool
}
Reason strings are the stable audit-trail discriminators — tests and downstream filters assert them verbatim.
Fork-MR guardrail
Before handing a RetryContext to the executor the engine calls
GuardForkPRRetry(req, wantsEnvSecrets). The function returns
ErrForkPRSecretMaterialisationBlocked when a fork-MR retry would
materialise environment-scoped secrets — retries must never elevate a
fork-MR's token scope; the invariant lives at the engine layer rather
than in a YAML opt-in.
Downstream re-evaluation helper
ShouldReexecute(prev, current map[string]string) is the pure input
comparator downstream jobs consume when deciding whether a needs:
edge has changed on retry. It returns true when at least one declared
output differs, covering adds / removes / value changes.
Not yet available
The following surfaces are planned but not part of the current release:
- HTTP endpoints:
POST /runs/{run_id}/jobs/{jid}/steps/{sid}/retryandPOST /runs/{run_id}/jobs/{jid}/retry?cell=<matrix_key>. - Engine wiring for workspace + upstream-output restore + retry-attempt
env var (
VETRIX_RETRY_ATTEMPT) injection into the executor context. - Frontend
Retrybutton +retry #Nchip on the run detail page. - Audit
retryevent writer.
The pure PlanRetry + GuardForkPRRetry + ShouldReexecute
ValidateRetryPolicysurface is the contract the HTTP handler and engine executor layer will import unchanged.