Skip to content

Unify trigger retries onto the connection-delivery outbox (do not build a second mechanism) #17

Closed

Description

@serge-ivo

Re-scoped 2026-08-04

Migration 0058 gave connections an at-least-once delivery outbox: write-before-attempt, exponential backoff (~1m/5m/15m/1h/3h, max 5 attempts), dead-lettering, idempotency on (connection, emitting run, payload-hash), replay, and trace correlation. See lib/connection-deliveries.ts.

Triggers still have none. lib/triggers.ts contains no retry path at all — a failed cron/webhook trigger is simply lost.

That is worse than the original state of this ticket: the platform now has two failure routes with different reliability guarantees, which is a trap for anyone reasoning about whether their automation is durable. The answer is "it depends which edge fired," and nothing in the UI says which.

So this ticket is no longer "add a retry policy to triggers." It is: make triggers reuse the delivery outbox rather than growing a parallel mechanism with its own semantics, its own backoff table, and its own dead-letter concept.

Scope

  • Generalize the outbox from connection-delivery to a generic durable-attempt record (source = connection | trigger), or have trigger runs enqueue through the same table.
  • Triggers inherit backoff, max attempts, dead-lettering, idempotency, and replay unchanged.
  • runDueTriggers and runDueDeliveries converge onto one due-work sweep on the per-minute cron.
  • Cron triggers still advance next_run_at safely and never hot-loop on failure.
  • Notification on repeated failure (the part of the original scope that stands).

Acceptance

  • A failing cron/webhook trigger retries with the same policy a connection delivery gets.
  • Exhausted attempts leave one terminal, visible, replayable state — the same one connections use.
  • No duplicate imports/tasks on retry (idempotency carries over).
  • A user cannot observe different durability depending on whether work arrived via a trigger or a connection.
  • Tests cover success-after-retry, exhausted retries, no-hot-loop, and notification.

Console surface for this: #19 (triggers) and #182 (connections).


Original ticket

Summary

Add retry/backoff behavior and user-visible failure notifications for webhook, cron, and connector sync triggers.

Why

Trigger runs currently record success/failure and increment failure counts, but failed work requires the user to inspect events manually. Production automation needs controlled retries and clear notification when a trigger is broken.

Scope

  • Add retry policy fields to trigger config or table: max attempts; backoff strategy or next retry time; notify on first failure / repeated failures
  • Add retry event statuses to agent_trigger_events.
  • Avoid hot loops and duplicate imports/tasks.
  • Send notifications through existing platform notification/push surfaces where available.
  • Add console visibility for failure state and retry schedule.

Activity

  1. changed the title [-]Trigger retry policy, backoff, and failure notifications[/-] [+]Unify trigger retries onto the connection-delivery outbox (do not build a second mechanism)[/+] on Aug 3, 2026
  2. serge-ivo commented on Aug 4, 2026

    @serge-ivo
    ContributorAuthor

    Closed by a1946a6 (migration 0064).

    Triggers now reuse the connection outbox rather than growing a second mechanism, which was the re-scoped ask. dispatchTrigger enqueues on failure only — the happy path keeps its extra write, and the vast majority of dispatches succeed.

    attemptDelivery already built a synthetic trigger row, so the retry mechanics needed no change at all. What it lacked was labelling: a stuck trigger and a stuck connection are the same mechanism but not the same problem, so failures are now logged under the producing edge and are distinguishable in the trace.

    Migration 0064 adds source with a 'connection' default, so every existing row and caller is unchanged and there is no data migration. connection_id becomes the generic producing-edge id and keeps its name — renaming would touch every query and index for a cosmetic gain, and SQLite rewrites the table to do it.

    Per-trigger accounting (failure_count, last_error) is untouched: the outbox is additional durability, not a replacement for what the console already shows.

    4 tests, including one asserting the happy path writes nothing to the outbox.

  3. serge-ivo commented on Aug 4, 2026

    @serge-ivo
    ContributorAuthor

    Done in a1946a6 (migration 0064). Triggers reuse the connection outbox rather than growing a second mechanism; retry mechanics needed no change, only labelling so a stuck trigger is distinguishable from a stuck connection.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend / Worker / API workconnectorsConnector + tool framework

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions