feat(tasks): annotate GET /api/tasks/runs list runs with schedule title + email subject

SW
2mo ago
+415-2

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.ts — selectScheduledActions({ 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

  • Tracking: recoupable/chat#1850 (Open — video/experience parity, item 1)
  • Contract (merge first): recoupable/docs#268
  • Consumer (merge last): chat PR feat/home-run-titles (renders run.title in HomeRunRow with today's label as fallback)
  • Merge order: docs#268 → this → chat

🤖 Generated with Claude Code


Summary by cubic

GET /api/tasks/runs list mode now returns two optional naming fields per run: title from the originating scheduled task and email_subject from the newest linked email. Previously names were not available; retrieve mode and all existing fields are unchanged. This makes chat rows distinguishable and prefers email_subject over title for display.

  • Resolves title: string|null by reverse-looking up each account scheduled_actions via Trigger.dev filter[schedule]; failures or unmapped runs yield null. Implemented with attachRunTitles + buildRunTitleMap.
  • Adds email_subject: string|null by querying the newest email_send_log row per trigger_run_id; lookup errors yield null. Implemented with attachRunEmailSubjects + selectEmailSubjectsByRunIds.
  • List handler now pipes runs through titles → email subjects; retrieve mode untouched. Types for email_send_log include subject and trigger_run_id.