Skip to content

feat(api): GET /runs — list runs filtered by status + initial_input fields #76

@viktor-shcherb

Description

@viktor-shcherb

Goal

Let an agent discover the right run_id for a user's natural-language request like "add Stripe to jobseek" without needing the user to manually paste a run_id.

Why

Companion to murmur#75. The end-user workflow on jobseek will be:

  1. User clicks "request company" on jseek.co, types a name + URL.
  2. Backend calls POST /pipelines/jobseek-add-company/runs, stores the run_id in a UI card, and shows the user a prompt they can copy to their own Claude session.
  3. (This issue) The user's Claude session resolves the run_id from a company name by calling list_runs (or its HTTP equivalent), then proceeds with pull_task({run_id}).

Without a list endpoint, the agent has no way to find pending runs.

Scope

  1. New publisher route GET /runs mounted alongside the existing GET /runs/{id}.
  2. Query params (all optional):
    • status — one of running, completed (?status=running is the demo-load-bearing case).
    • pipeline_id — exact match (e.g. jobseek-add-company).
    • initial_input — JSON-string fragment matched against runs.initial_input_json via JSON_EXTRACT. For the demo we need at least company-name search; spec a single field path syntax (e.g. ?initial_input.company_name=Stripe) or accept a raw JSON object body and match keys.
  3. Pagination: hard cap at 100, limit + offset query params. No total count needed for the demo.
  4. Response shape: { ok: true, data: { runs: [{run_id, pipeline_id, status, initial_input, created_at, webhook_status}] } }.

Interfaces

GET /runs?status=running&pipeline_id=jobseek-add-company&initial_input.company_name=Stripe&limit=10
 {
  ok: true,
  data: {
    runs: [
      { run_id, pipeline_id, status, initial_input, created_at, webhook_status }
    ]
  }
}

Verification

  • ?status=running returns only runs whose status == running, ordered by created_at DESC.
  • ?pipeline_id=jobseek-add-company&status=running AND-combines the filters.
  • ?initial_input.company_name=Stripe matches runs whose initial_input_json has that field equal to that value (case-insensitive optional).
  • ?limit=5&offset=10 paginates.
  • Bearer auth required (uses existing middleware).
  • Unauthed → 401, schema validation errors → 400, transport errors → 5xx.
  • Listing returns {runs: []} when no rows match (NOT 404).

Definition of done

  • Tests above all green.
  • pnpm typecheck && pnpm lint && pnpm test && pnpm grep:all clean.
  • PR opened, reviewer APPROVE.
  • docs/contracts.md describes the route shape.

Out of scope

  • Mutation endpoints (PATCH /runs/{id} to abort/retry — separate issue post-demo).
  • Listing runs the calling user "owns" (post-demo authz issue).
  • Full-text search on the entire initial_input blob — only specific field-path equality for the demo.

Blocked by

None. Independent of murmur#75.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions