Skip to content

fix(tracker): apply the workflow's on_failure transition when a dispatch fails - #52

Closed
impuls42 wants to merge 1 commit into
nullclaw:mainfrom
sengine-cloud:fix/tracker-failure-transition
Closed

impuls42 wants to merge 1 commit into
nullclaw:mainfrom
sengine-cloud:fix/tracker-failure-transition

Conversation

@impuls42

Copy link
Copy Markdown

What

When a tracker dispatch fails and there are no retries left, the task is now moved with the workflow's on_failure.transition_to trigger before the run is failed. If no trigger is configured, or the pipeline rejects it, the run is failed exactly as before, and the rejected case logs a warning.

Why

driveFailed only calls failRun. That ends the run but leaves the task in its current stage, which is still claimable. On the next poll the same task is claimed again, so a worker that fails every attempt produces an unbounded claim → dispatch → fail loop: a new run and a new workspace per tick, and the ticket never moves.

WorkflowDef.on_failure.transition_to already exists and defaults to "failed", but nothing read it on this path.

Validation

  • zig build is clean.
  • zig build test: 355/355 pass.
  • bash tests/test_e2e.sh: 57/57 pass.
  • Reproduced against a local nulltickets (current main) using a pipeline todo → failed (trigger failed), a dispatch workflow, a webhook worker that always returns 500, and poll_interval_ms: 1000. Results after 20 s:
    • before: 10 runs, task still in todo;
    • after: 1 run, task in failed.

Not in scope

There's no backoff and no dead-letter. A pipeline whose definition rejects the configured failure trigger still loops as it does today, but now logs failure transition '<trigger>' rejected for task <id>; task stays in stage <stage>, so the misconfiguration is visible.

🤖 Generated with Claude Code

…tch fails

driveFailed called failRun, which ends the run but leaves the task in its
current stage. A task still sitting in a claimable stage is claimed again on
the next poll, so a worker that fails every attempt produces an unbounded
claim/dispatch/fail loop: a new run and a new workspace per tick, and the
ticket never moves.

Reproduced against nulltickets with a worker whose dispatch always returns
500 and poll_interval_ms 1000: 10 runs in 20 seconds, task still in its
initial stage. After the change: one run, task in the configured failure
stage.

WorkflowDef already carries on_failure.transition_to and defaults it to
"failed"; it was never read on this path. Apply it, and fall back to failRun
when no transition is configured or the pipeline rejects the trigger, so the
run is closed out either way.

This does not add backoff or a dead letter. A pipeline that rejects the
configured trigger still loops, and that case now logs a warning naming the
trigger and the stage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant