Skip to content

feat(tasks): thread scheduled task titles onto GET /api/tasks/runs list runs#764

Open
sweetmantech wants to merge 1 commit into
mainfrom
feat/task-runs-titles
Open

feat(tasks): thread scheduled task titles onto GET /api/tasks/runs list runs#764
sweetmantech wants to merge 1 commit into
mainfrom
feat/task-runs-titles

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What

List mode of GET /api/tasks/runs now annotates each run with title: string | null — the originating scheduled task's scheduled_actions.title, null when unresolvable. Retrieve mode (runId) and every existing field are unchanged; the addition is purely additive.

Why

Trigger.dev list runs carry only taskIdentifier, and every scheduled prompt shares customer-prompt-task — so the chat homepage renders five identical "Scheduled Task" rows (recoupable/chat#1850, video-parity item 1, promoted from the chat#1853 fast-follow note).

How runs link to schedules (mechanism)

Trigger.dev's v1 list-runs rows expose no schedule reference (verified against the live API; the schedule object exists only on single-run retrieve). The join is the reverse lookup the repo already uses in enrichTasks: for each of the account's scheduled_actions with a trigger_schedule_id, fetch its runs via filter[schedule] and map run id → title. Verified live: schedule-filtered run ids intersect the account-tag list exactly (run_cmr91tpc0… appears in both for sched_d4o3wm3xskug35izn59ej / "Weekly Social Media Health Check Report").

Per-schedule fetch uses the caller's limit, which is sufficient coverage: a schedule can contribute at most limit runs to the top-limit account list.

Implementation

  • lib/tasks/buildRunTitleMap.ts — run id → title map via per-schedule fetchTriggerRuns({ "filter[schedule]": … }); fails open per schedule
  • lib/tasks/attachRunTitles.tsselectScheduledActions({ account_id }) (existing lib/supabase function) → map → annotate; fails open to all-null titles so the runs list never 500s on title resolution
  • lib/tasks/getTaskRunHandler.ts — list branch pipes runs through attachRunTitles

One exported function per file; TDD RED→GREEN per unit (new modules failed collection + handler assertion failed before implementation).

Verification

  • Full suite: 3,950 passed / 715 files (baseline 3,940 + 10 new: 5 buildRunTitleMap, 4 attachRunTitles, 1 handler)
  • pnpm exec tsc --noEmit: 200 errors = exact pre-existing baseline, 0 new (measured by stash/compare)
  • pnpm lint clean, prettier clean
  • Live mechanism probe against Trigger.dev v1 + prod scheduled_actions (above)
  • Preview: unauthed probe pending deploy; authed list-mode pass (titles on real runs) pending a bearer — will comment results on this PR

Links

🤖 Generated with Claude Code


Summary by cubic

GET /api/tasks/runs list mode now includes a title field per run, set to the originating scheduled task’s title or null if it can’t be resolved. This makes scheduled runs distinguishable in the chat UI; retrieve mode and existing fields are unchanged.

  • New Features
    • Add title: string|null to list runs by mapping run IDs to account schedules via Trigger.dev’s schedule filter; failures return null titles so the list never breaks.
    • New attachRunTitles and buildRunTitleMap utilities; list handler pipes runs through title annotation using the request limit for coverage.

Written for commit 8c05e16. Summary will update on new commits.

Review in cubic

…st runs

Trigger.dev list runs carry only taskIdentifier (every scheduled prompt
shares customer-prompt-task), so chat's homepage rendered identical
"Scheduled Task" rows. List mode now annotates each run with the
originating scheduled_actions.title, resolved by matching run ids
against each schedule's runs (filter[schedule] on trigger_schedule_id —
list runs expose no schedule reference, verified against the live v1
API). title is null when a run cannot be mapped; retrieve mode and all
existing fields are unchanged.

Fails open at both layers: a failed Trigger.dev schedule fetch skips
that schedule's titles, a failed scheduled_actions lookup yields null
titles — the runs list never breaks.

Contract: recoupable/docs#268. Tracking: recoupable/chat#1850
(video-parity item 1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
api Ready Ready Preview Jul 7, 2026 11:05pm

Request Review

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b6b63ff-9160-4bf7-ad37-f4a5ed5f5dbf

📥 Commits

Reviewing files that changed from the base of the PR and between 9a6cd0e and 8c05e16.

⛔ Files ignored due to path filters (3)
  • lib/tasks/__tests__/attachRunTitles.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/tasks/__tests__/buildRunTitleMap.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
  • lib/tasks/__tests__/getTaskRunHandler.test.ts is excluded by !**/*.test.*, !**/__tests__/** and included by lib/**
📒 Files selected for processing (3)
  • lib/tasks/attachRunTitles.ts
  • lib/tasks/buildRunTitleMap.ts
  • lib/tasks/getTaskRunHandler.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/task-runs-titles

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files

Confidence score: 3/5

  • In lib/tasks/getTaskRunHandler.ts, list-mode now does work proportional to all scheduled tasks because attachRunTitles fetches every schedule and then calls Trigger.dev per schedule, so large accounts can see slow responses, higher backend load, or request timeouts even when clients ask for a small run limit — cap/batch title enrichment to the requested run limit (or otherwise bound per-request fan-out) before merging.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/tasks/getTaskRunHandler.ts">

<violation number="1" location="lib/tasks/getTaskRunHandler.ts:32">
P2: List-mode requests now scale with the account's total scheduled task count, not the requested run limit, because `attachRunTitles` fetches every scheduled action and then one Trigger.dev run list per schedule. Consider bounding or batching this lookup so a large account cannot make `GET /api/tasks/runs` slow or trip Trigger.dev rate limits.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Client as Client (Next.js frontend)
    participant Handler as getTaskRunHandler
    participant Supabase as Supabase (scheduled_actions)
    participant Trigger as Trigger.dev API
    participant Attach as attachRunTitles
    participant BuildMap as buildRunTitleMap

    Client->>Handler: GET /api/tasks/runs?mode=list&accountId=acc_123&limit=20
    Handler->>Handler: validate query (existing)
    Note over Handler,Trigger: Existing: Fetch account-tagged runs
    Handler->>Trigger: fetchTriggerRuns({filter[tag]: "account:acc_123"}, 20)
    Trigger-->>Handler: runs[] (e.g., 2 runs)
    Note over Handler,Attach: NEW: Annotate runs with titles
    Handler->>Attach: attachRunTitles(runs, acc_123, 20)

    alt Empty runs list (short circuit)
        Attach->>Attach: return [] immediately
        Attach-->>Handler: []
    else Success path
        Attach->>Supabase: selectScheduledActions({account_id: acc_123})
        Supabase-->>Attach: actions[] (with trigger_schedule_id, title)
        Attach->>BuildMap: buildRunTitleMap(actions, 20)
        loop per action with trigger_schedule_id
            BuildMap->>Trigger: fetchTriggerRuns({filter[schedule]: sched_id}, 20)
            alt Fetch succeeded
                Trigger-->>BuildMap: scheduleRuns[]
                BuildMap->>BuildMap: map each run.id → action.title
            else Fetch failed
                BuildMap->>BuildMap: skip schedule (no entries)
            end
        end
        BuildMap-->>Attach: Map<runId, title>
        Attach->>Attach: runs.map(r => ({...r, title: map.get(r.id) ?? null}))
        Attach-->>Handler: runsWithTitles[]
    else Title resolution error (DB / Trigger.dev outage)
        Attach->>Attach: log error, return runs with title: null
        Attach-->>Handler: runsWithTitles[] (all null)
    end

    Handler-->>Client: 200 {status:"success", runs: runsWithTitles}
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

{ "filter[tag]": `account:${validatedQuery.accountId}` },
validatedQuery.limit,
);
const runsWithTitles = await attachRunTitles(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: List-mode requests now scale with the account's total scheduled task count, not the requested run limit, because attachRunTitles fetches every scheduled action and then one Trigger.dev run list per schedule. Consider bounding or batching this lookup so a large account cannot make GET /api/tasks/runs slow or trip Trigger.dev rate limits.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/tasks/getTaskRunHandler.ts, line 32:

<comment>List-mode requests now scale with the account's total scheduled task count, not the requested run limit, because `attachRunTitles` fetches every scheduled action and then one Trigger.dev run list per schedule. Consider bounding or batching this lookup so a large account cannot make `GET /api/tasks/runs` slow or trip Trigger.dev rate limits.</comment>

<file context>
@@ -26,8 +29,13 @@ export async function getTaskRunHandler(request: NextRequest): Promise<NextRespo
         { "filter[tag]": `account:${validatedQuery.accountId}` },
         validatedQuery.limit,
       );
+      const runsWithTitles = await attachRunTitles(
+        runs,
+        validatedQuery.accountId,
</file context>

@sweetmantech

Copy link
Copy Markdown
Contributor Author

Preview verification — api-qogcgjdqy-recoup.vercel.app (head 8c05e164)

Verified on the preview (no bearer required):

  • GET /api/tasks/runs unauthenticated → 401 {"status":"error","error":"Exactly one of x-api-key or Authorization must be provided"} — auth-first, per contract
  • OPTIONS /api/tasks/runs200 (CORS preflight intact)
  • GET /api/tasks/runs?limit=0 unauthenticated → 401 (auth still precedes param validation; existing behavior unchanged)

Verified live against the real backing services (mechanism proof, done pre-implementation):

  • Trigger.dev v1 list-runs rows carry no schedule reference (raw response inspected on prod runs; schedule exists only on single-run retrieve) — confirming the reverse-lookup design
  • GET /api/v1/runs?filter[schedule]=sched_d4o3wm3xskug35izn59ej returns run_cmr91tpc0ic000on1wojxiyqv, which also appears in the filter[tag]=account:d7091248… list — the exact join this PR implements; that schedule's scheduled_actions.title is "Weekly Social Media Health Check Report", so that run will list with that title

Pending (stated plainly):

  • Authed list-mode pass on the preview (real runs carrying title) is blocked without a bearer: previews validate API keys against a different key salt than prod, and no Privy token is mintable headlessly here. Recommend the standard post-merge prod check: signed-in homepage run rows on chat.recoupable.dev once feat: homepage run rows render the real task title, generic label as fallback chat#1857 deploys (its fallback keeps rows rendering either way).

CI: test/lint/format all green; suite 3,950 passed; tsc at the exact 200-error pre-existing baseline (0 new).

🤖 Generated with Claude Code

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