Appearance
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 (requestMatchCasting→Match.status = CASTING_REQUESTED, behind theCASTINGflag, 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
CASTINGfeature flag (default off); not yet verified in a deployed env. Backend commits421829e(schema),b3c77cb(service/triage/export),f6b84cc(seeder/tests); frontend14fd249(UI),057964a(tests).
- ✅ BE-CASTING49-001 — migration
20260629120000_epic49_casting_match_rework: casting re-anchoredApplication→Match, mixed-media renormalised (CastingResponseMedia+CastingMediaKind,PHOTOadded),SubmissionStatusREQUESTED→DRAFT→SUBMITTED→PROCESSING→READY/FAILED, lock/deadline +CastingExportStatusexport fields.- ✅ BE-CASTING49-002 —
src/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;requestMatchCastingextended with optionalsteps.- ✅ BE-CASTING49-003 —
src/workers/castingTriage.ts: Whisper transcript per VIDEO + GPT tldr/summary; fail-soft; degrades withoutOPENAI_API_KEY.- ✅ BE-CASTING49-004 —
src/services/casting/{renderer,queue}.ts: stitched MP4 (per-step title cards + bottom-right watermark) reusing reel ffmpeg helpers;exportCastingVideo.- ✅ FE-CASTING49-001 —
CastingComposerModal+CastingReviewModalin the JobDetailPage matches panel (request, review responses+transcript+AI, export→poll→download, lock).- ✅ FE-CASTING49-002 —
/casting/:matchIdCastingWizardPage(per-step text + record/upload/portfolio-pick, save/resume/edit-while-open, submit) + entry from MyApplicationsPage;useCastinghook,@castyou/core/queries/casting, i18n en/pt/es, pageTitle.- ✅ TEST-CASTING49-001 — BE
casting.test.ts(editability, mixed-media validation, submit gate, authz) + FEuseCasting.test.ts; seeder reworked to Match-scoped (6 templates, 15 submissions). BE 1576 / FE green; typecheck + lint clean.- ✅ E2E —
castyou-automated-tests/tests/casting.spec.ts: full Match→request(composer)→submit(wizard)→review round-trip, 4/4 green locally (commit7659271). CASTING already in the suite flag baseline. Not yet done: deploy-time setup (setOPENAI_API_KEYfor triage; drop a watermark PNG atsrc/services/reel/assets/watermark.pngor rely on the text fallback) and flipping theCASTINGflag 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 portfolioMediaItems. The portfolio link is one-way only — talent may pull an existing portfolio item in as an answer (mediaItemIdreference); casting media never flows back out into the portfolio.
Architecture:
- Re-anchor the retained Prisma tables from the dead
Applicationmodel toMatch. The inertCastingMap/CastingStep/CastingSubmission/CastingStepResponsetables (kept from Epic 14) are Application-coupled; Epic 42 deletedApplication. Rework toMatch-coupled. Oneprisma migrate. - Two orthogonal pipelines: (1) Triage — auto on submit: Whisper transcript per video attachment → GPT
tldr+summaryover 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 fromsrc/workers/{mediaProcessor,moderationScanner,emailSender}.ts; the export renderer reusessrc/services/reel/renderer.tshelpers (ffmpeg stitching,drawtexttitle cards, bottom-right watermark, image-hold durations, ffmpeg child-process guards) + thereel/queue.tsqueue pattern. Frontend reuses DSVideoRecorderandusePortfoliofor the portfolio-pick answer path. - Editability window: a submission is editable iff
lockedAt == null && Match.status == CASTING_REQUESTED(and beforedeadlineif 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— dropapplicationId/application; addmatchId String? @unique(null+jobId= job-level template; set = per-request map).CastingSubmission— dropapplicationId,finalVideoUrl,assemblyJobId; addmatchId String @unique,lockedAt DateTime?,deadline DateTime?,exportUrl String?,exportStatus(NONE|RENDERING|READY|FAILED),exportError String?,exportJobId String?; keeptldr,summary,castingMapId.CastingStep— keeporder,question,description,allowedTypes,required.CastingStepResponse— one per (submission, step): keeptextResponse String?; drop the singleresponseType/mediaUrl/duration/transcript; add child relationmedia CastingResponseMedia[].CastingResponseMedia(new table) —{ kind: PHOTO|VIDEO, mediaUrl String?(R2)| mediaItemId String?(portfolio),duration Float?,transcript String?,order Int }.CastingResponseTypeenum — addPHOTO(nowTEXT | PHOTO | VIDEO; record-vs-upload is a source detail, not a kind).Match— add reverse relation toCastingSubmission.
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. Requiresprisma 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.ts—upsertCastingTemplate(jobId, steps[])(job-levelCastingMap{matchId:null}); extendrequestMatchCastingto clone the template (or an inlinestepsoverride) into a per-matchCastingMap+ createCastingSubmission{REQUESTED};saveCastingProgress(submissionId, responses[])(upsert partial, staysDRAFT);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 — onSUBMITTED: Whisper-transcribe eachVIDEOCastingResponseMedia, then GPT-summarize text + transcripts intotldr+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+ acasting-renderBullMQ queue, reusingreel/renderer.tshelpers (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; updatesexportStatus/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 + AItldr/summary); an "Export video" button (enqueue → pollexportStatus→ 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 DSVideoRecorder, 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.