diff --git a/apps/web/src/components/VideoWorkflowStudio.tsx b/apps/web/src/components/VideoWorkflowStudio.tsx index 17653c688..1326d68c7 100644 --- a/apps/web/src/components/VideoWorkflowStudio.tsx +++ b/apps/web/src/components/VideoWorkflowStudio.tsx @@ -27,6 +27,7 @@ import { studioRunQuality, studioStatusLabel, studioStatusMessage, + studioVerifiedLiveUrl, type StudioPipelineCheck, type StudioRunQuality, } from '@/lib/studio-pipeline-status'; @@ -626,13 +627,13 @@ export default function VideoWorkflowStudio() { delayMs: 2000, signal: deployAbort.signal, }); - const live = polled.result?.live_url; + const live = studioVerifiedLiveUrl(polled.result?.live_url); if (live) setDeployLiveUrl(live); if (polled.result?.github_repo) setDeployRepo(polled.result.github_repo); if (polled.result?.jobId) setDeployJobId(polled.result.jobId); if (live) { - setActionMessage(`Deploy ready: ${live} (run ${started.runId})`); + setActionMessage(`Deploy receipt: ${live} (run ${started.runId})`); return; } if (polled.result?.kind === 'handoff') { @@ -667,7 +668,8 @@ export default function VideoWorkflowStudio() { }); jobId = kick.jobId; if (kick.jobId) setDeployJobId(kick.jobId); - if (kick.live_url) setDeployLiveUrl(kick.live_url); + const kickLive = studioVerifiedLiveUrl(kick.live_url); + if (kickLive) setDeployLiveUrl(kickLive); if (kick.github_repo) setDeployRepo(kick.github_repo); if (!kick.ok && !kick.jobId) { @@ -679,8 +681,8 @@ export default function VideoWorkflowStudio() { return; } - if (kick.live_url) { - setActionMessage(`Deploy live: ${kick.live_url}`); + if (kickLive) { + setActionMessage(`Deploy receipt: ${kickLive}`); return; } @@ -699,11 +701,12 @@ export default function VideoWorkflowStudio() { } const polled = await pollStudioJob(jobId, { attempts: 6, delayMs: 1500 }); - if (polled.live_url) setDeployLiveUrl(polled.live_url); + const polledLive = studioVerifiedLiveUrl(polled.live_url); + if (polledLive) setDeployLiveUrl(polledLive); if (polled.github_repo) setDeployRepo(polled.github_repo); - if (polled.live_url) { - setActionMessage(`Deploy ready: ${polled.live_url}`); + if (polledLive) { + setActionMessage(`Deploy receipt: ${polledLive}`); } else if (polled.jobStatus === 'failed' || polled.jobStatus === 'error') { setActionMessage( `Deploy job ${jobId} failed${polled.message ? `: ${polled.message}` : ''}. Export package for manual handoff.`, diff --git a/apps/web/src/lib/__tests__/studio-pipeline-status.test.ts b/apps/web/src/lib/__tests__/studio-pipeline-status.test.ts index 0afe3e3de..dc7a82717 100644 --- a/apps/web/src/lib/__tests__/studio-pipeline-status.test.ts +++ b/apps/web/src/lib/__tests__/studio-pipeline-status.test.ts @@ -348,6 +348,13 @@ describe('studio-pipeline-status', () => { expect(retired).not.toMatch(/durable Workflow DevKit/); }); + it('does not claim live deploy in retired studio without a verified receipt guard', () => { + const retired = readFileSync(join(process.cwd(), 'src/components/VideoWorkflowStudio.tsx'), 'utf8'); + expect(retired).toContain('studioVerifiedLiveUrl'); + expect(retired).not.toContain('setActionMessage(`Deploy live: ${kick.live_url}`)'); + expect(retired).not.toContain('setActionMessage(`Deploy ready: ${polled.live_url}`)'); + }); + it('renders review_action as a card with status, title, and detail', () => { const review = studioActionCard({ tool: 'review_action',