Skip to content

PixelAI v2: full core rebuild (Next 16 + AI SDK 7, multi-provider engine, auth, quotas, history) - #9

Merged
Pycomet merged 36 commits into
mainfrom
v2-rebuild
Jul 12, 2026
Merged

Pycomet merged 36 commits into
mainfrom
v2-rebuild

Conversation

@Pycomet

@Pycomet Pycomet commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Wholesale rebuild of PixelAI per the approved design spec (docs/superpowers/specs/2026-07-12-pixelai-v2-core-redesign-design.md) and implementation plan (docs/superpowers/plans/2026-07-12-pixelai-v2-rebuild.md). The v1 app (Next 14 + NextUI + broken Firebase static-hosting deploy) is replaced end to end; v1 remains recoverable on main history.

What's new

  • Stack: Next.js 16 App Router, React 19, TS strict, Tailwind v4, shadcn/ui (Base UI), AI SDK v7, Vitest 4, Playwright
  • Generation engine: five-provider registry — OpenAI, Replicate, Fal (first-party AI SDK) + Stability, HuggingFace (custom ImageModelV4 adapters) — with a client-safe metadata split so no SDK code ships to the browser
  • Parallel fan-out: one request per provider, independent result cards, per-provider retry, generation-id guard against stale batches
  • Security: server-side Firebase ID-token verification on every mutating route; single triple-guarded dev bypass (dead-code-eliminated in prod builds); open-redirect-hardened next param; hostname-allowlisted YouTube import; sanitized { error: { code, message } } contract everywhere
  • Persistence: every generation stored to Vercel Blob + Firestore before the response; /history gallery with owner-scoped delete (no-existence-leak 404s); shared GenerationDoc type enforcing the write/read seam
  • Quotas: transactional 10/day free cap with lazy UTC reset, bootstrap-shape-constrained Firestore rules, negative-counter clamp
  • Feature-flagged: YouTube URL import + GPT-4o-mini title inference (FEATURE_YT_IMPORT)
  • Also fixed in passing: a latent production-breaking bug (dynamic process.env[key] access meant Firebase client init would always throw in prod browsers)

Process

17 plan tasks, each implemented by a fresh subagent with TDD and gated by an independent spec+quality review; final whole-branch review (verdict: ready to merge) with all Critical/Important findings fixed.

Test plan

  • 81 unit/route tests green (pnpm test)
  • 5/5 quota tests on Firestore emulator (pnpm test:quota, needs Java)
  • Playwright dashboard smoke (pnpm exec playwright test)
  • tsc --noEmit, lint, build green; CI workflow included
  • Post-deploy: set env vars on Vercel, deploy firestore.rules, create composite index (uid asc, createdAt desc), verify real-key generation end to end

🤖 Generated with Claude Code

Pycomet and others added 30 commits July 12, 2026 05:09
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Split single jsdom project into two explicit projects: node env for .test.ts
files (excluding hooks), jsdom for .test.tsx and hooks/**. Ensures all test
files are discovered and run in correct environment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements ImageModelV4-compliant adapter for HuggingFace Inference API router.
Follows Stability adapter patterns with POST to https://router.huggingface.co/hf-inference/models/{modelId}.
Includes full test coverage for successful generation and cold-start error handling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Strengthened blob storage tests to assert exact pathname and Buffer type for PNG,
and added new tests to cover webp and jpeg extension mappings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ng state

- Add safeNextPath() to prevent open redirect via unvalidated next query param
- Extract shared email/password validation to lib/auth/schemas.ts
- Add pending state to social sign-in buttons to prevent double-submission
- Covers all redirect paths: password sign-in, register, social providers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add DELETE /api/generations/:id route with auth and ownership checks
- Test coverage: 401 unauthenticated, 404 missing/unowned doc, 200 with blob deletion order
- Add GenerationGrid component for browsing owner-scoped generations (Firestore query newest-first, limit 50)
- Add /history page with AuthGate wrapper
- Update firestore.rules with users/ and generations/ access patterns
- Note: Client Firestore query needs composite index in production

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…llback

- Wrap DELETE route in try/catch with 502 provider_error on unexpected failures
- Add test asserting 404 responses are identical (no existence leak)
- Add test for deleteGeneratedImage rejection returning 502 with safe message
- Fix optimistic delete rollback to maintain createdAt desc sort order
- Add user guard at top of handleDelete

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implement landing page hero with prompt input routing to dashboard/login,
navigation bar with auth dropdown or sign-in button, and error boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rewrite README.md with complete PixelAI product description, stack, setup guide, scripts table, architecture sketch, and deployment notes
- Create .github/workflows/ci.yml with lint/test/build on push to main and v2-rebuild, pull_request; Node 22, pnpm 10, Firebase placeholders for build
- Skip vercel.json: maxDuration (55s) is set via route segment config; no additional deploy config needed
- Verified: pnpm lint, pnpm test, pnpm build all pass locally with placeholder Firebase values

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pycomet and others added 6 commits July 12, 2026 17:07
…r firebase client init

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implement YouTube video metadata fetching via oEmbed and AI-powered
title-to-thumbnail inference using gpt-4o-mini, both behind FEATURE_YT_IMPORT flag.

Routes:
- POST /api/youtube-metadata: fetch title/author from YouTube oEmbed
- POST /api/infer-from-title: infer thumbnail form fields from video title
- GET /api/providers: added featureYtImport flag to response

UI:
- YouTubeImport component: URL input, import button with error handling
- Generator: conditionally render import widget, wire prefill to ThumbnailForm
- ThumbnailForm: accept optional prefill prop with subject/style/emotion/overlay

Tests:
- youtube-metadata: auth, flag gating, URL validation, oEmbed integration
- infer-from-title: auth, flag gating, title length validation, generateObject mocking
- All 73 tests passing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…validation order

- Finding 1 (Critical): Replace regex-based YouTube URL extraction with hostname-anchored
  validation using URL parsing. Validate hostname against allowlist ["youtube.com",
  "www.youtube.com", "m.youtube.com", "youtu.be"] to prevent bypasses like
  evilyoutube.com or youtube.com.evil.com. Extract video ID from parsed URL based on
  hostname and path/search params; validate ID format as /^[A-Za-z0-9_-]{11}$/.

- Finding 2 (Critical): Fix oEmbed error handling contract. Map any non-ok response
  (not just 404) to 400 invalid_input "Video not found." Keep 502 only for fetch
  throws/timeouts.

- Finding 3 (Minor): Reorder infer-from-title validation to auth → flag → validate →
  config (OPENAI_API_KEY check). Ensures invalid input returns 400 before checking for
  missing API key.

- Finding 4 (Minor): Remove requestAnimationFrame wrapper in thumbnail-form prefill
  effect. React automatically batches setState calls within effects; simpler and avoids
  missing-cancel issue. Add eslint-disable for legitimate effect setState pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…negative quota

Firestore rules now restrict create on users/{uid} to only the exact bootstrap
payload: plan='free', quotaUsedToday=0, quotaResetAt=null. Prevents accounts from
bootstrapping with negative quotaUsedToday to gain unlimited generations.

Belt-and-suspenders clamp in consume.ts ensures any negative quota is treated as 0.
New test case verifies clamping behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
pixelai Ready Ready Preview, Comment Jul 12, 2026 5:13pm

@Pycomet
Pycomet merged commit 93a6de5 into main Jul 12, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant