diff --git a/apps/web/src/app/pricing/page.tsx b/apps/web/src/app/pricing/page.tsx index abdab754f..e6a9275ad 100644 --- a/apps/web/src/app/pricing/page.tsx +++ b/apps/web/src/app/pricing/page.tsx @@ -17,8 +17,8 @@ const CORE_CAPABILITIES = [ 'Evidence-gated YouTube analysis', 'Timed caption transcript', 'Visible source provenance', - 'Durable workflow run history', - 'Review-only action preparation', + 'Workflow run history', + 'Proposed work from a Video Pack', 'Export after quality verification', ]; diff --git a/apps/web/src/components/OneLoopStudio.tsx b/apps/web/src/components/OneLoopStudio.tsx index eebb07129..4b9c556f3 100644 --- a/apps/web/src/components/OneLoopStudio.tsx +++ b/apps/web/src/components/OneLoopStudio.tsx @@ -1033,6 +1033,15 @@ export default function OneLoopStudio({ ) : null}
+ + ) : null} + ) : ( <> {reviewActions.map((action, i) => { @@ -239,12 +258,23 @@ export default function StudioIdeOutput({ -
+

{card.statusLabel}

{card.title}

{card.detail}

+ {onAct ? ( + + ) : null}
diff --git a/apps/web/src/components/studio/__tests__/StudioIdeOutput.playflow.test.tsx b/apps/web/src/components/studio/__tests__/StudioIdeOutput.playflow.test.tsx index fa67b7c34..8f39608cb 100644 --- a/apps/web/src/components/studio/__tests__/StudioIdeOutput.playflow.test.tsx +++ b/apps/web/src/components/studio/__tests__/StudioIdeOutput.playflow.test.tsx @@ -1,7 +1,7 @@ // @vitest-environment jsdom import React from 'react'; import { cleanup, fireEvent, render, screen } from '@testing-library/react'; -import { afterEach, describe, expect, it } from 'vitest'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import StudioIdeOutput from '@/components/studio/StudioIdeOutput'; import type { Video } from '@/store/dashboard-types'; @@ -56,4 +56,23 @@ describe('StudioIdeOutput playflow', () => { expect(card.textContent).toMatch(/needs review/i); expect(card.textContent).toMatch(/Review evidence/); }); + + it('renders review_action as a proposed card with Act on this run', () => { + const onAct = vi.fn(); + render( + , + ); + + fireEvent.click(screen.getByTestId('studio-ide-output-tab-intent')); + expect(screen.getByTestId('studio-review-action-card').textContent).toMatch(/Review this result/); + expect(screen.getByTestId('studio-action-cta').textContent).toBe('Act on this run'); + fireEvent.click(screen.getByTestId('studio-action-cta')); + expect(onAct).toHaveBeenCalledTimes(1); + }); }); 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 05167ecba..ff1125c60 100644 --- a/apps/web/src/lib/__tests__/studio-pipeline-status.test.ts +++ b/apps/web/src/lib/__tests__/studio-pipeline-status.test.ts @@ -546,7 +546,13 @@ describe('studio-pipeline-status', () => { expect(shell).toContain('data-testid="studio-ide-toolbar"'); expect(studio).toContain('StudioIdeShell'); expect(pricing).not.toMatch(/reviewed plan dispatches backend agents/); + expect(pricing).not.toMatch(/Durable workflow run history/); + expect(pricing).not.toMatch(/reviewed actions/i); + expect(pricing).toContain('Proposed work from a Video Pack'); + expect(pricing).toContain('Workflow run history'); expect(apiDocs).not.toMatch(/durable Studio analysis workflow/); + expect(studio).toContain('data-testid="studio-act-button"'); + expect(studio).toContain('Act on this run'); }); it('renders review_action as a card with status, title, and detail', () => { @@ -559,18 +565,28 @@ describe('studio-pipeline-status', () => { expect(review.title).toMatch(/review this result/i); expect(review.statusLabel).toBe('Needs review'); expect(review.detail).toBe('Review evidence'); + expect(review.ctaLabel).toBe('Act on this run'); + + const proposed = studioActionCard({ tool: 'review_action', status: 'proposed' }); + expect(proposed.detail).toMatch(/Proposed finding/); const tool = studioActionCard({ tool: 'persist_insight', status: 'completed' }); expect(tool.kind).toBe('tool'); expect(tool.title).toMatch(/persist insight/i); expect(tool.statusLabel).toBe('Done'); expect(tool.detail).toMatch(/no detail/i); + expect(tool.ctaLabel).toBe('Keep on this page'); const studio = readFileSync(join(process.cwd(), 'src/components/OneLoopStudio.tsx'), 'utf8'); const output = readFileSync(join(process.cwd(), 'src/components/studio/StudioIdeOutput.tsx'), 'utf8'); expect(studio).toContain('studioActionCard'); expect(output).toContain('studioActionCard'); expect(output).toContain('data-testid="studio-review-action-card"'); + expect(output).toContain('data-testid="studio-action-cta"'); + expect(studio).toContain('data-testid="studio-act-button"'); + expect(studio).not.toMatch( + /showAgentWorkflowUi[\s\S]{0,400}data-testid="studio-act-button"/, + ); // IDE: actions surface in the output pane Intent tab. expect(output).toContain("{ id: 'intent', label: 'Intent' }"); }); diff --git a/apps/web/src/lib/studio-pipeline-status.ts b/apps/web/src/lib/studio-pipeline-status.ts index 2bd869c7e..16d0d8731 100644 --- a/apps/web/src/lib/studio-pipeline-status.ts +++ b/apps/web/src/lib/studio-pipeline-status.ts @@ -637,6 +637,7 @@ export function studioActionCard(action: { statusLabel: string; detail: string; kind: 'review' | 'tool'; + ctaLabel: string; } { const isReview = action.tool === 'review_action'; const statusLabel = @@ -656,12 +657,15 @@ export function studioActionCard(action: { .join(' '); const detail = action.result?.trim() || - (isReview ? 'Open the evidence on this page before you act.' : 'No detail from this tool.'); + (isReview + ? 'Proposed finding — open the evidence on this page before you act.' + : 'No detail from this tool.'); return { title, statusLabel, detail, kind: isReview ? 'review' : 'tool', + ctaLabel: isReview ? 'Act on this run' : 'Keep on this page', }; } diff --git a/apps/web/src/lib/studio-playflow-PLAN.md b/apps/web/src/lib/studio-playflow-PLAN.md index ab68c22ec..db662b14c 100644 --- a/apps/web/src/lib/studio-playflow-PLAN.md +++ b/apps/web/src/lib/studio-playflow-PLAN.md @@ -17,6 +17,7 @@ - [x] Transcript retry control when `studioCanRetryTranscript` - [x] Export toast no longer uses sticky-footer `bottom-20` - [x] Focused Vitest +- [x] Leftover after #2437: pricing Core copy (G) + ungated Act CTA on review cards (E) ## 3. Definition of Done (Success Verification) * **Expected Outcome:** Anonymous Studio on a seadance/cluely pack can click **Ship this** (ZIP export). Stack checks read skip/unknown and do not disable the button. No live hostname claim. Transcript retry and review cards are visible. @@ -27,3 +28,4 @@ * **What was done:** Restored a discoverable **Ship this** toolbar CTA that downloads the pack ZIP. Anonymous/unknown SeaDance/Cluely stack checks render as skip/unknown and never disable the button. Intent tab renders `review_action` cards. Transcript retry is visible on retryable failure. Export toast sits at `bottom-4` (no sticky footer). Origin deploy button was not restored. * **Why it was needed:** IDE shell left playflow helpers unwired; dogfood kill F was a disabled Deploy with no Ship this. * **How it was tested:** Focused Vitest (49 + 16), type-check, eslint. No production live claim. GitHub issue create was 403 so this cut has no new issue number. +* **Leftover after #2437 (this PR):** Pricing Core copy no longer says durable/reviewed; Act on this run is a toolbar + Intent CTA without `agent-workflow-ui`. Focused Vitest 5 files / 53 passed plus 3 related files / 15 passed. Canonical leftover issue #2440.