Skip to content

Epic 49 — On-System Casting (Structured Auditions, Redesigned)

New design 2026-06-29. This is the fresh redesign of Epic 14 (Casting Submission) and the build-out of the casting capture flow stubbed by Epic 43's BE-MATCHLIFE-002 — which is now superseded by this epic. Epic 43 already ships the casting request status (requestMatchCastingMatch.status = CASTING_REQUESTED, behind the CASTING flag, default off); Epic 49 builds everything behind that status. The original Epic 14 build (auto-assembled branded reel + Whisper/GPT pipeline) is not restored as-is — see [[Casting Submission — Redesign]].

Status: ✅ BUILT 2026-06-29 (all tickets P1–P7). Behind the CASTING feature flag (default off); not yet verified in a deployed env. Backend commits 421829e (schema), b3c77cb (service/triage/export), f6b84cc (seeder/tests); frontend 14fd249 (UI), 057964a (tests).

  • BE-CASTING49-001 — migration 20260629120000_epic49_casting_match_rework: casting re-anchored ApplicationMatch, mixed-media renormalised (CastingResponseMedia + CastingMediaKind, PHOTO added), SubmissionStatus REQUESTED→DRAFT→SUBMITTED→PROCESSING→READY/FAILED, lock/deadline + CastingExportStatus export fields.
  • BE-CASTING49-002src/services/casting/index.ts (template upsert, request/clone, save-progress→DRAFT, submit w/ required-step gate, lock, editability window) + GraphQL types/queries/mutations + resolvers/casting.ts; requestMatchCasting extended with optional steps.
  • BE-CASTING49-003src/workers/castingTriage.ts: Whisper transcript per VIDEO + GPT tldr/summary; fail-soft; degrades without OPENAI_API_KEY.
  • BE-CASTING49-004src/services/casting/{renderer,queue}.ts: stitched MP4 (per-step title cards + bottom-right watermark) reusing reel ffmpeg helpers; exportCastingVideo.
  • FE-CASTING49-001CastingComposerModal + CastingReviewModal in the JobDetailPage matches panel (request, review responses+transcript+AI, export→poll→download, lock).
  • FE-CASTING49-002/casting/:matchId CastingWizardPage (per-step text + record/upload/portfolio-pick, save/resume/edit-while-open, submit) + entry from MyApplicationsPage; useCasting hook, @castyou/core/queries/casting, i18n en/pt/es, pageTitle.
  • TEST-CASTING49-001 — BE casting.test.ts (editability, mixed-media validation, submit gate, authz) + FE useCasting.test.ts; seeder reworked to Match-scoped (6 templates, 15 submissions). BE 1576 / FE green; typecheck + lint clean.
  • E2Ecastyou-automated-tests/tests/casting.spec.ts: full Match→request(composer)→submit(wizard)→review round-trip, 4/4 green locally (commit 7659271). CASTING already in the suite flag baseline. Not yet done: deploy-time setup (set OPENAI_API_KEY for triage; drop a watermark PNG at src/services/reel/assets/watermark.png or rely on the text fallback) and flipping the CASTING flag on; no end-to-end verification in a deployed env yet.

Product decisions (user, 2026-06-29):

  • v1 scope = self-tape Q&A + AI triage (not the minimal no-AI version, not the full old branded-reel pipeline). Producers get an auto transcript + short AI summary per submission to skim fast.
  • Access = free for all producers (still flag-gated; no tier gate, no CasTars cost in v1).
  • Step answers are mixed-media: a step answer is text and/or N media of mixed kinds (text+photo, text+video, multiple clips, etc.). The old single-type model could not express this and had no PHOTO kind — both fixed here.
  • Talent can save & resume: partial saves, exit, resume, and edit completed steps while the request is still open/accepting; explicit submit triggers triage; re-edit allowed until the producer locks the submission to review.
  • Producer can export a stitched video: on-demand render of all responses into one MP4 — per-step title cards + bottom-right watermark. Separate from the AI triage.
  • Casting media stays OUT of the portfolio (user, 2026-06-29): fresh recordings/uploads land in a dedicated R2 casting/ folder and are never created as portfolio MediaItems. The portfolio link is one-way only — talent may pull an existing portfolio item in as an answer (mediaItemId reference); casting media never flows back out into the portfolio.

Architecture:

  • Re-anchor the retained Prisma tables from the dead Application model to Match. The inert CastingMap/CastingStep/CastingSubmission/CastingStepResponse tables (kept from Epic 14) are Application-coupled; Epic 42 deleted Application. Rework to Match-coupled. One prisma migrate.
  • Two orthogonal pipelines: (1) Triage — auto on submit: Whisper transcript per video attachment → GPT tldr + summary over text + transcripts; fail-soft. (2) Export — producer-initiated stitched-video render. Independent of each other.
  • Reuse, don't rebuild: OpenAI via src/services/ai/* + src/services/media/index.ts; R2 presign via the portfolio upload path (graphql/resolvers/portfolio.ts); BullMQ worker pattern from src/workers/{mediaProcessor,moderationScanner,emailSender}.ts; the export renderer reuses src/services/reel/renderer.ts helpers (ffmpeg stitching, drawtext title cards, bottom-right watermark, image-hold durations, ffmpeg child-process guards) + the reel/queue.ts queue pattern. Frontend reuses DS VideoRecorder and usePortfolio for the portfolio-pick answer path.
  • Editability window: a submission is editable iff lockedAt == null && Match.status == CASTING_REQUESTED (and before deadline if the producer set one). Producer locking freezes it for review. (Defaults: re-edit allowed after submit until lock; no deadline unless set.)
  • Photos are stored/rendered but not vision-analyzed in v1 triage (videos transcribed, text + transcripts summarized).

Schema delta (one prisma migrate):

  • CastingMap — drop applicationId/application; add matchId String? @unique (null + jobId = job-level template; set = per-request map).
  • CastingSubmission — drop applicationId, finalVideoUrl, assemblyJobId; add matchId String @unique, lockedAt DateTime?, deadline DateTime?, exportUrl String?, exportStatus (NONE|RENDERING|READY|FAILED), exportError String?, exportJobId String?; keep tldr, summary, castingMapId.
  • CastingStep — keep order, question, description, allowedTypes, required.
  • CastingStepResponse — one per (submission, step): keep textResponse String?; drop the single responseType/mediaUrl/duration/transcript; add child relation media CastingResponseMedia[].
  • CastingResponseMedia (new table){ kind: PHOTO|VIDEO, mediaUrl String? (R2) | mediaItemId String? (portfolio), duration Float?, transcript String?, order Int }.
  • CastingResponseType enum — add PHOTO (now TEXT | PHOTO | VIDEO; record-vs-upload is a source detail, not a kind).
  • Match — add reverse relation to CastingSubmission.

BE-CASTING49-001 — Schema rework (Application→Match, mixed-media, lock/export fields)

Files:

  • Edit: castyou-backend/prisma/schema.prisma — apply the full schema delta above. Requires prisma migrate. Re-run the seeder after ([[BE Seeder]]).

Acceptance criteria: Tables compile and migrate; a CastingSubmission links to a Match (not an Application); a CastingStepResponse can hold textResponse + multiple CastingResponseMedia of mixed PHOTO/VIDEO kinds; lock/deadline/export fields exist.

BE-CASTING49-002 — Casting service + GraphQL (request / save / submit / lock / review)

Files:

  • Create: castyou-backend/src/services/casting/index.tsupsertCastingTemplate(jobId, steps[]) (job-level CastingMap{matchId:null}); extend requestMatchCasting to clone the template (or an inline steps override) into a per-match CastingMap + create CastingSubmission{REQUESTED}; saveCastingProgress(submissionId, responses[]) (upsert partial, stays DRAFT); submitCastingResponses(submissionId) (→ SUBMITTED, enqueue triage); lockCastingSubmission(submissionId) (producer freeze). Editability guard per the window rule.
  • Edit: src/graphql/schema/index.ts + resolvers — casting types/mutations/queries; producer review query returns steps + responses + media + tldr/summary.

Acceptance criteria: With CASTING on: producer requests (template prefilled), talent saves partial + resumes + edits completed steps + submits, producer reviews; edits blocked once locked or match resolved; all authorized to the correct party. Flag off hides it.

BE-CASTING49-003 — Triage worker (Whisper transcript + GPT summary)

Files:

  • Create: castyou-backend/src/workers/castingTriage.ts (+ queue) mirroring the BullMQ worker pattern — on SUBMITTED: Whisper-transcribe each VIDEO CastingResponseMedia, then GPT-summarize text + transcripts into tldr + summary. SUBMITTED → PROCESSING → READY; fail-soft → leave reviewable, record error.

Acceptance criteria: Submitting populates transcripts + tldr/summary; failures don't block producer review; re-submitting an edited video step re-runs triage for the change.

BE-CASTING49-004 — Export renderer (stitched video: title cards + watermark)

Files:

  • Create: castyou-backend/src/services/casting/renderer.ts + a casting-render BullMQ queue, reusing reel/renderer.ts helpers (drawtext, watermark overlay, ffmpeg guards). exportCastingVideo(submissionId) enqueues a render → per-step title card (the question) + step media in order (videos play, photos hold ~4s, text-only → text card) + bottom-right watermark throughout → single MP4 to R2; updates exportStatus/exportUrl/exportError.

Acceptance criteria: Producer triggers export, a single stitched MP4 with per-step titles + watermark lands in R2 and is downloadable; render failures surface a generic error + retain the raw error for admins.

FE-CASTING49-001 — Producer composer + review + export UI

Files:

  • Create: in the per-job Matches panel (Epic 43 FE-MATCHLIFE-001) — a "Request casting" composer (prompt builder, prefilled from the job template; per-step allowedTypes + required); a submission review view (each response's text + media player + transcript + AI tldr/summary); an "Export video" button (enqueue → poll exportStatus → download). DS components + i18n en/pt/es + pageTitles. ([[Design System Rule]], [[Page Titles]])

Acceptance criteria: Producer builds/sends a casting request, reviews submitted responses with transcript + AI summary, and exports a stitched video — all reflecting server state after reload; hidden when CASTING is off.

FE-CASTING49-002 — Talent self-tape wizard (mixed-media, save & resume)

Files:

  • Create: a talent casting wizard reached from the Matches page (Epic 43 FE-MATCHLIFE-002) — one screen per step honoring allowedTypes: text + multi-media answers via DS VideoRecorder, file upload, or portfolio pick (usePortfolio); save-and-exit, resume, and edit completed steps while open; explicit submit. DS + i18n en/pt/es + pageTitles. ([[Unified Portfolio (Epic 34)]])

Acceptance criteria: Talent answers steps with text/photo/video or a mix, saves partway and resumes later, edits completed steps while the request is open, and submits; composer disabled once locked/closed.

TEST-CASTING49-001 — Casting flow coverage

Acceptance criteria: Schema/auth, request→save→resume→edit→submit, mixed-media responses (text+photo+video), the editability/lock window, triage (transcript + summary, fail-soft), and the export render (title cards + watermark, failure path) are all tested; seeder exercises a full casting end-to-end.