Skip to main content
Coming soon. Listening jobs are under active development. The listening schema below is accepted and validated today — you can author it and it will parse — but Shipfox does not execute listening jobs yet: a listening job currently acts as a scheduling boundary, and jobs that depend on it stay blocked. This page documents the authored shape and what it will do, so you can follow along; details may still evolve before release.
Today a workflow is a one-shot DAG: a trigger creates a run, every job runs exactly once in needs order, and the run ends when all jobs end. A listening job — a job driven by events — changes that: it listens for events and runs one execution per event batch, inside the same run, until a resolution condition is met. needs still gates when the job activates; from then on, events gate each execution.

What you’ll build with it

Two driving use cases shape the design: Agentic PR-review loop. Job A opens a pull request with an agent. Job B (needs: [A]) listens for new review comments and runs an agent on each batch — answering reviewers, applying requested changes — until the PR is closed or merged.
# Illustrative — listening jobs and the PR events they need are coming soon.
jobs:
  open_pr:
    steps:
      - model: claude-opus-4-8
        prompt: Implement the fix described in the run context and open a PR.
      - run: gh pr create --fill
  address_reviews:
    needs: [open_pr]
    listening:
      on:
        - source: github_acme
          event: pull_request_review_comment.created
      until:
        - source: github_acme
          event: pull_request.closed
      batch:
        debounce: "30s"
    steps:
      - model: claude-opus-4-8
        prompt: >
          New review comments arrived on the PR this run opened. Address each one:
          reply, or apply the requested change and push.
Issue-watcher loop. A job listens for new comments on an issue and keeps the issue or its spec document up to date — until the issue is closed.
# Illustrative — same caveat.
jobs:
  watch_issue:
    listening:
      on:
        - source: github_acme
          event: issue_comment.created
      until:
        - source: github_acme
          event: issues.closed
    steps:
      - model: claude-opus-4-8
        prompt: Fold the new issue comments into the spec and update the issue body.
These sketches assume PR-comment and issue events from the GitHub integration, which today delivers push — richer event ingestion ships alongside listening jobs.

The model

A run is a DAG of jobs; a job has one or more executions; an execution runs the steps. A one-shot job has exactly one execution. A listening job gets one execution per event batch:
  • needs gates the job — the listener activates only when its dependencies succeed.
  • on events gate each execution — every matching event (batch) starts one.
  • Executions serialize per job; events arriving during an execution coalesce into the next batch (tune with batch.debounce, batch.max_size, batch.max_wait).
  • The job resolves when an until event arrives, the timeout elapses, or max_executions is reached.

The listening shape

jobs:
  address_reviews:
    needs: [open_pr]
    listening:
      on:                      # required — events that start an execution
        - source: github_acme
          event: pull_request_review_comment.created
      until:                   # optional — events that resolve the job
        - source: github_acme
          event: pull_request.closed
      timeout: "24h"           # optional — max wall-clock listening time
      max_executions: 50       # optional — cap on executions
      batch:                   # optional — event coalescing
        debounce: "30s"
        max_size: 10
        max_wait: "5m"
      on_resolve: finish       # 'finish' (default) or 'cancel'
    steps:
      - run: ./react.sh
listening.on
trigger[]
required
The events the job listens for. Each entry uses the same {source, event, filter?, with?} shape as a workflow trigger — one event-selector shape, used at run level (triggers:) and at job level (on/until).
listening.until
trigger[]
Events that resolve the listening job.
listening.timeout
string
Maximum wall-clock time to keep listening before the job resolves.
listening.max_executions
number
Caps how many executions the job runs before it resolves.
listening.batch
object
Coalesces bursts of events: debounce (quiet window before an execution starts), max_size (events per batch), and max_wait (longest a batch waits before flushing).
listening.on_resolve
'finish' | 'cancel'
What happens when until fires: finish completes the job normally (default), cancel cancels it.

Triggers

The trigger shape reused by on and until.

Integrations

The sources that emit the events a listening job reacts to.