Skip to content

feat: handle ffmpeg-edit output in pollContentRuns#139

Open
recoup-coding-agent wants to merge 1 commit intomainfrom
feature/rec-68-poll-edit-output
Open

feat: handle ffmpeg-edit output in pollContentRuns#139
recoup-coding-agent wants to merge 1 commit intomainfrom
feature/rec-68-poll-edit-output

Conversation

@recoup-coding-agent
Copy link
Copy Markdown
Collaborator

@recoup-coding-agent recoup-coding-agent commented Apr 14, 2026

Summary

  • Add fallback in pollContentRuns to check output.url (from ffmpeg-edit tasks) in addition to output.videoSourceUrl (from create-content tasks)
  • This allows the existing polling infrastructure to handle both content creation and content edit task results

Context

Part of REC-68: when users reply in existing content threads, the bot now triggers ffmpeg-edit tasks. The polling task needs to extract the video URL from the edit task output which uses a different field name.

Test plan

  • Verify existing content creation polling still works (reads videoSourceUrl)
  • Verify ffmpeg-edit task results are correctly polled (reads url fallback)

🤖 Generated with Claude Code


Summary by cubic

Update pollContentRuns to fall back to output.url (from ffmpeg-edit) when output.videoSourceUrl is missing, so polling works for both edit and create-content runs. This fulfills REC-68 by supporting replies that trigger ffmpeg-edit tasks.

Written for commit d4a6fe9. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved handling of video output sources to support alternate field formats, ensuring video URLs are correctly populated from available data sources.

The ffmpeg-edit task returns `url` instead of `videoSourceUrl`.
Add fallback to check `output.url` so the polling task can handle
both create-content and ffmpeg-edit run outputs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

The pollContentRuns function was updated to support an alternate output field for determining video URLs. When processing completed run results, the function now uses a fallback pattern that sources the videoUrl from either the existing videoSourceUrl field or an alternate url field when the former is absent.

Changes

Cohort / File(s) Summary
Output Field Fallback Logic
src/content/pollContentRuns.ts
Updated completed run output handling to support dual output fields (videoSourceUrl and url). The videoUrl is now derived using null coalescing to prefer videoSourceUrl but fall back to url when unavailable.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A URL field springs forth, oh what delight!
When one path fails, another shines bright,
With fallback logic, smooth and clean,
The video plays in between,
A rabbit's output now doubly takes flight! 🎬

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding support for ffmpeg-edit output handling in pollContentRuns by introducing a fallback to the url field.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/rec-68-poll-edit-output

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/content/pollContentRuns.ts`:
- Around line 42-47: The code reads run.output into the local const output via a
type assertion but never validates it; add a Zod schema (e.g., OutputSchema)
that defines videoSourceUrl?: string, url?: string, captionText?: string and
uses refine() to require that at least one of videoSourceUrl or url is present,
then parse/validate run.output with OutputSchema.parse or safeParse before
treating the run as completed; if validation fails, do not return status:
"completed" (handle as still pending/errored) and only set videoUrl from the
validated output (prefer videoSourceUrl or fallback to url) when the schema
passes. Ensure these changes touch the same identifiers: the const output
assignment, any logic that returns status: "completed", and the code that reads
videoUrl/captionText.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ccca4a3-217b-4d0a-a2bc-6f145fe7202b

📥 Commits

Reviewing files that changed from the base of the PR and between ddf9404 and d4a6fe9.

📒 Files selected for processing (1)
  • src/content/pollContentRuns.ts

Comment on lines 42 to 47
const output = run.output as {
videoSourceUrl?: string;
captionText?: string;
/** ffmpeg-edit tasks return `url` instead of `videoSourceUrl`. */
url?: string;
} | null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify this file is currently using a type assertion for run.output
# and does not validate with Zod.
rg -n --type=ts 'run\.output\s+as\s+\{' src/content/pollContentRuns.ts
rg -n --type=ts 'from ["'"'"']zod["'"'"']|z\.object\(|safeParse\(' src/content/pollContentRuns.ts

Repository: recoupable/tasks

Length of output: 99


🏁 Script executed:

cat -n src/content/pollContentRuns.ts

Repository: recoupable/tasks

Length of output: 2253


🏁 Script executed:

cat -n src/content/sendContentCallback.ts

Repository: recoupable/tasks

Length of output: 1792


🏁 Script executed:

cat -n src/tasks/pollContentRunTask.ts

Repository: recoupable/tasks

Length of output: 1374


🏁 Script executed:

# Check how run.output is typed in the SDK
rg -n "runs\.poll\(" src/ -A 5 | head -20

Repository: recoupable/tasks

Length of output: 1241


🏁 Script executed:

# Check if Zod is available in the project dependencies
cat package.json | grep -A 5 -B 5 "zod"

Repository: recoupable/tasks

Length of output: 477


🏁 Script executed:

# Check for other schema definitions using Zod in the project
find src -name "*.ts" -type f | xargs grep -l "z.object\|z.string" | head -10

Repository: recoupable/tasks

Length of output: 397


🏁 Script executed:

# Check an example Zod schema in the project
head -50 src/recoup/fetchTask.ts

Repository: recoupable/tasks

Length of output: 1417


🏁 Script executed:

# Check if there are any tests for pollContentRuns to understand expected behavior
find . -name "*.test.ts" -o -name "*.spec.ts" | xargs grep -l "pollContentRuns" 2>/dev/null

Repository: recoupable/tasks

Length of output: 212


🏁 Script executed:

cat -n src/content/__tests__/pollContentRuns.test.ts

Repository: recoupable/tasks

Length of output: 4345


🏁 Script executed:

# Verify the learnings about result.ok check for triggerAndWait
rg -n "triggerAndWait\|runs\.poll" src/content/pollContentRuns.ts -B 2 -A 2

Repository: recoupable/tasks

Length of output: 42


Validate run output with Zod schema and require a URL before returning completed.

Lines 42–47 and 53 use a type assertion on run.output without schema validation. When both videoSourceUrl and url are absent or null, the code still returns status: "completed" with videoUrl: undefined, which can misrepresent success to external callbacks.

Per coding guidelines, "Use Zod for schema validation" for TypeScript files. Add a Zod schema with a refine() check to ensure at least one video URL field is present before marking the run as completed.

Proposed fix
 import { runs } from "@trigger.dev/sdk/v3";
+import { z } from "zod";
 import { logStep } from "../sandboxes/logStep";
 
 const POLL_INTERVAL_MS = 30_000;
+const ContentRunOutputSchema = z
+  .object({
+    videoSourceUrl: z.string().url().optional(),
+    /** ffmpeg-edit tasks return `url` instead of `videoSourceUrl`. */
+    url: z.string().url().optional(),
+    captionText: z.string().optional(),
+  })
+  .refine(output => Boolean(output.videoSourceUrl ?? output.url), {
+    message: "Missing video URL in run output",
+  });
 
 export type ContentRunResult = {
   runId: string;
   status: "completed" | "failed" | "timeout";
   videoUrl?: string;
   captionText?: string;
   error?: string;
 };
 
 /**
  * Waits for all Trigger.dev create-content runs to reach a terminal state
  * using the native runs.poll() function, then maps results.
  */
 export async function pollContentRuns(
   runIds: string[],
 ): Promise<ContentRunResult[]> {
   const settled = await Promise.allSettled(
     runIds.map(runId =>
       runs.poll(runId, { pollIntervalMs: POLL_INTERVAL_MS }),
     ),
   );
 
   return settled.map((result, i) => {
     const runId = runIds[i];
 
     if (result.status === "rejected") {
       logStep(`Run poll failed: ${runId}`, false, { runId, error: result.reason });
       return {
         runId,
         status: "failed" as const,
         error: result.reason?.message ?? "Unknown error",
       };
     }
 
     const run = result.value;
 
     if (run.status === "COMPLETED") {
-      const output = run.output as {
-        videoSourceUrl?: string;
-        captionText?: string;
-        /** ffmpeg-edit tasks return `url` instead of `videoSourceUrl`. */
-        url?: string;
-      } | null;
+      const parsedOutput = ContentRunOutputSchema.safeParse(run.output ?? {});
+      if (!parsedOutput.success) {
+        logStep(`Run completed with invalid output: ${runId}`, false, {
+          runId,
+          issues: parsedOutput.error.issues,
+        });
+        return {
+          runId,
+          status: "failed" as const,
+          error: "Completed run missing valid output URL",
+        };
+      }
+      const output = parsedOutput.data;
 
       logStep(`Run completed: ${runId}`, false, { runId });
       return {
         runId,
         status: "completed" as const,
-        videoUrl: output?.videoSourceUrl ?? output?.url,
-        captionText: output?.captionText,
+        videoUrl: output.videoSourceUrl ?? output.url,
+        captionText: output.captionText,
       };
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const output = run.output as {
videoSourceUrl?: string;
captionText?: string;
/** ffmpeg-edit tasks return `url` instead of `videoSourceUrl`. */
url?: string;
} | null;
import { runs } from "@trigger.dev/sdk/v3";
import { z } from "zod";
import { logStep } from "../sandboxes/logStep";
const POLL_INTERVAL_MS = 30_000;
const ContentRunOutputSchema = z
.object({
videoSourceUrl: z.string().url().optional(),
/** ffmpeg-edit tasks return `url` instead of `videoSourceUrl`. */
url: z.string().url().optional(),
captionText: z.string().optional(),
})
.refine(output => Boolean(output.videoSourceUrl ?? output.url), {
message: "Missing video URL in run output",
});
export type ContentRunResult = {
runId: string;
status: "completed" | "failed" | "timeout";
videoUrl?: string;
captionText?: string;
error?: string;
};
/**
* Waits for all Trigger.dev create-content runs to reach a terminal state
* using the native runs.poll() function, then maps results.
*/
export async function pollContentRuns(
runIds: string[],
): Promise<ContentRunResult[]> {
const settled = await Promise.allSettled(
runIds.map(runId =>
runs.poll(runId, { pollIntervalMs: POLL_INTERVAL_MS }),
),
);
return settled.map((result, i) => {
const runId = runIds[i];
if (result.status === "rejected") {
logStep(`Run poll failed: ${runId}`, false, { runId, error: result.reason });
return {
runId,
status: "failed" as const,
error: result.reason?.message ?? "Unknown error",
};
}
const run = result.value;
if (run.status === "COMPLETED") {
const parsedOutput = ContentRunOutputSchema.safeParse(run.output ?? {});
if (!parsedOutput.success) {
logStep(`Run completed with invalid output: ${runId}`, false, {
runId,
issues: parsedOutput.error.issues,
});
return {
runId,
status: "failed" as const,
error: "Completed run missing valid output URL",
};
}
const output = parsedOutput.data;
logStep(`Run completed: ${runId}`, false, { runId });
return {
runId,
status: "completed" as const,
videoUrl: output.videoSourceUrl ?? output.url,
captionText: output.captionText,
};
}
});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/content/pollContentRuns.ts` around lines 42 - 47, The code reads
run.output into the local const output via a type assertion but never validates
it; add a Zod schema (e.g., OutputSchema) that defines videoSourceUrl?: string,
url?: string, captionText?: string and uses refine() to require that at least
one of videoSourceUrl or url is present, then parse/validate run.output with
OutputSchema.parse or safeParse before treating the run as completed; if
validation fails, do not return status: "completed" (handle as still
pending/errored) and only set videoUrl from the validated output (prefer
videoSourceUrl or fallback to url) when the schema passes. Ensure these changes
touch the same identifiers: the const output assignment, any logic that returns
status: "completed", and the code that reads videoUrl/captionText.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

No issues found across 1 file

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