v0.11
2026-05-23PRE-LAUNCHApp-shell stability: Clerk's UserButton was emitting hydration mismatch errors on every authenticated route. The shell now hydrates cleanly + the e2e harness no longer flakes on heavy upload paths.
- fixTopbar's <UserButton /> wrapped in a mount-gate (stable aria-hidden placeholder during SSR + first client render, swap to real Clerk widget after useEffect). Eliminates `Hydration failed because the server rendered HTML didn't match the client` errors on /dashboard, /projects, /projects/new, /projects/[id], /studio, /exports, /billing, /settings. Placeholder dims match the eventual avatar box so there's no layout shift.
- adde2e/no-hydration-errors.spec.ts — Playwright spec that visits all 8 Topbar routes, listens for any console-error or pageerror matching React #418 / #421 / #423 / #425 codes or `hydrat*` text, fails the build on a regression.
- perfPlaywright workers capped at 2 locally + 1 retry (CI keeps 1 worker / 2 retries). The studio upload + autosave path saturates Next dev under unbounded parallelism; the cap drops e2e flakes to zero and the full suite runs 21/21 green.
v0.10
2026-05-23PRE-LAUNCHStudio uploads now persist + Export current produces a real exact-pixel App Store PNG. Two stacked bugs were silently masking each other in the pre-fix code.
- add/api/upload/direct — same-origin proxied multipart upload that PUTs to R2 with server credentials. Sidesteps missing R2 bucket-CORS without operator action. Studio's onUpload routes through this; the blob URL is swapped for the durable https URL on success and screenshotRemote flips to true.
- add/api/r2-proxy — same-origin read proxy for R2 public URLs. Strict key-regex validation (users/<uuid>/(projects/<uuid>|uploads)/<nanoid>.<ext>), content-type allowlist (PNG/JPEG/WEBP), 12 MB cap, immutable cache headers. Lets html-to-image's canvas.drawImage read R2 bytes without tainting the canvas.
- fixStudio readiness now requires screenshotRemote === true. A blob URL alone (browser-local, stripped on save) no longer counts as ready — the prior signal lied because panels looked ready but vanished on reload.
- fixExport pixel-ratio double-scaling. html-to-image multiplies canvasWidth by pixelRatio when both are set; the prior code passed both and produced 3782×… instead of 1290×2796. Dropped the redundant canvasWidth/canvasHeight options — single source of truth is `node CSS width × pixelRatio = output px`.
- adde2e/studio-export-loop.spec.ts — sharp-measured PNG validation. Drives upload → autosave → Export current → captured download → sharp.metadata() asserts format=png + exact 1290×2796 dims + file size > 20 KB (rules out tainted blanks). Cross-surface READY asserted on /dashboard + /projects + /projects/[id] after the export.
- adde2e/studio-upload-persistence.spec.ts — proves the upload survives autosave + page reload, with the panel still marked READY on the rehydrated /studio surface.
Truthful surfaces sweep: Studio, /exports, /projects/[id], /dashboard, and /projects all derive status from one shared readiness model instead of hardcoding `READY` / `DRAFT` regardless of real state.
- addlib/studio/readiness.ts — single source of truth. evaluatePanel + evaluateStudio + statusOf + statusLabel produce a canonical enum (empty / blocked / partial / ready) that every surface consumes.
- addlib/studio/project-status.ts — wraps the readiness chain for per-project status across the list + overview surfaces. Same enum, same badge variant, same state-aware next-action (Open studio / Upload in Studio / Prepare in Studio / Open Exports).
- fixStudio's `EXPORT READY` InfoCell + Export current / Export all buttons now gate on real readiness. Empty projects no longer claim ready or enable export. Defense-in-depth: if a blocked panel is dispatched anyway (DevTools strip), the run logs a Blocked row with the missing-pieces reason instead of silently no-opping.
- fix/projects/[id]/exports — dead-end `Render now · coming soon` CTA replaced with state-aware `Prepare in Studio` / `Open Studio to export`. Per-device cards show real PANELS X / READY Y counts derived from studio.panels[].deviceId. Header readiness card matches Studio's data-readiness-status.
- fix/projects/[id] overview — replaced hardcoded `Shot grid · 0 / 24 slots` + 8 empty placeholders + always-`◯ READY` target rows with real panel-derived state. Per-panel checklist surfaces what's blocking; target rows progress READY / PARTIAL / DRAFTING / TARGETED.
- fix/dashboard rows + /projects cards no longer hardcode `<Badge>Draft</Badge>` on every entry. Both surfaces consume projectStatus() — data-project-status enum + truthful badge label + truthful `X / Y panels ready` sub-line. Cross-surface consistency spec asserts /dashboard / /projects / /projects/[id] all agree per project.
Screenshot Studio engine: ASOForge-style constrained pack builder replaces the Fabric.js freeform editor as the primary editing surface. /editor redirects to /studio.
- addcomponents/studio/* — Phase A engine: one project owns an ordered panel set (headline + subhead + screenshot + theme + layout + device class + frame style per panel). Constrained-by-design produces App Store-ready output without freeform layout decisions.
- addMulti-panel filmstrip on /studio with ordered selection, duplication, reordering, deletion, and bulk export naming. Each panel renders into the device frame at exact pixel dimensions on export.
- addStudio is now the default editing route — /projects/[id]/editor server-side redirects to /projects/[id]/studio. The legacy Fabric route is retired from the user path; lib/canvas/* stays as schema infrastructure that Studio reads/writes through.
- fixStudio device-class switch was a UI lie. Clicking iPhone 6.7 or iPad 13 left iPhone 6.9 visually selected; preview header + filmstrip metadata stuck on the old device. Added aria-pressed + aria-checked + data-active + role=radio markers + an active text-color flip so the selected state is unambiguous. Extracted the switch logic to a pure reducer (lib/studio/device-switch.ts) with 12 unit specs + 3 Playwright specs covering click → selected styling, filmstrip metadata, and selection persistence across page reload (autosave).
AI dispatch is real now — Copy + Backdrop both call live models behind a Trigger.dev task that owns the credit ledger transaction.
- addAI Copy module wired end-to-end: GPT-5 via Vercel AI SDK + Zod, dispatched as a Trigger.dev task, polled for completion.
- addAI Backdrop module wired end-to-end: gpt-image-1 6-frame composition, six art-direction presets (minimal-light, tactical-dark, warm-organic, playful-gradient, tech-minimal, editorial), result lands in Cloudflare R2.
- addStatus passthrough at /api/ai/runs/[runId] so the editor can poll any dispatched task.
- addSentry observability helper (lib/observability/log.ts) replaces every console.error in the API surface.
- addOG image fonts: Archivo Black + JetBrains Mono fetched from Google's CDN at edge runtime, rendered into every social card.
- fixEditor save path is now single-source — FabricCanvas's status bar is the only save UI; redundant disabled topbar Save removed.
- remPolotno scaffolding deleted (PolotnoProvider, PolotnoCanvas) — Fabric.js v7 is the locked canvas.
Purchase + project-create flows are functional in code but require Stripe price IDs and Trigger.dev secrets in Vercel before traffic can hit them.
- addStripe Checkout: /api/stripe/checkout POST + new PurchaseButton client island. Surfaces inline errors when Stripe price IDs aren't configured.
- add/api/projects POST creates real DB rows; new-project wizard COMMIT button now routes into the editor instead of 404-ing.
- add/api/ai/template-set migrated from synchronous route handler to a Trigger.dev task — closes the credit-ledger invariant violation flagged in audit pass 3.
- fixMobile horizontal scroll: html { overflow-x: clip } global guard + dropped whitespace-nowrap on the hero headline that overflowed below 375px.
Pre-launch hygiene + the work-in-progress banner. Site goes from 'looking ready' to 'visibly building'.
- addHazard-stripe WIP banner above every page — local-storage dismissable, version-keyed so we can re-arm it for major changes.
- addBuild-time Clerk live-key guard in next.config.ts: warns by default, fails the Vercel build when STRICT_LAUNCH_CHECKS=1.
- fixWordmark lockup splits SHOTS / HQ across six surfaces — Header, Footer, sticky CTA bar, auth chrome, sidebar, OG route.
- fixEvery unwired CTA in the logged-in app surfaces is now visibly disabled with 'coming soon' tooltips. No more silent no-ops.
Conversion audit pass — anti-AI-slop sweep, brand-respectful motion, and the /tools/web-hero landing.
- add/tools/web-hero designer landing for the surface that ships outside App Store.
- addScroll-reveal motion across marketing surfaces — IntersectionObserver-based, GPU-only transform + opacity, prefers-reduced-motion honored.
- fixTactical theme contrast: --fg-mute calibrated to 6.2:1 against #0A0A0A (was failing WCAG AA at 4.04:1).
- fixHero backdrop banding: quality bumped to 95, AVIF format priority, fractalNoise dither overlay to break 8-bit gradient banding.
Brand-extraction pipeline: paste a URL, the model reads the site's palette, typography, and voice and seeds it into the project.
- add/api/brand/extract endpoint backed by gpt-5 with Zod-validated BrandProfile schema.
- addSurface system: every project ships App Store + Web hero + OG card + Discord banner + Product Hunt gallery + GitHub social card from one canvas.
- perfPipeline diagram tracer animation: pure transform/opacity, runs at 60fps on iOS Safari without GPU thermal throttling.
Canvas + credit ledger landed. Trigger.dev task graph for the long-running AI work.
- addFabric.js v7 canvas integrated, state persisted to JSONB. (Polotno was evaluated and rejected — $899/mo not viable for v1.)
- addCredit ledger: append-only, idempotency-keyed, Stripe meter event reuses the same key.
- addTrigger.dev v3 tasks: ai-generate-copy, ai-restyle, batch-translate, render-screenshot, upload-to-app-store. Each task is the transaction boundary for debit + AI + meter.
Initial scaffold. Auth, DB, storage, payments wired before the first feature commit.
- addNext.js 16 App Router + Clerk auth + Neon Postgres (Drizzle) + Cloudflare R2 + Stripe Billing + sharp render pipeline.
- addDual theme system (Tactical / Swiss) keyed off CSS variables for instant runtime switching inside the app.
Roadmap
Next ship targetsNot shipped