Skip to content

Gate workspace ZIP exports and refinery dataset downloads behind Workflow Pro - #1802

Open
groupthinking with Copilot wants to merge 3 commits into
mainfrom
copilot/gate-workspace-zip-exports
Open

Gate workspace ZIP exports and refinery dataset downloads behind Workflow Pro#1802
groupthinking with Copilot wants to merge 3 commits into
mainfrom
copilot/gate-workspace-zip-exports

Conversation

Copilot AI commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Canonical issue

Linked by the system.

Outcome

Workspace ZIP exports and /api/v1/refinery/dataset now fail closed for free callers with a Stripe-backed 402 response, while paid callers can unlock access immediately via verified checkout session activation and retry the same download flow without losing context.

Scope

  • Included:
    • Paid route gate
      • Added a shared gate for paid download surfaces in apps/web/src/lib/billing/pro-feature-gate.ts.
      • Reuses trusted billing identity, cached checkout activation, live Stripe session lookup, and signed billing-cookie minting after successful verification.
    • Workspace ZIP export
      • Added POST /api/workspace/export.
      • Validates exported file paths, returns ZIP bytes for Pro, and returns 402 payment_required with Stripe checkout metadata for free callers.
    • Refinery dataset access
      • Added GET /api/v1/refinery/dataset.
      • Gates both dataset metadata and JSONL download behind the same Pro verification flow.
    • Client download behavior
      • Switched scaffold export to the new workspace export API instead of local-only blob assembly.
      • Added transient retry behavior for server-side export failures and checkout redirect handling for 402 responses.
    • Billing/status surface
      • Extended paid feature flags to expose workspaceZipExport and refineryDatasetAccess.
    • Focused coverage
      • Added route tests for free vs Pro access, paid-session verification, and client retry/download behavior.
  • Explicitly excluded:
    • New pricing/catalog changes
    • New non-Pro access paths
    • Broader billing/store refactors beyond these gated download surfaces

Risk

  • Risk level: medium
  • Failure mode:
    • Free users could be incorrectly blocked or Pro users could fail to unlock if Stripe/session activation regresses.
    • Download UX could stall if the export route contract drifts from the client helper.
  • Rollback:
    • Revert the new paid-route helper and the two gated routes; client export falls back to pre-gate behavior with a single revert.

Verification

List exact automated and manual checks, tied to the current head SHA.

  • Focused tests
    • npm test --prefix apps/web -- src/lib/__tests__/action-surface.test.ts src/app/api/__tests__/workspace-export-route.test.ts src/app/api/v1/refinery/dataset/__tests__/route.test.ts src/app/api/__tests__/billing-activate-route.test.ts src/app/api/__tests__/billing-checkout-route.test.ts src/app/api/__tests__/billing-renew-route.test.ts src/app/api/__tests__/billing-spoofing.test.ts src/app/api/__tests__/video-generate-route.test.ts src/app/api/__tests__/actions-route.test.ts
  • Required CI
  • Review threads resolved

Production evidence

Agent handoff

  • One canonical issue is linked
  • No competing PR implements the same issue
  • Acceptance criteria are satisfied
  • Required checks pass on the current head
  • Human decision is requested only for product, security, irreversible infrastructure, or production approval
const access = await requireProFeatureAccess(request, {
  featureKey: 'workspace_export',
  featureLabel: 'Workspace ZIP exports',
  sessionId: body.sessionId,
});

if (!access.ok) return access.response;

@vercel

vercel Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
v0-uvai Ready Ready Preview, v0 Sep 12, 2026 9:06am UTC

Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com>
Copilot AI changed the title [WIP] Add Stripe checkout for workspace ZIP exports and datasets Gate workspace ZIP exports and refinery dataset downloads behind Workflow Pro Sep 8, 2026
Copilot AI requested a review from groupthinking September 8, 2026 23:41
Comment thread apps/web/src/components/dashboard/panels.tsx Outdated
…a 402-with-`checkoutUrl` is silently swallowed, so users get no feedback when a download fails.

This commit fixes the issue reported at apps/web/src/components/dashboard/panels.tsx:255

## Bug

`downloadScaffoldPackage` (in `apps/web/src/lib/action-surface.ts`) was changed from an always-succeeding client-side zip into an async network call (`POST /api/workspace/export`). Its `ScaffoldDownloadResult` type has many failure shapes:

- network error → `{ ok: false, status: 0, error }`
- transient 5xx after 3 retries → `{ ok: false, status: 5xx, error }`
- a `500 payment_gate_failed` → `{ ok: false, status: 500, error }`
- a 402 with a null/absent `checkoutUrl` → `{ ok: false, status: 402, checkoutUrl: undefined }`

But the `ActionsPanel` handler only reacted to a single shape:

```ts
const result = await downloadScaffoldPackage(pkg);
if (!result.ok && result.checkoutUrl) {
  window.location.href = result.checkoutUrl;
}
```

**Concrete trigger:** a Pro user (or any user hitting a transient 500 / network error) clicks "Export package". `result.ok` is `false` and `result.checkoutUrl` is `undefined`, so the `if` branch is skipped and nothing happens — no download, no error, no feedback. This is a regression in error visibility because the previous implementation always produced a zip locally.

The sibling handler in `OneLoopStudio.tsx` handles all three cases (402+url redirect, other-failure toast, success toast) via `setExportToast`, confirming an established pattern for surfacing export outcomes.

## Fix

`ActionsPanel` had no error/success UI mechanism, so I added a lightweight one mirroring the `OneLoopStudio` pattern:

- Added `exportMessage` state (`{ tone: 'success' | 'error'; text }`) with an auto-dismiss `useEffect` (5s).
- Rewrote `exportScaffold` to handle every result shape: `402 + checkoutUrl` → error toast + redirect; any other failure → error message with `result.error`; success → success message with the filename; plus a `try/catch` for thrown errors.
- Rendered the message inside the "Project scaffold" section with `role="alert"`/`role="status"` for accessibility.

Type-checking `panels.tsx` with `tsc --noEmit` reports no errors.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: groupthinking <garveyht@gmail.com>
@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 717cd315-d812-473f-aa37-72469046ac72

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation javascript Pull requests that update javascript code tests labels Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 82e85b8.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

const trimmedPath = path.trim().replace(/\\/g, '/');
if (!trimmedPath || trimmedPath.startsWith('/')) return null;
if (trimmedPath.split('/').some((part) => !part || part === '.' || part === '..')) return null;
normalized[trimmedPath] = content;
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR Validation

⚠️ PR title should follow conventional commits format
⚠️ Large PR detected (787 lines changed)

@groupthinking
groupthinking marked this pull request as ready for review September 12, 2026 10:12
@groupthinking groupthinking added the bug Something isn't working label Sep 12, 2026
@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Backlog triage: leaving open. Workflow Pro ZIP/dataset gate (#1643) is real but CONFLICTING vs rewritten main. Rebase + core-green; do not invent Stripe secrets.

@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

CoS hold: named timeout cut in flight. Diff includes OneLoopStudio.tsx (dogfood path). Do not dual-write. Blocker: named timeout cut in flight.

@groupthinking
groupthinking enabled auto-merge (squash) September 12, 2026 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation javascript Pull requests that update javascript code tests

Projects

None yet

3 participants