This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
Novita Arena is a Next.js 16 web application where LLM models compete to generate HTML/visual outputs. Users select two models, choose a category (Physics, Visual Magic, Game Jam, General), and watch them generate code simultaneously with side-by-side comparison.
npm run dev # Start dev server (http://localhost:3000)
npm run build # Production build
npm run lint # ESLint (next.js + TypeScript)
npm run knip # Check unused dependencies/exportsNo test runner is configured.
This repo is the implementation surface for the Render Arena Agent Team Linear/Symphony demo. Keep the visible workflow general-purpose: Product, Engineering, and PR Review collaborate on Render Arena product changes. Do not frame the workflow as model-launch-only, even though adding or replacing a Novita model is the first high-value demo scenario.
- Product: turns business intent into a clear Render Arena requirement. It must inspect repo context and Novita capability before asking the Product Owner for decisions.
- Engineering: implements scoped frontend/config changes and opens PRs.
- PR Review: reviews the PR, runs validation, checks evidence, and recommends Human Review or rework.
- Human-facing statuses are limited to
Backlog,AI Executing,Needs Clarification,Human Review,Merging,Done, andCanceled. - Product, Engineering, and PR Review are represented by Linear delegate plus labels, not by separate statuses.
- Use
Needs Clarificationonly after research, with concrete options, recommendation, and tradeoffs. - Use
Human Reviewonly when a PR, preview, or reviewable evidence package exists. - Keep secrets out of Linear comments, PRs, docs, and logs.
When the request involves adding, replacing, or validating a model:
- Use the local
Novitaaiskill from.agents/skills/novitaai/SKILL.md. - Verify model availability through
GET https://api.novita.ai/openai/v1/modelsusingNEXT_NOVITA_API_KEYorNOVITA_API_KEYfrom local/runtime environment. - Convert Novita model prices into this repo's
lib/config.tsunit: dollars per million tokens. - Product output must name the model id, display name, provider group, selector group, icon/socialTag choice, price fields, and acceptance criteria.
- Engineering should normally edit only
lib/config.tsunless Product explicitly asks for default model, copy, or UI placement changes. - PR Review must verify model existence, selector visibility, no unrelated product changes,
npm run lint,npm run build, and Vercel preview/deploy evidence when available.
- Framework: Next.js 16 (App Router) + React 19 + TypeScript
- Styling: Tailwind CSS v4
- UI: shadcn/ui patterns + Base UI React
- Streaming: Vercel AI SDK (
streamObject()) - Database: Supabase (PostgreSQL with RLS)
- Auth: Novita OAuth → Supabase session bridge
/app
/api # API routes (auth, apps, media)
/gallery # Gallery page
/playground # Playground/Arena page
/components
/app # Page-level components
/base # Reusable components (button, textarea, model-selector)
/ui # UI utilities
/playground # Arena-specific components
/hooks # useAuth, useModelGeneration, useArenaPlayground, useScreenRecorder
/lib # Config, utilities, Supabase clients
/types # TypeScript interfaces
User Input → useModelGeneration → POST /api/apps/[id]/generate (streaming)
→ Model API via Novita → HTML Response → StreamingCodeDisplay → Supabase → Gallery
POST /api/apps- Create appPOST /api/apps/[id]/generate- Trigger model generation (streaming)POST /api/apps/[id]/publish- Publish to galleryGET /api/auth/me- Current user
Use @/ path alias for all internal imports:
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'Never nest <button> inside Base UI triggers. Use render prop:
// ✓ Correct
<Popover.Trigger render={<button>Click me</button>} />
// ✗ Wrong - causes hydration errors
<Popover.Trigger>
<button>Click me</button>
</Popover.Trigger>export async function POST(req: Request) {
try {
const body = await req.json()
return Response.json({ data })
} catch (error) {
return Response.json(
{ error: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
)
}
}
export const maxDuration = 60Required in .env.local:
NOVITA_API_KEY=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
NEXT_SUPABASE_SERVICE_ROLE_KEY=
NEXT_ENCRYPTION_MASTER_KEY= # 32-char hex for pgcryptoModels are defined in /lib/config.ts. Default models:
- Model A:
pa/claude-opus-4-6 - Model B:
moonshotai/kimi-k2.6
Categories: Physics, Visual Magic, Game Jam, General
No Redux/Zustand. Uses React hooks:
useAuth()- AuthenticationuseModelGeneration()- Streaming generationuseArenaPlayground()- Arena state
/lib/supabase/client.ts- Browser client/lib/supabase/server.ts- Server client with auth (for API routes)/lib/supabase/admin.ts- Admin client with service role (bypasses RLS)