Skip to content

Fix merge conflicts in PR #198#199

Closed
Jackson57279 wants to merge 53 commits intoremove-inngestfrom
tembo/fix-pr-198-merge-conflict
Closed

Fix merge conflicts in PR #198#199
Jackson57279 wants to merge 53 commits intoremove-inngestfrom
tembo/fix-pr-198-merge-conflict

Conversation

@Jackson57279
Copy link
Owner

@Jackson57279 Jackson57279 commented Dec 27, 2025

Summary

Fixed merge conflicts in PR #198 by resolving authentication migration from Clerk to Stack Auth, updating environment variables from OpenRouter to Vercel AI Gateway, and implementing comprehensive project improvements including:

  • Authentication: Migrated from Clerk to Stack Auth with Convex integration
  • AI Gateway: Updated from OpenRouter to Vercel AI Gateway (AI_GATEWAY_API_KEY, AI_GATEWAY_BASE_URL)
  • Database: Added new tables (e2bRateLimits, jobQueue, specs) and enhanced schema
  • Features: Added spec mode, job queue system, E2B rate limiting, and BotID protection
  • Documentation: Comprehensive rules in .cursor/rules/ and updated guides
  • Security: Added BotID protection for sensitive endpoints
  • Error Handling: Enhanced E2B error prevention with circuit breakers and retry logic

Want tembo to make any changes? Add a review or comment with @tembo and i'll get back to work!

tembo.io app.tembo.io

Summary by CodeRabbit

Release Notes

  • New Features

    • Spec Mode for code planning: review and approve AI-generated specifications before code generation.
    • Multi-framework support: Now supports React, Vue, Angular, Svelte, and Next.js.
    • Sandbox persistence: Automatic pause/resume for idle sandboxes to optimize resources.
    • Enhanced subscription dashboard: View plan status, daily credits, and billing details.
    • Project file downloads: Export all generated files as a ZIP archive.
    • Privacy & Terms pages: Added legal documentation.
  • Improvements

    • Improved error recovery and resilience for sandbox operations.
    • Better AI context awareness with image attachments.
    • Enhanced form validation and user feedback.
  • Bug Fixes

    • Fixed image loading issues from hosted services.
    • Resolved code export incompleteness for certain frameworks.

✏️ Tip: You can customize this high-level summary in your review settings.

… UI elements. Update message handling in Inngest functions.
…nt variable for project ID. Update handler and layout components to handle cases where Stack Auth is not configured.
…nvironment variables. Update ConvexClientProvider and handler components to handle cases where Stack Auth is not configured, improving overall error handling and application stability.
…including eslint-config-next, next, react, and react-dom, ensuring compatibility and improved features.
…uploads and public link inputs. Update API routes and UI components to streamline the import process, improving user experience and error handling.
…out and middleware. Update bun.lock and package.json to include new dependencies and version overrides for esbuild, enhancing project structure and authentication flow.
…ig from the proxy module, streamlining the codebase.
…ility with binary data, enhancing the import process.
… Update related components and API routes to utilize Clerk's user management and authentication flow, enhancing overall user experience and code consistency.
…ional skip mechanism based on user authentication state, improving performance and reducing unnecessary API calls.
…rocess. Update getToken function to handle potential null values more gracefully and improve integration with ConvexHttpClient by setting authentication conditionally based on token availability.
…including eslint-config-next, next, react, and react-dom, ensuring compatibility and improved features.
…R_DOMAIN and simplify error handling. Update FragmentWeb component to accept projectId prop and enhance file download logic with improved error handling and user feedback.
…webhook handling, and environment configuration

- Added Clerk Billing migration documentation
- Updated database schema to support Clerk-specific fields
- Replaced custom billing components with Clerk's PricingTable
- Implemented access control checks for Clerk plans
- Configured webhook handlers for subscription events
- Removed legacy Stripe-specific code and environment variables
- Created quick reference and setup checklist for Clerk Billing
- Enhanced prompt API for better user experience
- Implemented webhook processing for Clerk events
Jackson57279 and others added 19 commits December 14, 2025 05:07
…iltering (#165)

Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
…dations (#166)

Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
Co-authored-by: tembo[bot] <208362400+tembo[bot]@users.noreply.github.com>
… components for improved speed-critical task handling
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: dogesman098 <dogesman098@gmail.com>
… documentation and codebase

- Updated environment variables and configuration references from `AI_GATEWAY_API_KEY` and `AI_GATEWAY_BASE_URL` to `OPENROUTER_API_KEY` and `OPENROUTER_BASE_URL`.
- Modified related documentation, examples, and test scripts to reflect the new API integration.
- Adjusted model selection logic to favor updated model identifiers for improved performance.
…ebhook route

- Removed 'revoked' status from SubscriptionStatus type.
- Mapped 'revoked' status to 'canceled' in the status mapping function for consistency.
- Changed `polarSubscriptionId` to `clerkSubscriptionId` for consistency across subscription operations.
- Enhanced metadata to include `polarCustomerId` and a new `source` field set to "polar".
- Updated related logging messages to reflect the changes.
…rFixFunction

- Added logic to enforce the use of the Z.AI provider for the GLM model (z-ai/glm-4.7) in both codeAgentFunction and errorFixFunction.
- Updated provider configuration to prevent fallbacks, ensuring consistent model routing through OpenRouter.
- Simplified sandbox URL retrieval logic by skipping the `getHost()` method during code generation, using a fallback URL format instead.
- Enhanced error handling for port-based URL retrieval, with improved logging for debugging purposes.
- Updated console messages to reflect the changes in sandbox URL handling.
@vercel
Copy link

vercel bot commented Dec 27, 2025

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

Project Deployment Review Updated (UTC)
zapdev Error Error Dec 27, 2025 4:31am

@codecapyai
Copy link

codecapyai bot commented Dec 27, 2025

❌ Error while analyzing PR and generating tests:

Could not parse response content as the length limit was reached - CompletionUsage(completion_tokens=325, prompt_tokens=199675, total_tokens=200000, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=325, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 27, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

This PR introduces comprehensive platform enhancements including E2B error prevention with rate limiting and job queues, Clerk Billing migration support, Spec Mode for code generation planning workflows, bot protection across API routes, Figma direct import functionality, new Convex data models, and extensive developer documentation.

Changes

Cohort / File(s) Summary
Documentation & Rules
.cursor/rules/*, explanations/*, audit/*, AGENTS.md, BLACKBOX.md, TODO.md, CLAUDE.md, README_*, START_HERE.md, various guides
Adds comprehensive developer documentation covering Convex migration, Clerk Billing setup, E2B error prevention, Sandbox persistence, Spec Mode implementation, Better Auth migration, SEO/AI optimization, and Cursor IDE rules. Includes audit reports, checklists, and quick-start guides.
Clerk Billing Migration
CLERK_BILLING_*.md, env.example, convex/subscriptions.ts, src/app/api/webhooks/clerk/route.ts
Introduces Clerk Billing integration with new webhook handler for subscription lifecycle, adds revokeAllUserSubscriptions mutation, updates environment variables from Stripe to Clerk, and provides comprehensive migration guides.
E2B Error Prevention & Rate Limiting
convex/e2bRateLimits.ts, convex/jobQueue.ts, convex/schema.ts (new tables), src/inngest/circuit-breaker.ts
Adds new Convex modules for E2B rate limit tracking (recordRequest, checkRateLimit, getStats) and job queue management with priority-based processing, including schema tables with optimized indexes.
Spec Mode Feature
convex/specs.ts, convex/schema.ts (specMode fields), convex/messages.ts (selectedModel), src/app/api/*
Implements spec/planning mode for code generation with mutations (updateSpec, approveSpec, rejectSpec, getSpec), extends message schema with specMode/specContent/selectedModel fields, and adds supportive documentation.
API Route Security & Bot Protection
src/app/api/**/*route.ts (auth, import, webhooks, agent)
Systematically adds bot verification via checkBotId across routes, introduces dynamic route exports, implements Convex client auth patterns, and strengthens error handling. Affects: agent/generate, agent/token, enhance-prompt, fix-errors, import/, messages/update, polar/, transfer-sandbox, webhooks, download routes.
Figma Import Enhancement
src/app/api/import/figma/direct/route.ts, src/inngest/functions/process-figma-direct.ts, convex/schema.ts (imports table reference)
Adds direct Figma file import capability with new API route, Inngest processor function, design system extraction, and message creation workflow.
Authentication & Configuration
src/app/handler/[...stack]/page.tsx, src/app/layout.tsx, proxy.ts, next.config.mjs, eslint.config.mjs, package.json
Migrates StackServerApp to pre-initialized import, adds Clerk middleware proxy, wraps Next.js config with BotID support, updates ESLint to use createRequire, and refreshes dependencies (removes @inngest/agent-kit, adds AI SDK packages, Playwright, etc.).
Convex Helpers & Schema
convex/helpers.ts, convex/messages.ts, convex/projects.ts, convex/usage.ts
Updates hasProAccess to accept optional userId parameter, refactors message attachment handling (addAttachmentForUser), changes getCurrentUserId pattern in projects, and passes userId to pro access checks.
Home & Subscription Pages
src/app/(home)/page-content.tsx, src/app/(home)/page.tsx, src/app/(home)/subscription/page.tsx, src/app/(home)/pricing/page-content.tsx
Adds new PageContent client component with confetti celebration on subscription, implements subscription dashboard with plan display and usage, adds Polar config validation with graceful degradation.
AI & SEO Pages
src/app/ai-info/page.tsx, src/app/privacy/page.tsx, src/app/terms/page.tsx, src/app/sitemap.ts
Adds machine-readable AI info page with structured data, comprehensive Privacy Policy and Terms of Service pages, and updates sitemap with new routes.
Admin & Dashboard
src/app/dashboard/admin/e2b-health/page.tsx, src/app/dashboard/subscription/page.tsx
Introduces E2B health monitoring dashboard showing circuit breaker, rate limits, and queue stats; updates subscription dashboard with regex-based pro access detection.
Code View & File Explorer
src/components/code-view/index.tsx, src/components/file-explorer.tsx
Replaces global Prism highlighting with per-element ref-based highlighting; adds download functionality to file explorer with fragmentId and allFiles props.
Utilities & Scripts
mcp/playwright-server.ts, check_zod.js, build_output.txt
Adds MCP server backed by Playwright with page navigation/snapshot/review tools; includes Zod type checking utility and build output logging.
Environment & Git
.gitignore, env.example, public/robots.txt
Adds .env*.local ignore pattern, restructures env.example for Clerk/OpenRouter/Convex, and enhances robots.txt with AI-specific crawling rules and agent allowances.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant Browser
    participant API
    participant Convex
    participant E2B
    participant Inngest

    rect rgb(200, 220, 255)
    Note over User,Inngest: Spec Mode Workflow
    User->>Browser: Submit code generation request
    Browser->>API: POST /api/agent/generate (mode='spec')
    API->>Convex: Create message with specMode=PLANNING
    Convex-->>API: Return messageId
    API->>Inngest: Trigger specPlanningAgentFunction
    Inngest->>E2B: Create sandbox & run planning
    E2B-->>Inngest: Return spec content
    Inngest->>Convex: Update message (specMode=AWAITING_APPROVAL, specContent=...)
    Convex-->>Inngest: Success
    Inngest-->>API: Complete with spec
    API-->>Browser: Stream progress, show spec for approval
    end

    rect rgb(220, 255, 220)
    Note over User,Convex: Spec Approval & Code Generation
    User->>Browser: Approve spec
    Browser->>API: POST /api/specs/approve
    API->>Convex: approveSpec (specMode=APPROVED)
    Convex-->>API: Return approved spec
    API->>Inngest: Trigger codeAgentFunction with approved spec injected in prompt
    Inngest->>E2B: Generate code using spec constraints
    E2B-->>Inngest: Code files
    Inngest->>Convex: Create fragment with generated files
    Convex-->>Inngest: fragmentId
    Inngest-->>API: Complete
    API-->>Browser: Stream result with spec and generated code
    end

    rect rgb(255, 220, 220)
    Note over API,Inngest: E2B Rate Limit & Job Queue
    API->>Convex: recordRequest('sandbox.create')
    Convex->>Convex: Check if exceeded rate limit
    alt Rate limit exceeded
        Convex-->>API: exceeded=true
        API->>Inngest: Queue job (priority=high, status=PENDING)
        Inngest->>Inngest: getNextJob() retrieves queued jobs
        Inngest->>E2B: Create sandbox when capacity available
    else Within limit
        API->>E2B: Create sandbox immediately
        E2B-->>API: sandboxId
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • changes #159: Overlapping migration work across same core infrastructure files (eslint.config.mjs, package.json, authentication routes, API handlers), suggesting coordinated refactoring of build configuration and auth patterns.

Poem

🐰 Whiskers twitch with joy today—
Specs to plan, rate limits sway,
Billing flows through Clerk's domain,
Figma imports, bots refrain!
E2B dreams with circuit dreams,
ZapDev flows in schemas' streams!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.34% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix merge conflicts in PR #198' accurately describes the primary change in the pull request, which is resolving merge conflicts from PR #198.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tembo/fix-pr-198-merge-conflict

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 and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +114 to +118
"google/gemini-3-pro": {
name: "Gemini 3 Pro",
provider: "google",
description:
"Google's most intelligent model with state-of-the-art reasoning",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add missing prime-intellect model to MODEL_CONFIGS

MODEL_CONFIGS still ends at the Gemini entry and does not register prime-intellect/intellect-3, but the updated tests/model-selection.test.ts now asserts that this model exists. As-is, the test suite will fail and any request specifying that model will be rejected as unknown. Please add the missing model configuration so selection and tests stay in sync.

Useful? React with 👍 / 👎.

Comment on lines 178 to +181
);

if (hasCodingFocus && !isVeryLongPrompt) {
chosenModel = "alibaba/qwen3-max";
chosenModel = "moonshotai/kimi-k2-0905";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Coding prompts still route to Kimi instead of Gemini

The selection branch for coding-focused prompts still assigns moonshotai/kimi-k2-0905 when the prompt is not very long, but the updated tests now expect Gemini to be preferred for these refinement cases. This mismatch will cause the new prefers Gemini for coding-focused refinements test to fail and production traffic will continue using the old model. Update this branch to return the intended Gemini model to align with the test and rollout.

Useful? React with 👍 / 👎.

@Jackson57279
Copy link
Owner Author

@greptileai

@Jackson57279
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 27, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/app/api/webhooks/polar/route.ts (1)

92-107: Avoid as any cast for subscription status.

Using as any on line 98 bypasses TypeScript's type checking, which conflicts with the strict TypeScript guidelines. Consider mapping the Polar status to your expected union type explicitly.

🔎 Proposed fix
+        // Map Polar subscription status to our status type
+        const mappedStatus = ((): "incomplete" | "active" | "canceled" | "past_due" | "unpaid" => {
+          switch (subscription.status) {
+            case "active":
+            case "trialing":
+              return "active";
+            case "canceled":
+              return "canceled";
+            case "past_due":
+              return "past_due";
+            case "unpaid":
+              return "unpaid";
+            default:
+              return "incomplete";
+          }
+        })();
+
         // Sync subscription to Convex
         await convex.mutation(api.subscriptions.createOrUpdateSubscription, {
           userId,
           polarCustomerId: subscription.customerId,
           polarSubscriptionId: subscription.id,
           productId: subscription.productId,
           productName,
-          status: subscription.status as any,
+          status: mappedStatus,
src/app/api/import/figma/callback/route.ts (1)

24-26: Remove dead code block.

This if (false) block is unreachable dead code and should be removed.

🔎 Proposed fix
-  if (false) {
-    return NextResponse.redirect(new URL("/", request.url));
-  }
-
   const { searchParams } = new URL(request.url);
env.example (1)

35-41: Clarify OAuth provider configuration.

The environment file includes OAuth credentials for Google, GitHub, and Figma (lines 36-41), but the AI summary indicates "Google OAuth remains as the sole OAuth provider." This inconsistency could confuse developers about which OAuth providers are actually supported.

Consider either:

  1. Removing unused OAuth provider placeholders if only Google is supported
  2. Updating documentation to reflect that multiple OAuth providers are available
♻️ Duplicate comments (2)
src/inngest/functions.ts (2)

84-122: Verify MODEL_CONFIGS includes all models referenced in tests.

As noted in a previous review, if tests reference prime-intellect/intellect-3, this model configuration is still missing from MODEL_CONFIGS. Please verify that all models used in tests are registered in this configuration object.


168-182: Model selection mismatch with test expectations.

As flagged in a previous review, the selection logic for coding-focused prompts still assigns moonshotai/kimi-k2-0905 (line 181), but tests may expect Gemini to be preferred for coding refinements. This mismatch will cause test failures. Please align the model selection logic with test expectations.

🟠 Major comments (20)
src/app/api/webhooks/clerk/route.ts-85-85 (1)

85-85: Remove as any type assertion.

This bypasses type safety. With the interface suggested earlier, you can use proper type narrowing or type guards instead.

🔎 Proposed fix (after adding ClerkSubscriptionData interface)
-        const subscription = evt.data as any;
+        const subscription = evt.data as ClerkSubscriptionData;

As per coding guidelines, strict TypeScript usage is required.

Committable suggestion skipped: line range outside the PR's diff.

src/app/api/webhooks/clerk/route.ts-61-62 (1)

61-62: Validate NEXT_PUBLIC_CONVEX_URL before use.

The non-null assertion assumes the environment variable exists. If missing, this will cause a runtime error with an unclear message. Apply the same validation pattern used for CLERK_WEBHOOK_SECRET.

🔎 Proposed fix
   // Initialize Convex client
+  const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL;
+  if (!convexUrl) {
+    throw new Error(
+      "Please add NEXT_PUBLIC_CONVEX_URL to .env or .env.local"
+    );
+  }
-  const convex = new ConvexHttpClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
+  const convex = new ConvexHttpClient(convexUrl);
src/app/api/webhooks/clerk/route.ts-124-124 (1)

124-124: Remove as any type assertion.

Same issue as Line 85. Use the proper interface type instead.

🔎 Proposed fix
-        const subscription = evt.data as any;
+        const subscription = evt.data as ClerkSubscriptionData;

Committable suggestion skipped: line range outside the PR's diff.

src/app/api/webhooks/clerk/route.ts-9-13 (1)

9-13: Avoid any type; define proper interfaces for Clerk Billing events.

Using any for subscription event data bypasses TypeScript's type safety. Define explicit interfaces for the subscription payload to catch type errors at compile time and improve code maintainability.

🔎 Proposed type definitions
+interface ClerkSubscriptionData {
+  id: string;
+  user_id?: string;
+  userId?: string;
+  plan_id?: string;
+  planId?: string;
+  plan_name?: string;
+  planName?: string;
+  status: string;
+  current_period_start?: number;
+  current_period_end?: number;
+  cancel_at_period_end?: boolean;
+  features?: string[];
+  metadata?: Record<string, unknown>;
+}
+
 type ClerkBillingEvent = 
   | WebhookEvent 
-  | { type: "subscription.created"; data: any }
-  | { type: "subscription.updated"; data: any }
-  | { type: "subscription.deleted"; data: any };
+  | { type: "subscription.created"; data: ClerkSubscriptionData }
+  | { type: "subscription.updated"; data: ClerkSubscriptionData }
+  | { type: "subscription.deleted"; data: ClerkSubscriptionData };

As per coding guidelines, strict TypeScript usage is required.

convex/subscriptions.ts-218-222 (1)

218-222: Add returns validator to the mutation.

The mutation is missing a returns validator. Per coding guidelines, all Convex functions must include both argument and return validators.

🔎 Proposed fix
 export const revokeAllUserSubscriptions = mutation({
   args: {
     userId: v.string(),
   },
+  returns: v.object({
+    revokedCount: v.number(),
+  }),
   handler: async (ctx, args) => {

As per coding guidelines, ALWAYS include argument and return validators for all Convex functions.

src/app/api/webhooks/polar/route.ts-33-40 (1)

33-40: Remove or redact sensitive data from logs.

Logging all headers (line 35) exposes sensitive information including webhook signatures, authorization tokens, and potentially cookies. The body preview (line 40) may also contain PII such as customer emails or payment details. This level of logging is appropriate for local debugging but poses a compliance/privacy risk in production.

🔎 Proposed fix to redact sensitive data
     console.log("🔔 Webhook POST request received at /api/webhooks/polar");
-    console.log("URL:", request.url);
-    console.log("Headers:", Object.fromEntries(request.headers.entries()));
-    
+    console.log("URL:", request.url);
+
     // Get the raw body for signature verification
     const body = await request.text();
-    console.log("Body length:", body.length);
-    console.log("Body preview:", body.substring(0, 200));
+    console.log("Body length:", body.length);

If debugging is needed, consider using a feature flag or NODE_ENV check to enable verbose logging only in development.

CLERK_BILLING_QUICK_REFERENCE.md-1-239 (1)

1-239: Move documentation file to explanations/ folder.

Per coding guidelines, documentation files should be placed in the explanations/ folder, not the root directory.

As per coding guidelines, store .md documentation files in @/explanations/ directory.

CLERK_BILLING_MIGRATION.md-1-75 (1)

1-75: Move documentation file to explanations/ folder.

Per coding guidelines, documentation files should be placed in the explanations/ folder, not the root directory. Only core setup files (CLAUDE.md, README.md) should remain in root.

#!/bin/bash
# Suggested: Move file to explanations/ folder
# mv CLERK_BILLING_MIGRATION.md explanations/CLERK_BILLING_MIGRATION.md

As per coding guidelines, store .md documentation files in @/explanations/ directory.

CLERK_BILLING_MIGRATION_SUMMARY.md-1-208 (1)

1-208: Relocate documentation to explanations/ folder per coding guidelines.

This comprehensive Clerk Billing migration documentation should be moved to the explanations/ folder to align with the project's coding guidelines, which specify that documentation files should be placed in explanations/ rather than the root directory.

As per coding guidelines: *.md: Documentation files should be placed in explanations/ folder, not in the root directory.

🔎 Suggested relocation

Move this file to:

explanations/CLERK_BILLING_MIGRATION_SUMMARY.md

Update any references to this file path in related documentation.

mcp/playwright-server.ts-11-23 (1)

11-23: Consider resource cleanup and concurrency issues.

The singleton pattern for browser and page instances has potential issues:

  1. Resource leaks: No cleanup mechanism if the process crashes or exits unexpectedly.
  2. Concurrency conflicts: Multiple concurrent tool invocations could interfere with each other using the shared page instance.
🔎 Recommended improvements

Consider adding:

  1. Graceful shutdown handler:
process.on('SIGTERM', async () => {
  if (browser) {
    await browser.close();
  }
  process.exit(0);
});
  1. Per-request page contexts or document the single-threaded assumption:
// Document that this server handles requests serially
// Multiple concurrent requests will share the same page instance

Alternatively, create a new page context per request if concurrent access is expected.

convex/e2bRateLimits.ts-73-103 (1)

73-103: Missing return validator for getStats query.

Per coding guidelines, return validators are required for all Convex functions.

🔎 Add return validator
 export const getStats = query({
   args: {},
+  returns: v.object({
+    totalRequests: v.number(),
+    byOperation: v.record(v.string(), v.number()),
+    timeWindow: v.string(),
+    timestamp: v.number(),
+  }),
   handler: async (ctx) => {
convex/e2bRateLimits.ts-109-137 (1)

109-137: cleanup should be internalMutation and needs return validator.

This cleanup function should not be exposed to the public Internet. A malicious actor could repeatedly call it. Per coding guidelines and learnings, use internalMutation for private/scheduled functions.

🔎 Make cleanup internal and add return validator
-export const cleanup = mutation({
+export const cleanup = internalMutation({
   args: {},
+  returns: v.object({
+    deletedCount: v.number(),
+    totalOldRecords: v.number(),
+    timestamp: v.number(),
+  }),
   handler: async (ctx) => {
src/app/api/projects/[projectId]/download/route.ts-49-54 (1)

49-54: Handle null return from api.projects.get to prevent unauthorized access.

The api.projects.get query enforces ownership checks internally by returning null if the user doesn't own the project (convex/projects.ts:296-298). However, the download route discards this result without checking if it's null, allowing it to proceed to call api.messages.list even for unauthorized projects. Add an explicit check: if api.projects.get returns null, return a 403 Forbidden response before attempting to list messages.

src/inngest/functions.ts-1517-1576 (1)

1517-1576: Dev server startup introduces timeout and resource management risks.

The implementation starts a dev server in the background (line 1528) and polls for up to 30 seconds for readiness (lines 1531-1551). This introduces several concerns:

  1. Timeout risk: The 30-second wait could cause Inngest function timeouts, especially if multiple operations are performed in sequence.
  2. Resource leak: The background dev server process continues running with no explicit cleanup/termination logic, potentially causing resource exhaustion and unexpected E2B billing.
  3. Unreliable fallback: If the server fails to start but the fallback URL is returned anyway (lines 1553-1556), the URL won't actually work for the user.

Consider alternatives such as:

  • Pre-warming dev servers outside the critical path
  • Using static preview URLs that don't require dev servers
  • Implementing explicit cleanup in a finally block or separate cleanup job
src/inngest/functions.ts-479-492 (1)

479-492: Remove dev server startup from E2B sandbox execution.

The getDevServerCommand function is used at lines 1517–1576 to start dev servers in E2B sandboxes. This violates established guidelines: E2B sandboxes should only run bun run lint and bun run build for validation, never start dev servers. Dev servers can cause resource exhaustion, timeouts, and exceed sandbox execution limits.

Remove the dev server startup logic and rely only on build and lint validation.

src/app/api/import/figma/direct/route.ts-51-63 (1)

51-63: Avoid any type assertion; use proper typing for Convex API.

The (api as any) assertion violates the TypeScript strict mode guideline. If imports.createImport is a valid mutation, it should be properly typed.

🔎 Proposed fix
-    const importId = await fetchMutation((api as any).imports.createImport, {
+    const importId = await fetchMutation(api.imports.createImport, {
       projectId,
       source: "FIGMA",
       sourceId,
       sourceName,
       sourceUrl,
       metadata: {
         inputType: fileBase64 ? "file" : "link",
         fileName,
         fileSize,
         figmaUrl: figmaUrl || undefined,
       },
     });

If api.imports.createImport doesn't exist in the generated types, ensure the Convex schema and functions are properly defined and regenerate types with npx convex dev.

As per coding guidelines, strict TypeScript usage should avoid using any type.

src/app/privacy/page.tsx-61-62 (1)

61-62: Update authentication provider reference to match current implementation.

The privacy policy references "Clerk" for authentication (lines 61 and 132), but the PR summary indicates migration to Stack Auth. This inconsistency could create compliance issues.

🔎 Proposed fix
-              <li>Authentication credentials (managed securely through Clerk)</li>
+              <li>Authentication credentials (managed securely through Stack Auth)</li>
-              <li><strong>Authentication:</strong> Clerk (for secure user authentication)</li>
+              <li><strong>Authentication:</strong> Stack Auth (for secure user authentication)</li>

Also applies to: 132-132

src/app/api/import/figma/direct/route.ts-37-45 (1)

37-45: Add file size validation to prevent memory issues with large uploads.

Large .fig files could exhaust server memory when loaded entirely into a Buffer. Consider adding a size limit check before processing.

🔎 Proposed fix
+    const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB limit
+
     if (file) {
       fileName = file.name;
       fileSize = file.size;
+      if (fileSize > MAX_FILE_SIZE) {
+        return NextResponse.json(
+          { error: `File too large. Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB` },
+          { status: 400 }
+        );
+      }
       if (!fileName.toLowerCase().endsWith(".fig")) {
         return NextResponse.json({ error: "Only .fig files are supported" }, { status: 400 });
       }
       const fileBuffer = Buffer.from(await file.arrayBuffer());
       fileBase64 = fileBuffer.toString("base64");
     }
convex/jobQueue.ts-82-93 (1)

82-93: Avoid using .filter() in Convex queries; use indexes instead.

Per coding guidelines, do NOT use filter in queries; instead define an index in the schema and use withIndex for query filtering. The current implementation filters in-memory which is inefficient.

🔎 Proposed fix

Add a composite index in schema.ts:

// In convex/schema.ts, add to jobQueue indexes:
.index("by_userId_status", ["userId", "status"])

Then update the query:

 export const getUserJobs = query({
   args: {
     userId: v.string(),
   },
+  returns: v.array(v.any()), // Add proper return type
   handler: async (ctx, args) => {
-    return await ctx.db
+    // Get all non-completed jobs - need to query multiple statuses
+    const pending = await ctx.db
       .query("jobQueue")
-      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
-      .filter((q) => q.neq(q.field("status"), "COMPLETED"))
+      .withIndex("by_userId_status", (q) => 
+        q.eq("userId", args.userId).eq("status", "PENDING")
+      )
       .collect();
+    const processing = await ctx.db
+      .query("jobQueue")
+      .withIndex("by_userId_status", (q) => 
+        q.eq("userId", args.userId).eq("status", "PROCESSING")
+      )
+      .collect();
+    const failed = await ctx.db
+      .query("jobQueue")
+      .withIndex("by_userId_status", (q) => 
+        q.eq("userId", args.userId).eq("status", "FAILED")
+      )
+      .collect();
+    return [...pending, ...processing, ...failed];
   },
 });

The same issue applies to getProjectJobs (lines 98-109) and cleanup (lines 248-260). Based on coding guidelines.

Committable suggestion skipped: line range outside the PR's diff.

src/app/api/agent/generate/route.ts-216-223 (1)

216-223: Potential memory leak in serverless environment with unbounded cache.

SANDBOX_CACHE is a module-level Map that grows without bounds. While clearCacheEntry schedules deletion after CACHE_EXPIRY, there's no maximum size limit. In serverless environments with container reuse, or under high load, this could lead to memory pressure.

🔎 Suggested bounded cache implementation
 const SANDBOX_CACHE = new Map<string, Sandbox>();
 const CACHE_EXPIRY = 5 * 60 * 1000;
+const MAX_CACHE_SIZE = 50;

 function clearCacheEntry(sandboxId: string) {
   setTimeout(() => {
     SANDBOX_CACHE.delete(sandboxId);
   }, CACHE_EXPIRY);
 }

+function addToCacheWithEviction(sandboxId: string, sandbox: Sandbox) {
+  // Evict oldest entries if at capacity
+  if (SANDBOX_CACHE.size >= MAX_CACHE_SIZE) {
+    const firstKey = SANDBOX_CACHE.keys().next().value;
+    if (firstKey) SANDBOX_CACHE.delete(firstKey);
+  }
+  SANDBOX_CACHE.set(sandboxId, sandbox);
+  clearCacheEntry(sandboxId);
+}

Committable suggestion skipped: line range outside the PR's diff.

🟡 Minor comments (21)
build_output.txt-1-2 (1)

1-2: Remove accidentally committed build artifact and add to .gitignore.

This file contains a Windows TIMEOUT command error from a local development machine and should not be in version control. The CI pipeline runs successfully on Ubuntu despite this artifact's presence.

Remove build_output.txt from this commit and add build_output.txt to .gitignore to prevent similar accidents. While this is a housekeeping issue rather than a build blocker, it should be cleaned up before merging.

src/app/api/webhooks/clerk/route.ts-56-56 (1)

56-56: Fix typo: "occured" → "occurred".

🔎 Proposed fix
-    return new Response("Error occured", {
+    return new Response("Error occurred", {
src/app/api/webhooks/clerk/route.ts-33-33 (1)

33-33: Fix typo: "occured" → "occurred".

🔎 Proposed fix
-    return new Response("Error occured -- no svix headers", {
+    return new Response("Error occurred -- no svix headers", {
src/app/api/webhooks/polar/route.ts-56-61 (1)

56-61: Avoid exposing internal error details in the response.

Returning String(err) in the response body (line 59) may leak internal implementation details to callers. The server-side logging is sufficient for debugging; the client response should remain generic.

🔎 Proposed fix
       console.error("❌ Webhook signature verification failed:", err);
       console.error("Error details:", JSON.stringify(err, null, 2));
       return NextResponse.json(
-        { error: "Invalid webhook signature", details: String(err) },
+        { error: "Invalid webhook signature" },
         { status: 401 }
       );
src/app/api/webhooks/polar/route.ts-200-227 (1)

200-227: Inconsistent status mapping and potential date parsing issue.

  1. The status mapping on line 202 only handles "trialing" but the type assertion on line 212 assumes all other statuses are valid. If Polar sends an unexpected status, this could cause issues downstream.

  2. The instanceof Date checks (lines 213, 216) may fail if the SDK returns ISO strings instead of Date objects, leading to incorrect fallback values.

🔎 Proposed fix for robust date handling
-            currentPeriodStart: sub.currentPeriodStart instanceof Date
-              ? sub.currentPeriodStart.getTime()
-              : Date.now(),
-            currentPeriodEnd: sub.currentPeriodEnd instanceof Date
-              ? sub.currentPeriodEnd.getTime()
-              : Date.now() + 30 * 24 * 60 * 60 * 1000,
+            currentPeriodStart: sub.currentPeriodStart
+              ? new Date(sub.currentPeriodStart).getTime()
+              : Date.now(),
+            currentPeriodEnd: sub.currentPeriodEnd
+              ? new Date(sub.currentPeriodEnd).getTime()
+              : Date.now() + 30 * 24 * 60 * 60 * 1000,

This handles both Date objects and ISO strings consistently.

src/app/api/webhooks/polar/route.ts-231-238 (1)

231-238: Error swallowing may hide actual failures.

The catch block on lines 236-238 logs a benign message for any error, including actual failures (e.g., network issues, mutation errors). Consider distinguishing between "not found" and other error types.

🔎 Proposed fix
         if (activeSubscriptions.length === 0) {
-          try {
-            await convex.mutation(api.subscriptions.revokeAllUserSubscriptions, {
-              userId: externalId,
-            });
-            console.log(`Revoked all subscriptions for user ${externalId} (no active subscriptions)`);
-          } catch (err) {
-            console.log(`No subscriptions to revoke for user ${externalId}`);
-          }
+          await convex.mutation(api.subscriptions.revokeAllUserSubscriptions, {
+            userId: externalId,
+          });
+          console.log(`Revoked all subscriptions for user ${externalId} (no active subscriptions)`);
         }

If the mutation is designed to be a no-op when there are no subscriptions, the try-catch is unnecessary. If it throws on "not found," handle that specific case rather than swallowing all errors.

src/app/api/webhooks/polar/route.ts-65-69 (1)

65-69: Validate environment variable and avoid logging event data.

The non-null assertion on NEXT_PUBLIC_CONVEX_URL (line 65) will cause a runtime exception if the variable is undefined. Additionally, the event data preview (line 69) may contain customer PII.

🔎 Proposed fix
     // Initialize Convex client
+    if (!process.env.NEXT_PUBLIC_CONVEX_URL) {
+      console.error("Missing NEXT_PUBLIC_CONVEX_URL environment variable");
+      return NextResponse.json(
+        { error: "Server configuration error" },
+        { status: 500 }
+      );
+    }
-    const convex = new ConvexHttpClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
+    const convex = new ConvexHttpClient(process.env.NEXT_PUBLIC_CONVEX_URL);

     console.log("✅ Polar webhook event received:", event.type);
-    console.log("Event timestamp:", event.timestamp);
-    console.log("Event data preview:", JSON.stringify(event.data).substring(0, 200) + "...");
+    console.log("Event timestamp:", event.timestamp);
mcp/playwright-server.ts-38-44 (1)

38-44: URL normalization may produce unexpected results.

The fallback behavior when URL parsing fails prepends https:// to the entire raw string, which may not work correctly for partial URLs or those with schemes other than http(s).

🔎 Example edge case
// Current behavior:
normalizeUrl("example.com")  "https://example.com" ✓
normalizeUrl("ftp://example.com")  throws, then "https://ftp://example.com" 

Consider validating that the input is either a valid URL or a bare domain:

function normalizeUrl(raw: string): string {
  // If already has a scheme, validate and return
  if (raw.includes('://')) {
    return new URL(raw).toString();
  }
  // Otherwise, treat as bare domain and add https
  return new URL(`https://${raw}`).toString();
}
BLACKBOX.md-1-532 (1)

1-532: Consider moving BLACKBOX.md to explanations/ folder.

According to coding guidelines: "Documentation files should be placed in explanations/ folder, not in the root directory" with exceptions only for CLAUDE.md, README.md, and AGENTS.md. BLACKBOX.md is not in the exception list and should be moved to maintain consistency with the documented guidelines.

🔎 Suggested relocation
# Move the file to explanations/
git mv BLACKBOX.md explanations/BLACKBOX.md

Update any references to this file in other documentation to reflect the new path.

As per coding guidelines and the file's own statement at line 33 about documentation placement.

convex/e2bRateLimits.ts-40-68 (1)

40-68: Bug in resetAt calculation and missing return validator.

The resetAt calculation at line 65 is incorrect. hourAgo + 60 * 60 * 1000 always equals now, which doesn't represent when the oldest record expires. To calculate when the rate limit resets, you need the timestamp of the oldest request in the window.

Also, the return validator is missing per coding guidelines.

🔎 Fix resetAt calculation and add return validator
 export const checkRateLimit = query({
   args: {
     operation: v.string(),
     maxPerHour: v.number(),
   },
+  returns: v.object({
+    count: v.number(),
+    limit: v.number(),
+    exceeded: v.boolean(),
+    remaining: v.number(),
+    resetAt: v.number(),
+  }),
   handler: async (ctx, args) => {
     const now = Date.now();
     const hourAgo = now - 60 * 60 * 1000;

     const recentRequests = await ctx.db
       .query("e2bRateLimits")
       .withIndex("by_operation_timestamp", (q) =>
         q.eq("operation", args.operation).gt("timestamp", hourAgo)
       )
       .collect();

     const count = recentRequests.length;
     const exceeded = count >= args.maxPerHour;
     const remaining = Math.max(0, args.maxPerHour - count);

+    // Calculate when the oldest request expires (rate limit resets)
+    const oldestTimestamp = recentRequests.length > 0
+      ? Math.min(...recentRequests.map(r => r.timestamp))
+      : now;
+    const resetAt = oldestTimestamp + 60 * 60 * 1000;

     return {
       count,
       limit: args.maxPerHour,
       exceeded,
       remaining,
-      resetAt: hourAgo + 60 * 60 * 1000,
+      resetAt,
     };
   },
 });
src/inngest/functions/process-figma-direct.ts-75-94 (1)

75-94: Empty URL may cause downstream issues.

At line 83, url: figmaUrl || "" sets an empty string when figmaUrl is undefined. This could cause validation issues or confusing data in the attachments table. Consider making the URL optional or using a placeholder that's clearly identifiable.

🔎 Use a more explicit placeholder or handle optionally
           attachments: [
             {
-              url: figmaUrl || "",
+              url: figmaUrl || "file://local-upload",
               size: 0,
               importId,
convex/e2bRateLimits.ts-7-35 (1)

7-35: Missing return validator and consider using internalMutation.

Per coding guidelines, all Convex functions must include argument AND return validators. Additionally, recordRequest modifies rate limit data and should likely be an internal function not exposed to the public Internet. Based on learnings, use internalMutation for private functions that are not part of the public API.

🔎 Add return validator and make internal
-import { mutation, query, internalMutation } from "./_generated/server";
+import { mutation, query, internalMutation, internalQuery } from "./_generated/server";
 import { v } from "convex/values";

 /**
  * Record an E2B API request for rate limit tracking
  */
-export const recordRequest = mutation({
+export const recordRequest = internalMutation({
   args: {
     operation: v.string(),
   },
+  returns: v.object({
+    recorded: v.boolean(),
+    timestamp: v.number(),
+  }),
   handler: async (ctx, args) => {

Committable suggestion skipped: line range outside the PR's diff.

CHANGES_SUMMARY.md-1-226 (1)

1-226: Move documentation file to explanations/ folder.

According to the coding guidelines: "Minimize the creation of .md files; if necessary, place them in the @explanations folder". This file should be moved to explanations/CHANGES_SUMMARY.md to follow the project conventions.

As per coding guidelines: Documentation files should be placed in the explanations/ folder, not in the root directory.

explanations/README_CONVEX.md-128-158 (1)

128-158: Add language identifier to fenced code block.

The directory structure code block is missing a language identifier, which affects rendering in some Markdown viewers and fails linting rules.

🔎 Proposed fix
-```
+```text
 zapdev/
 ├── convex/
 │   ├── schema.ts              # Database schema (7 tables)
src/app/ai-info/page.tsx-75-75 (1)

75-75: Incorrect React version number.

Line 75 states "React 18" but the PR uses React 19.2.0 (as shown in library context). This is factually incorrect and will mislead AI agents reading this page.

Fix for React version
-- **React 18**: Single Page Applications with Vite, Chakra UI, Tailwind CSS
+- **React 19**: Single Page Applications with Vite, Chakra UI, Tailwind CSS
src/app/(home)/page.tsx-79-85 (1)

79-85: Remove the unnecessary Suspense wrapper around PageContent.

PageContent is a synchronous client component that contains only React hooks (useEffect, useSearchParams, useRouter) and renders standard JSX. It performs no async operations, lazy loading, or data fetching, so the Suspense boundary will never be triggered and adds unnecessary overhead. Render PageContent directly without the Suspense wrapper.

src/app/dashboard/admin/e2b-health/page.tsx-113-114 (1)

113-114: Use replaceAll or global regex to replace all underscores.

replace("_", " ") only replaces the first occurrence. Operations with multiple underscores (e.g., sandbox_create_timeout) would display incorrectly.

🔎 Proposed fix
                          <span className="capitalize">
-                            {operation.replace("_", " ")}
+                            {operation.replaceAll("_", " ")}
                          </span>
src/components/file-explorer.tsx-102-102 (1)

102-102: Fix double semicolon syntax issue.

There's a double semicolon after the interface definition which is likely a typo.

🔎 Proposed fix
-  allFiles?: Record<string, unknown>;
-};;
+  allFiles?: Record<string, unknown>;
+};

Committable suggestion skipped: line range outside the PR's diff.

convex/jobQueue.ts-189-237 (1)

189-237: getStats collects all jobs which may cause performance issues at scale.

The getStats query collects the entire jobQueue table (line 192), which will become slow and expensive as the table grows. Consider using aggregation queries or caching stats.

🔎 Recommendations
  1. Add pagination or limit the query
  2. Consider maintaining stats in a separate document updated on each job state change
  3. Or use separate indexed queries for each status count rather than loading all documents
src/app/api/agent/generate/route.ts-981-988 (1)

981-988: Avoid exposing internal error details to clients.

The full error message is sent to the client, which could leak internal implementation details, file paths, or stack traces. Provide a generic error message to clients while logging full details server-side.

🔎 Proposed fix
     } catch (error) {
       const errorMessage = error instanceof Error ? error.message : String(error);
       console.error('[ERROR] Agent failed:', errorMessage);
       
+      // Sanitize error message for client
+      const clientMessage = errorMessage.includes('not found') 
+        ? 'Resource not found'
+        : errorMessage.includes('timeout')
+        ? 'Operation timed out'
+        : 'An unexpected error occurred. Please try again.';
+      
       await sendProgress({
         type: 'error',
-        message: errorMessage,
+        message: clientMessage,
       });
     } finally {
src/app/layout.tsx-92-98 (1)

92-98: Create the ai:info-url endpoint or remove the metadata reference.

The custom metadata in the Next.js metadata export includes "ai:info-url": "https://zapdev.link/ai-info" for AI discovery tools. This external endpoint does not exist in the codebase and will return 404 for any AI crawlers that follow this link. Either implement the endpoint or remove this metadata declaration.

Comment on lines +35 to +45
### Access Control
```typescript
// Check if user has Pro plan
const isPro = await hasProAccess(ctx);

// Check specific plan
const hasPlan = await hasPlan(ctx, "Pro");

// Check specific feature
const hasFeature = await hasFeature(ctx, "advanced_features");
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and its location
find . -name "CLERK_BILLING_QUICK_REFERENCE.md" -type f 2>/dev/null | head -20

# Search for hasPlan, hasFeature, hasProAccess function definitions
rg -n "export.*function (hasPlan|hasFeature|hasProAccess)" --type ts -A 5

# Check convex directory for helpers
git ls-files convex/ | grep -i helper

Repository: Jackson57279/zapdev

Length of output: 476


🏁 Script executed:

# Search for hasPlan and hasFeature definitions in helpers.ts
rg -n "hasPlan|hasFeature" convex/helpers.ts -B 2 -A 8

# Check the entire helpers.ts file to see what functions are exported
rg -n "^export" convex/helpers.ts

# Also check if these functions might be in other convex files
rg -n "function hasPlan|function hasFeature" convex/ --type ts -B 1 -A 5

Repository: Jackson57279/zapdev

Length of output: 229


Remove hasPlan and hasFeature examples — these functions do not exist in convex/helpers.ts.

Only hasProAccess is implemented. The code examples showing hasPlan(ctx, "Pro") and hasFeature(ctx, "advanced_features") reference functions that don't exist. Update the documentation to show only the available helpers: hasProAccess, requireAuth, getCurrentUserId, and getCurrentUserClerkId.

Additionally, move this file from the root directory to the explanations/ folder per coding guidelines.

🤖 Prompt for AI Agents
In CLERK_BILLING_QUICK_REFERENCE.md around lines 35-45, remove the non-existent
examples calling hasPlan(ctx, "Pro") and hasFeature(ctx, "advanced_features")
and replace them with only the documented helpers (hasProAccess, requireAuth,
getCurrentUserId, getCurrentUserClerkId) so the code snippet demonstrates
supported functions; also relocate this file from the repository root into the
explanations/ directory to follow project guidelines.

Comment on lines +288 to 298
const userId = await getCurrentUserId(ctx);

const project = await ctx.db.get(args.projectId);
if (!project) {
throw new Error("Project not found");
return null;
}

// Ensure user owns the project
if (project.userId !== userId) {
throw new Error("Unauthorized");
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -nP --type=ts --type=tsx 'api\.projects\.get\(' -g '!convex/projects.ts'

Repository: Jackson57279/zapdev

Length of output: 90


🏁 Script executed:

rg -nP 'api\.projects\.get\(' --type ts -g '!convex/projects.ts'

Repository: Jackson57279/zapdev

Length of output: 45


🏁 Script executed:

# First, check the structure of projects.ts to understand the export
head -50 convex/projects.ts

# Then search for any references to projects.get or project queries
rg 'projects\.get' --type ts

Repository: Jackson57279/zapdev

Length of output: 2316


🏁 Script executed:

# Check project-header.tsx
rg -n -B5 -A10 'api\.projects\.get' src/modules/projects/ui/components/project-header.tsx

# Check [projectId]/page.tsx
rg -n -B5 -A10 'api\.projects\.get' src/app/projects/[projectId]/page.tsx

# Check download/route.ts
rg -n -B5 -A10 'api\.projects\.get' src/app/api/projects/[projectId]/download/route.ts

Repository: Jackson57279/zapdev

Length of output: 1633


🏁 Script executed:

# Get more context around the api.projects.get call
sed -n '40,70p' src/app/api/projects/[projectId]/download/route.ts

Repository: Jackson57279/zapdev

Length of output: 1019


Verify that callers handle null return from get() query.

The get() query now returns null instead of throwing errors when the project is not found or the user is unauthorized. This breaking change requires all callers to be updated.

Current status:

  • src/modules/projects/ui/components/project-header.tsx: ✓ Properly checks if (project === null)
  • src/app/projects/[projectId]/page.tsx: ✓ Properly checks if (!project)
  • src/app/api/projects/[projectId]/download/route.ts: ✗ Does not check the null return — The query result is ignored, allowing the code to proceed with message fetching even if the project doesn't exist or user is unauthorized. This line needs to handle the null case and return an error response.
🤖 Prompt for AI Agents
In convex/projects.ts around lines 288-298 the get() call now returns null for
missing/unauthorized projects; update
src/app/api/projects/[projectId]/download/route.ts to check the query result for
null immediately after fetching the project and, if null, short-circuit by
returning an HTTP error response (e.g., new Response("Project not found or
access denied", { status: 404 }) or 403 depending on your policy) instead of
proceeding to fetch messages; ensure no further work is done when project is
null.

Comment on lines +226 to +230
const activeSubscriptions = await ctx.db
.query("subscriptions")
.withIndex("by_userId", (q) => q.eq("userId", args.userId))
.filter((q) => q.eq(q.field("status"), "active"))
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not use .filter() in queries; define a composite index instead.

Using .filter() after .withIndex() violates Convex best practices and causes performance degradation. The proper approach is to define a composite index in the schema and query it directly.

🔎 Recommended fix

Step 1: Add a composite index to your schema (in convex/schema.ts or wherever subscriptions table is defined):

subscriptions: defineTable({
  // ... existing fields
})
  .index("by_userId", ["userId"])
  .index("by_polarSubscriptionId", ["polarSubscriptionId"])
  .index("by_userId_status", ["userId", "status"]), // Add this index

Step 2: Update the query to use the composite index:

     // Find all active subscriptions for this user
     const activeSubscriptions = await ctx.db
       .query("subscriptions")
-      .withIndex("by_userId", (q) => q.eq("userId", args.userId))
-      .filter((q) => q.eq(q.field("status"), "active"))
+      .withIndex("by_userId_status", (q) => 
+        q.eq("userId", args.userId).eq("status", "active")
+      )
       .collect();

As per coding guidelines: "Do NOT use filter in queries; instead define an index in the schema and use withIndex for query filtering."

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In convex/subscriptions.ts around lines 226–230 you use .filter() after
.withIndex(), which is disallowed and slow; add a composite index in your schema
(e.g., in convex/schema.ts where subscriptions table is defined) such as
.index("by_userId_status", ["userId", "status"]), then update this query to use
that composite index with .withIndex("by_userId_status", q => q.eq("userId",
args.userId).eq("status", "active")) (remove the .filter call) so the DB uses
the index directly.

Comment on lines +1 to 10
import { createRequire } from "module";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});
const require = createRequire(import.meta.url);
const nextConfig = require("eslint-config-next");
const nextTypescriptConfig = require("eslint-config-next/typescript");

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
...nextConfig,
...nextTypescriptConfig,
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

eslint-config-next ESLint flat config compatibility 2025

💡 Result:

Short answer: As of late 2025, eslint-config-next supports ESLint's flat config (examples in the Next.js docs) and Next.js added ESLint v9/flat support, but there are still some reported edge-case issues (notably around spreading/using next/core-web-vitals in third‑party flat configs). Recommended approach: use the official eslint.config.{mjs,js} example from the Next.js docs (or import the core-web-vitals export), and if you need to adapt an older eslintrc-style shareable config use ESLint's FlatCompat or add @next/eslint-plugin-next directly. Sources: Next.js docs (flat config example) [1]; Next.js GitHub issue reporting flat-config problems with next/core-web-vitals [2]; Next.js commit adding ESLint v9 support [3]; ESLint v9 release notes (flat config default) [4].

Sources
[1] Next.js ESLint docs — example eslint.config.mjs with eslint-config-next/core-web-vitals.
[2] Issue: "New ESLint 'flat' configuration file does not work with next/core-web-vitals" on vercel/next.js.
[3] Commit/PR: "[BREAKING] Support ESLint v9 in plugin, config and next lint" (vercel/next.js).
[4] ESLint v9.0.0 release notes (flat config is default).


Use the official Next.js flat config pattern instead of spreading loaded CJS modules.

The createRequire + spreading approach for loading eslint-config-next has known edge-case issues with flat config format. Instead, follow the official Next.js docs pattern: use eslint-config-next/core-web-vitals export, leverage ESLint's FlatCompat adapter for CJS module compatibility, or add the @next/eslint-plugin-next plugin directly to the config array.

🤖 Prompt for AI Agents
In eslint.config.mjs around lines 1–10, the config currently uses createRequire
+ spreading of CJS exports from eslint-config-next and
eslint-config-next/typescript which can break the ESLint flat config pattern;
replace this with the official Next.js flat-config approach by either importing
the provided flat export (eslint-config-next/core-web-vitals) or using ESLint's
FlatCompat adapter to convert the CJS shareable configs before spreading, or
alternatively add @next/eslint-plugin-next directly into the array; update the
file to import the correct core-web-vitals export or create a FlatCompat
instance and use it to load the CJS configs so the resulting array follows the
flat config pattern.

Comment on lines +1 to +352
# Better Auth Implementation Summary

**Date**: November 12, 2025
**Status**: ✅ Complete - Ready for Testing
**Migration**: Clerk → Better Auth + Convex

---

## 🎯 Problem Solved

### Original CORS Errors:
```
Access to fetch at 'https://zapdev.link/api/auth/get-session'
from origin 'https://www.zapdev.link' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

POST https://zapdev.link/api/auth/sign-in/social net::ERR_FAILED
Reason: CORS request external redirect not allowed
```

### Root Causes:
1. **Domain Mismatch**: `www.zapdev.link` ↔ `zapdev.link`
2. **307 Redirects**: Breaking CORS preflight requests
3. **Missing CORS Headers**: No proper configuration for auth endpoints

---

## ✅ Implementation Complete

### Phase 1: Dependencies ✅
- ✅ Installed `@convex-dev/better-auth@0.9.7`
- ✅ Installed `better-auth@1.3.27` (pinned)
- ✅ Updated `convex@1.29.0`

### Phase 2: Convex Backend ✅
- ✅ Created `convex/convex.config.ts` - Registered Better Auth component
- ✅ Updated `convex/auth.config.ts` - Changed to CONVEX_SITE_URL
- ✅ Created `convex/auth.ts` - Main auth instance with Convex adapter
- ✅ Created `convex/http.ts` - HTTP router with auth routes

### Phase 3: API Routes ✅
- ✅ Created `src/app/api/auth/[...all]/route.ts` - Next.js proxy handler

### Phase 4: Client & Server Setup ✅
- ✅ Created `src/lib/auth-client.ts` - Better Auth client with Convex plugin
- ✅ Created `src/lib/auth-server.ts` - Server-side token helper
- ✅ Updated `src/components/convex-provider.tsx` - Using ConvexBetterAuthProvider
- ✅ Updated `src/app/layout.tsx` - Removed ClerkProvider

### Phase 5: Configuration ✅
- ✅ Updated `src/middleware.ts` - Simplified without Clerk
- ✅ Updated `next.config.mjs` - Added CORS headers for `/api/auth/*`
- ✅ Updated `vercel.json` - Added 308 redirect from www → non-www

### Phase 6: Environment Variables ✅
- ✅ Updated `.env.local` with Better Auth config
- ✅ Set `BETTER_AUTH_SECRET` in Convex (generated with openssl)
- ✅ Set `SITE_URL=https://zapdev.link` in Convex

### Phase 7: Documentation ✅
- ✅ Created `explanations/BETTER_AUTH_MIGRATION.md` - Full migration guide
- ✅ Created `explanations/BETTER_AUTH_QUICK_START.md` - Developer quick reference

---

## 📁 Files Changed

### New Files (8):
```
convex/auth.ts
convex/convex.config.ts
convex/http.ts
explanations/BETTER_AUTH_MIGRATION.md
explanations/BETTER_AUTH_QUICK_START.md
src/app/api/auth/[...all]/route.ts
src/lib/auth-client.ts
src/lib/auth-server.ts
```

### Modified Files (11):
```
bun.lock
convex/_generated/api.d.ts
convex/_generated/server.d.ts
convex/_generated/server.js
convex/auth.config.ts
next.config.mjs
package.json
src/app/layout.tsx
src/components/convex-provider.tsx
src/middleware.ts
vercel.json
```

---

## 🔧 Configuration Summary

### CORS Headers (next.config.mjs)
```javascript
{
source: '/api/auth/:path*',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
{ key: 'Access-Control-Allow-Origin', value: 'https://zapdev.link' },
{ key: 'Access-Control-Allow-Methods', value: 'GET,POST,PUT,DELETE,OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: '[standard auth headers]' },
]
}
```

### Domain Redirect (vercel.json)
```json
{
"redirects": [{
"source": "/:path*",
"has": [{ "type": "host", "value": "www.zapdev.link" }],
"destination": "https://zapdev.link/:path*",
"permanent": true,
"statusCode": 308
}]
}
```

### Environment Variables
```bash
# .env.local
NEXT_PUBLIC_CONVEX_URL=https://dependable-trout-339.convex.cloud
NEXT_PUBLIC_CONVEX_SITE_URL=https://dependable-trout-339.convex.site
NEXT_PUBLIC_APP_URL=https://zapdev.link
BETTER_AUTH_SECRET=Djot5JwR8GHI8p5o2ewNIx5EELKN27eA2Y3yZAaNJEI=
SITE_URL=https://zapdev.link

# Convex Environment (via convex env set)
BETTER_AUTH_SECRET=<same-as-above>
SITE_URL=https://zapdev.link
```

---

## 🔄 Authentication Flow

### New Architecture:
```
User (Browser)
authClient (Better Auth React)
/api/auth/* (Next.js API Route)
Convex HTTP Router (convex/http.ts)
Better Auth Instance (convex/auth.ts)
Convex Database (users, sessions, accounts)
Session Token
ConvexBetterAuthProvider
Authenticated Convex Queries/Mutations
```

---

## 🧪 Testing Required

### Before Deployment:
- [ ] Start dev servers: `bun run dev` + `bun run convex:dev`
- [ ] Test sign-up with email/password
- [ ] Test sign-in with email/password
- [ ] Test Google OAuth (if configured)
- [ ] Test GitHub OAuth (if configured)
- [ ] Test sign-out
- [ ] Test protected routes
- [ ] Test session persistence across page reloads
- [ ] Verify CORS headers in browser DevTools
- [ ] Test www redirect

### After Deployment:
- [ ] Verify production environment variables in Vercel
- [ ] Verify Convex production environment variables
- [ ] Test authentication flow on production domain
- [ ] Monitor Sentry for authentication errors
- [ ] Check Convex dashboard for database tables

---

## 🚀 Deployment Checklist

### 1. Convex Deployment
```bash
# Set production environment variables
convex env set BETTER_AUTH_SECRET <production-secret> --prod
convex env set SITE_URL https://zapdev.link --prod

# Deploy Convex backend
bun run convex:deploy
```

### 2. Vercel Deployment
```bash
# Set environment variables in Vercel dashboard:
NEXT_PUBLIC_CONVEX_URL=https://<prod-deployment>.convex.cloud
NEXT_PUBLIC_CONVEX_SITE_URL=https://<prod-deployment>.convex.site
NEXT_PUBLIC_APP_URL=https://zapdev.link
SITE_URL=https://zapdev.link

# Deploy (push to main or use Vercel CLI)
git push origin master
```

### 3. OAuth Providers (if using)
Update callback URLs to:
- Google: `https://zapdev.link/api/auth/callback/google`
- GitHub: `https://zapdev.link/api/auth/callback/github`

---

## 📊 Migration Benefits

### Technical Benefits:
- ✅ Self-hosted authentication (no external API calls)
- ✅ Single database (Convex for both auth and app data)
- ✅ Type-safe authentication (full TypeScript support)
- ✅ Real-time session updates via Convex subscriptions
- ✅ Simplified architecture (fewer moving parts)

### Business Benefits:
- ✅ No per-user pricing (cost savings)
- ✅ No vendor lock-in (open-source)
- ✅ Full control over auth flows
- ✅ Custom authentication logic possible
- ✅ Better GDPR compliance (self-hosted)

### Developer Experience:
- ✅ Simpler API (React hooks + Convex queries)
- ✅ Better debugging (all auth data in Convex dashboard)
- ✅ Faster iteration (no external service dependencies)
- ✅ Consistent patterns (similar to Convex queries)

---

## 🔍 Key Code Patterns

### Client Component:
```typescript
import { authClient } from "@/lib/auth-client";

const { data: session } = authClient.useSession();
await authClient.signIn.email({ email, password });
await authClient.signOut();
```

### Server Component:
```typescript
import { getToken } from "@/lib/auth-server";
import { fetchQuery } from "convex/nextjs";

const token = await getToken();
const user = await fetchQuery(api.auth.getCurrentUser, {}, { token });
```

### Convex Function:
```typescript
import { authComponent } from "./auth";

export const myQuery = query({
handler: async (ctx) => {
const user = await authComponent.getAuthUser(ctx);
if (!user) throw new Error("Unauthorized");
// ... query logic
},
});
```

---

## 🎓 Next Steps

### Immediate (Before Production):
1. ✅ Complete basic testing locally
2. ⏳ Update all components using Clerk hooks → Better Auth hooks
3. ⏳ Update tRPC context to use Better Auth tokens
4. ⏳ Test OAuth providers thoroughly
5. ⏳ Deploy to staging environment first

### Short-term (After Production):
1. Create user migration script from Clerk → Better Auth
2. Remove Clerk dependencies: `bun remove @clerk/nextjs @clerk/themes`
3. Enable email verification: `requireEmailVerification: true`
4. Add password reset functionality
5. Add email change functionality

### Long-term (Enhancements):
1. Add additional OAuth providers (Twitter, Discord, Apple)
2. Implement two-factor authentication (2FA)
3. Add session management UI (view/revoke active sessions)
4. Implement rate limiting for auth endpoints
5. Add audit logging for security events

---

## 📚 Documentation

All documentation is located in `/explanations/`:
- **BETTER_AUTH_MIGRATION.md** - Complete migration guide with rollback plan
- **BETTER_AUTH_QUICK_START.md** - Developer quick reference and code examples

Additional resources:
- [Better Auth Docs](https://www.better-auth.com/docs)
- [Convex + Better Auth](https://convex-better-auth.netlify.app/)
- [Better Auth GitHub](https://github.com/better-auth/better-auth)

---

## 🆘 Support & Troubleshooting

### Common Issues:

**Issue**: Components not found error
**Solution**: Run `bun run convex:dev` to regenerate types

**Issue**: CORS errors persisting
**Solution**: Check domain consistency and verify www redirect is active

**Issue**: OAuth not working
**Solution**: Update callback URLs in OAuth provider console

**Issue**: Session not persisting
**Solution**: Verify `SITE_URL` matches domain exactly (no trailing slash)

---

## ✅ Status: Ready for Local Testing

The migration is **complete** and ready for:
1. Local development testing
2. Component migration (Clerk → Better Auth hooks)
3. Staging deployment
4. Production deployment

All core infrastructure is in place. The main remaining work is:
- Updating existing components to use Better Auth hooks instead of Clerk
- Testing authentication flows thoroughly
- Deploying to production

---

**Implemented by**: Claude (Anthropic AI Assistant)
**Date**: November 12, 2025
**Convex Dev Server**: ✅ Verified working (9.46s build time)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify which authentication library is actually installed and used

echo "=== Checking package.json for auth dependencies ==="
cat package.json | jq '.dependencies | to_entries | .[] | select(.key | contains("auth") or contains("clerk"))' 2>/dev/null || grep -E "(auth|clerk)" package.json

echo -e "\n=== Searching for Better Auth imports ==="
rg -n "from ['\"]better-auth" --type=ts --type=tsx 2>/dev/null || echo "No Better Auth imports found"

echo -e "\n=== Searching for Stack Auth imports ==="
rg -n "from ['\"]@stackframe/stack" --type=ts --type=tsx 2>/dev/null || echo "No Stack Auth imports found"

echo -e "\n=== Checking convex/auth.ts if exists ==="
if [ -f "convex/auth.ts" ]; then
  echo "Found convex/auth.ts:"
  head -30 convex/auth.ts
else
  echo "convex/auth.ts not found"
fi

echo -e "\n=== Checking for convex/auth.config.ts ==="
if [ -f "convex/auth.config.ts" ]; then
  echo "Found convex/auth.config.ts:"
  cat convex/auth.config.ts
else
  echo "convex/auth.config.ts not found"
fi

Repository: Jackson57279/zapdev

Length of output: 1339


Remove this document: describes Better Auth but actual implementation uses Stack Auth.

The document claims to summarize a migration to Better Auth with files like convex/auth.ts, src/lib/auth-client.ts, and src/lib/auth-server.ts. However, verification shows:

  1. convex/auth.config.ts exists with Stack Auth configuration — contains explicit comment "// Stack Auth + Convex Integration" and references api.stack-auth.com
  2. convex/auth.ts does not exist — the document claims this was created
  3. No Better Auth imports found in the codebase
  4. Actual auth implementation uses Stack Auth (Convex JWT providers from Stack Auth)

This document describes an entirely different authentication system than what is actually implemented. It will mislead any developer attempting to follow it. Either remove this file or completely rewrite it to accurately document the Stack Auth + Convex integration currently in place.

🧰 Tools
🪛 LanguageTool

[style] ~3-~3: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ...ntation Summary Date: November 12, 2025 Status: ✅ Complete - Ready for Te...

(MISSING_COMMA_AFTER_YEAR)


[style] ~351-~351: Some style guides suggest that commas should set off the year in a month-day-year date.
Context: ... AI Assistant) Date: November 12, 2025 Convex Dev Server: ✅ Verified wor...

(MISSING_COMMA_AFTER_YEAR)

Comment on lines +93 to +95
<PolarCheckoutButton className="flex-1">
Upgrade to Pro
</PolarCheckoutButton>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: PolarCheckoutButton is missing required props.

According to the component definition in src/components/polar-checkout-button.tsx, PolarCheckoutButton requires the following props:

  • productId (required)
  • productName (required)
  • price (required)
  • interval (optional, defaults to "month")

The button is currently used without any props, which will cause runtime errors when users attempt to upgrade.

🔎 Proposed fix
-            {!isProUser ? (
-              <PolarCheckoutButton className="flex-1">
-                Upgrade to Pro
-              </PolarCheckoutButton>
+            {!isProUser ? (
+              <PolarCheckoutButton 
+                productId={process.env.NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID!}
+                productName="ZapDev Pro"
+                price={29}
+                interval="month"
+                className="flex-1"
+              >
+                Upgrade to Pro
+              </PolarCheckoutButton>

Ensure NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID environment variable is configured.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<PolarCheckoutButton className="flex-1">
Upgrade to Pro
</PolarCheckoutButton>
<PolarCheckoutButton
productId={process.env.NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID!}
productName="ZapDev Pro"
price={29}
interval="month"
className="flex-1"
>
Upgrade to Pro
</PolarCheckoutButton>
🤖 Prompt for AI Agents
In src/app/(home)/subscription/page.tsx around lines 93 to 95,
PolarCheckoutButton is being rendered without required props and will crash at
runtime; update the usage to pass productId, productName and price (and
optionally interval defaulting to "month") — e.g. read productId from
NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID (ensure this env var is set), provide the
human-readable productName (e.g. "Pro") and numeric/string price (e.g. "9.99"),
and omit or set interval if monthly; ensure the component receives these props
so it matches the definition in src/components/polar-checkout-button.tsx.

Comment on lines +49 to +86
export const MODEL_CONFIGS = {
'anthropic/claude-haiku-4.5': {
name: 'Claude Haiku 4.5',
provider: 'anthropic',
description: 'Fast and efficient for most coding tasks',
temperature: 0.7,
},
'openai/gpt-5.1-codex': {
name: 'GPT-5.1 Codex',
provider: 'openai',
description: 'OpenAI\'s flagship model for complex tasks',
temperature: 0.7,
},
'moonshotai/kimi-k2-thinking': {
name: 'Kimi K2 Thinking',
provider: 'moonshot',
description: 'Fast and efficient for speed-critical tasks',
temperature: 0.7,
},
'google/gemini-3-pro-preview': {
name: 'Gemini 3 Pro',
provider: 'google',
description: 'Specialized for coding tasks',
temperature: 0.7,
},
'xai/grok-4-fast-reasoning': {
name: 'Grok 4 Fast',
provider: 'xai',
description: 'Fast reasoning model',
temperature: 0.7,
},
'prime-intellect/intellect-3': {
name: 'Intellect 3',
provider: 'prime-intellect',
description: 'Advanced reasoning model from Prime Intellect',
temperature: 0.7,
},
} as const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for references to these model identifiers in the codebase
rg -n "gpt-5.1-codex|gemini-3-pro-preview|claude-haiku-4.5|kimi-k2-thinking|grok-4-fast|intellect-3" --type=ts --type=tsx --type=md -g '!node_modules'

Repository: Jackson57279/zapdev

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Check for gateway configuration files or model validation
find . -type f \( -name "*gateway*" -o -name "*config*" -o -name "*model*" \) \
  \( -name "*.ts" -o -name "*.tsx" -o -name "*.json" -o -name "*.md" \) \
  | grep -v node_modules | head -30

Repository: Jackson57279/zapdev

Length of output: 282


🏁 Script executed:

#!/bin/bash
# Look at the actual file context around the MODEL_CONFIGS
cat -n src/app/api/agent/generate/route.ts | sed -n '40,100p'

Repository: Jackson57279/zapdev

Length of output: 2223


🏁 Script executed:

#!/bin/bash
# Search for any use of MODEL_CONFIGS to understand how it's validated
rg -n "MODEL_CONFIGS" src/ -A 3 -B 1 --type=ts --type=tsx

Repository: Jackson57279/zapdev

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Read the Vercel AI Gateway optimization documentation
cat -n ./explanations/vercel_ai_gateway_optimization.md

Repository: Jackson57279/zapdev

Length of output: 6642


🏁 Script executed:

#!/bin/bash
# Check the model selection tests
cat -n ./tests/model-selection.test.ts

Repository: Jackson57279/zapdev

Length of output: 2649


🏁 Script executed:

#!/bin/bash
# Search for MODEL_CONFIGS usage and validation (without tsx flag)
rg -n "MODEL_CONFIGS|gpt-5.1-codex|gemini-3-pro-preview|claude-haiku-4.5" --type=ts -A 2 -B 1 | head -100

Repository: Jackson57279/zapdev

Length of output: 5848


🏁 Script executed:

#!/bin/bash
# Find where selectedModel is used in API calls
rg -n "selectedModel" src/inngest/functions.ts -A 5 | head -50

Repository: Jackson57279/zapdev

Length of output: 1760


🏁 Script executed:

#!/bin/bash
# Look for openai client usage and how model is passed
rg -n "generateObject|generateText|model:" src/inngest/functions.ts | grep -i model | head -20

Repository: Jackson57279/zapdev

Length of output: 649


🏁 Script executed:

#!/bin/bash
# Check src/app/api/agent/generate/route.ts for how selectedModel is used
cat -n src/app/api/agent/generate/route.ts | sed -n '200,300p'

Repository: Jackson57279/zapdev

Length of output: 3651


🏁 Script executed:

#!/bin/bash
# Check if src/app/api/agent/generate/route.ts actually calls the inngest functions or has other logic
cat -n src/app/api/agent/generate/route.ts | sed -n '1,100p'

Repository: Jackson57279/zapdev

Length of output: 3613


🏁 Script executed:

#!/bin/bash
# Search for any .env or configuration files that might map these models
find . -name ".env*" -o -name "*config*" | grep -v node_modules | head -20

Repository: Jackson57279/zapdev

Length of output: 312


🏁 Script executed:

#!/bin/bash
# Check if these models are referenced as test data or if there's any mapping
rg -n "gpt-5.1-codex|gpt-5-nano|glm-4.7" src/ --type=ts -B 3 -A 3 | head -40

Repository: Jackson57279/zapdev

Length of output: 2220


🏁 Script executed:

#!/bin/bash
# Check if the route.ts file is actually used in production or is development code
grep -n "export.*POST\|export.*GET\|export.*handler" src/app/api/agent/generate/route.ts

Repository: Jackson57279/zapdev

Length of output: 113


🏁 Script executed:

#!/bin/bash
# Look for any comments or context about these models being test/placeholder models
rg -n "test|mock|placeholder|fake|debug" src/app/api/agent/generate/route.ts -i

Repository: Jackson57279/zapdev

Length of output: 881


🏁 Script executed:

#!/bin/bash
# Check OpenRouter documentation or if there's any mapping/validation specific to OpenRouter
rg -n "openrouter|OpenRouter" src/ --type=ts -B 2 -A 2 | head -50

Repository: Jackson57279/zapdev

Length of output: 3115


🏁 Script executed:

#!/bin/bash
# Verify if the route actually uses selectedModel to call API or just for metadata
cat -n src/app/api/agent/generate/route.ts | sed -n '140,220p'

Repository: Jackson57279/zapdev

Length of output: 2831


🏁 Script executed:

#!/bin/bash
# Check if the route.ts actually makes API calls or just stores metadata
cat -n src/app/api/agent/generate/route.ts | sed -n '600,700p'

Repository: Jackson57279/zapdev

Length of output: 4032


🏁 Script executed:

#!/bin/bash
# Verify what real models OpenRouter supports - check if there's any documentation or comments
rg -n "claude-3|gpt-4|gemini|supported.*models" src/ --type=ts -i | head -30

Repository: Jackson57279/zapdev

Length of output: 1682


🏁 Script executed:

#!/bin/bash
# Find the getAIProvider function to see if it maps these fictional models to real ones
rg -n "getAIProvider|function.*getAIProvider" src/ --type=ts -A 10 | head -50

Repository: Jackson57279/zapdev

Length of output: 3402


🏁 Script executed:

#!/bin/bash
# Check if these models exist on OpenRouter by looking for any real model references
rg -n "gemini-2.5-flash|claude-3|gpt-4|grok-2" src/ --type=ts | head -20

Repository: Jackson57279/zapdev

Length of output: 244


🏁 Script executed:

#!/bin/bash
# Check Vercel AI Gateway documentation or find validation for these models
rg -n "gemini-2.5-flash|claude-haiku|supported.*model" src/ --type=ts | head -20

Repository: Jackson57279/zapdev

Length of output: 1333


🏁 Script executed:

#!/bin/bash
# See full getAIProvider function
cat -n src/app/api/agent/generate/route.ts | sed -n '103,120p'

Repository: Jackson57279/zapdev

Length of output: 618


🏁 Script executed:

#!/bin/bash
# Check where these model identifiers are actually used in API calls
cat -n src/app/api/agent/generate/route.ts | sed -n '785,815p'

Repository: Jackson57279/zapdev

Length of output: 1351


Update MODEL_CONFIGS to use models actually supported by Vercel AI Gateway.

The models in MODEL_CONFIGS (lines 50–86) don't exist in Vercel AI Gateway: gpt-5.1-codex (GPT-5 doesn't exist), claude-haiku-4.5 (current is 3.5), gemini-3-pro-preview (current is 2.5), kimi-k2-thinking (not a real Moonshot model), grok-4-fast-reasoning (current is 2), and intellect-3 (fictional). These will cause runtime failures when runAgentLoop and generateText (lines 789–810) attempt API calls. Use actual Vercel AI Gateway model identifiers or ensure aliases are properly configured.

🤖 Prompt for AI Agents
In src/app/api/agent/generate/route.ts around lines 49 to 86, MODEL_CONFIGS
contains fictional or unsupported model identifiers for Vercel AI Gateway which
will cause runtime API failures; update the entries to use actual Vercel AI
Gateway model IDs (or map each alias to a validated supported ID), correct
provider fields to match gateway providers, and adjust
names/descriptions/temperatures as needed; ensure MODEL_CONFIGS only lists
supported models or add a validation step that throws a clear error if an
unsupported model id is used so runAgentLoop/generateText cannot call the
gateway with invalid identifiers.

Comment on lines +607 to +631
export async function POST(req: NextRequest) {
const encoder = new TextEncoder();
const stream = new TransformStream();
const writer = stream.writable.getWriter();

const sendProgress = async (event: ProgressEvent) => {
const message = `data: ${JSON.stringify(event)}\n\n`;
await writer.write(encoder.encode(message));
};

(async () => {
const convex = getConvexClient();
let sandboxId: string | null = null;
let selectedFramework: Framework = 'nextjs';
let selectedModel: keyof typeof MODEL_CONFIGS = 'anthropic/claude-haiku-4.5';

try {
const body = await req.json();
const {
projectId,
value: prompt,
model: requestedModel = 'auto',
mode = 'fast',
} = body;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing authentication on public API endpoint.

This POST handler processes user requests without any authentication or authorization checks. Per coding guidelines, API routes should sync credit usage with Clerk and verify plan: 'pro' for Pro tier. Without authentication:

  • Any unauthenticated user can consume E2B sandbox resources
  • No user identity verification before accessing project data
  • Potential for abuse and resource exhaustion
🔎 Suggested authentication pattern
export async function POST(req: NextRequest) {
  // Add authentication check
  const { userId } = await auth();
  if (!userId) {
    return new Response(JSON.stringify({ error: 'Unauthorized' }), {
      status: 401,
      headers: { 'Content-Type': 'application/json' },
    });
  }
  
  // Verify project ownership
  const project = await convex.query(api.projects.getForSystem, {
    projectId: projectId as Id<'projects'>,
  });
  
  if (!project || project.userId !== userId) {
    return new Response(JSON.stringify({ error: 'Forbidden' }), {
      status: 403,
      headers: { 'Content-Type': 'application/json' },
    });
  }
  
  // ... rest of handler
}
🤖 Prompt for AI Agents
In src/app/api/agent/generate/route.ts around lines 607 to 631, the POST handler
is missing authentication and authorization checks which allows unauthenticated
use of sandbox resources; fix by calling the authentication helper (e.g.,
auth()) at the start and return 401 if no userId, then parse projectId and query
Convex for the project record to verify existence and ownership (project.userId
=== userId) and return 403 if not allowed, additionally verify the user's
plan/tier (ensure plan === 'pro' when required) and sync or decrement credits
via Clerk/usage endpoints before doing heavy processing; perform these checks
before creating streams or starting generation and ensure error responses
include appropriate JSON content-type and status codes.

Comment on lines +29 to +84
export async function POST(request: NextRequest) {
try {
const { prompt } = await request.json();

if (!prompt || typeof prompt !== "string" || prompt.trim().length === 0) {
return NextResponse.json(
{ error: "Prompt is required" },
{ status: 400 }
);
}

if (prompt.length > 5000) {
return NextResponse.json(
{ error: "Prompt is too long (max 5000 characters)" },
{ status: 400 }
);
}

const apiKey = process.env.OPENROUTER_API_KEY;
const baseUrl = process.env.OPENROUTER_BASE_URL || "https://openrouter.ai/api/v1";

if (!apiKey) {
console.error("[ERROR] OPENROUTER_API_KEY not configured");
return NextResponse.json(
{ error: "AI service not configured" },
{ status: 500 }
);
}

console.log("[DEBUG] Enhancing prompt with length:", prompt.length);
console.log("[DEBUG] Using model: openai/gpt-5-nano");
console.log("[DEBUG] API Base URL:", baseUrl);

const response = await fetch(`${baseUrl}/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
"HTTP-Referer": process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
"X-Title": "ZapDev Prompt Enhancer",
},
body: JSON.stringify({
model: "openai/gpt-5-nano",
messages: [
{
role: "system",
content: ENHANCE_PROMPT_SYSTEM,
},
{
role: "user",
content: prompt,
},
],
temperature: 0.7,
max_tokens: 1000,
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add authentication and rate limiting to prevent abuse.

This endpoint performs expensive AI API calls but has no authentication or rate limiting, allowing anyone to abuse the service.

🔎 Recommended security measures
import { getUser } from "@/lib/auth-server";

export async function POST(request: NextRequest) {
  // Add authentication
  const user = await getUser();
  if (!user) {
    return NextResponse.json(
      { error: "Unauthorized" },
      { status: 401 }
    );
  }

  // Consider adding rate limiting here
  // Check user's plan and enforce appropriate limits
  
  try {
    const { prompt } = await request.json();
    // ... rest of logic

Based on coding guidelines: "Sync credit usage with Clerk custom claim plan: 'pro' for Pro tier verification" - consider applying similar protection here.

Comment on lines +13 to +29
let convexClient: ConvexClient | null = null;
function getConvexClient() {
if (!convexClient) {
const url = process.env.NEXT_PUBLIC_CONVEX_URL;
if (!url) {
throw new Error("NEXT_PUBLIC_CONVEX_URL environment variable is not set");
}
convexClient = new ConvexClient(url);
}
return convexClient;
}

const convex = new Proxy({} as ConvexClient, {
get(_target, prop) {
return getConvexClient()[prop as keyof ConvexClient];
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find all ConvexClient initializations to see patterns
ast-grep --pattern 'new ConvexClient($_)'

Repository: Jackson57279/zapdev

Length of output: 336


🏁 Script executed:

# Check how environment variables are used in Inngest functions
rg -n "CONVEX" src/inngest/ --type ts

Repository: Jackson57279/zapdev

Length of output: 1157


🏁 Script executed:

# Look at Convex client setup and authentication patterns
fd -e ts -e tsx src/inngest | head -20

Repository: Jackson57279/zapdev

Length of output: 412


🏁 Script executed:

# Check if there are other env var patterns for Convex (non NEXT_PUBLIC)
rg -n "CONVEX" . --type-add 'env:*.env*' -t env 2>/dev/null || rg -n "CONVEX" .env.example .env.local .env 2>/dev/null || echo "Checking root env files..."

Repository: Jackson57279/zapdev

Length of output: 89


🏁 Script executed:

# Look for how Convex authentication is handled elsewhere
rg -n "ConvexClient|initConvex|Convex.*auth" src/ --type ts --max-count 20

Repository: Jackson57279/zapdev

Length of output: 4894


🏁 Script executed:

# Check the getConvexClientWithAuth implementation
cat -n src/lib/auth-server.ts | head -60

Repository: Jackson57279/zapdev

Length of output: 2103


🏁 Script executed:

# Check one of the API routes that uses authenticated client
cat -n src/app/api/agent/generate/route.ts | sed -n '85,100p'

Repository: Jackson57279/zapdev

Length of output: 601


🏁 Script executed:

# Check if Inngest functions have any auth context available
rg -n "getUser|getAuth|authentication|auth" src/inngest/functions/ --type ts -A 2

Repository: Jackson57279/zapdev

Length of output: 45


🏁 Script executed:

# Check Inngest configuration
rg -n "Inngest|inngest" . --type ts -l | grep -E "(config|setup|\.ts$)" | head -5

Repository: Jackson57279/zapdev

Length of output: 256


🏁 Script executed:

# Look at one complete Inngest function to understand auth context
cat -n src/inngest/functions/process-figma-direct.ts | head -80

Repository: Jackson57279/zapdev

Length of output: 3411


🏁 Script executed:

# Check if there's documentation or comments about why unauthenticated ConvexClient is used
rg -n "unauthenticated|no auth|without auth" src/inngest/ --type ts

Repository: Jackson57279/zapdev

Length of output: 45


🏁 Script executed:

# Check the actual Inngest client initialization
cat -n src/inngest/client.ts 2>/dev/null || cat -n src/inngest/index.ts 2>/dev/null || fd inngest -t f src/inngest/ --max-depth 1 | head -5

Repository: Jackson57279/zapdev

Length of output: 632


🏁 Script executed:

# Check if Inngest functions make any user-facing mutations or queries
rg -n "api\." src/inngest/functions/process-figma-direct.ts -A 1 | head -20

Repository: Jackson57279/zapdev

Length of output: 454


🏁 Script executed:

# See what these mutations/actions do
cat -n src/convex/imports.ts | grep -A 5 "markProcessing"

Repository: Jackson57279/zapdev

Length of output: 116


🏁 Script executed:

# Check if there's a security/design decision documented
rg -n "TODO|FIXME|HACK|XXX|NOTE.*auth" src/inngest/ --type ts

Repository: Jackson57279/zapdev

Length of output: 45


🏁 Script executed:

# Find Convex schema files
fd . convex/ -type f -name "*.ts" | head -20

Repository: Jackson57279/zapdev

Length of output: 233


🏁 Script executed:

# Look at the mutations being called from Inngest
rg -n "markProcessing|markComplete|markFailed|createWithAttachments" convex/ --type ts -B 2 -A 10 | head -60

Repository: Jackson57279/zapdev

Length of output: 2666


🏁 Script executed:

# Check if these are public or internal mutations
rg -n "export.*mutation|query.*exports" convex/ --type ts | grep -E "(markProcessing|markComplete|markFailed|createWithAttachments)"

Repository: Jackson57279/zapdev

Length of output: 432


🏁 Script executed:

# Check if markProcessing/markComplete/markFailed in imports.ts have any auth checks
cat -n convex/imports.ts | sed -n '131,180p'

Repository: Jackson57279/zapdev

Length of output: 1675


🏁 Script executed:

# Check createWithAttachments action to see if it validates ownership
cat -n convex/messages.ts | sed -n '56,120p'

Repository: Jackson57279/zapdev

Length of output: 2496


Unauthenticated Convex client will cause mutation failures in Inngest functions.

The Inngest function calls mutations and actions that require authentication (markProcessing, markComplete, markFailed, createWithAttachments all call requireAuth(ctx) or ctx.auth.getUserIdentity()), but the ConvexClient is initialized without authentication. This will cause these calls to fail with "Unauthorized" errors since the mutations check userId !== importRecord.userId.

This issue affects all Inngest functions (process-figma-direct.ts, process-figma-import.ts, process-github-import.ts, auto-pause.ts). Either:

  • Use a backend auth approach (service token or internal mutations for Inngest), or
  • Configure authentication for the ConvexClient before calling these mutations

Also note: NEXT_PUBLIC_CONVEX_URL is intended for client-side code; server-side Inngest functions should use a non-public environment variable or handle authentication differently.

@Jackson57279 Jackson57279 deleted the tembo/fix-pr-198-merge-conflict branch January 31, 2026 20:02
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