Changelog

Every update, in order — newest first.

v5.019 Jun 2026

SEO strategy research and content distribution — keyword research identified as the top organic growth priority, blog content strategy narrowed to product-adjacent topics for topical authority with Google, LinkedIn success story published to build credibility, and engagement continued across LinkedIn and Reddit.

Features1
  • LinkedIn success story post published — showcased platform progress and outcomes to build credibility and demonstrate real-world value to hiring professionals and the developer community
Improvements3
  • LinkedIn engagement continued — active commenting, discussions, and interactions on hiring and developer content to increase profile reach and strengthen relationships within the recruitment and engineering communities
  • Reddit engagement activity ongoing — commenting in r/recruitment, r/webdev, and r/saas; posts still being removed in some communities but progress improving gradually; Reddit remains a secondary acquisition channel for now
  • Platform awareness outreach continued — networking with relevant hiring and developer professionals to grow platform visibility and build early audience ahead of content-led growth
Research2
  • SEO strategy researched — keyword research identified as the top priority for improving website visibility; Ahrefs access requested from Sasha to support keyword discovery and competitive analysis
  • Blog content strategy defined — concluded that content should focus on subjects closely related to the product rather than purely targeting high-search-volume keywords; this approach attracts the right audience (recruiters and engineering managers) and builds topical authority with Google more effectively than chasing broad traffic
v4.918 Jun 2026

Content execution and distribution cycle — published a new article on coding assessment limitations in the era of vibe coding, followed by multi-platform distribution and continued community engagement across LinkedIn, Twitter, and Reddit to drive early traction and visibility.

Features1
  • Blog article published: 'Why Traditional Coding Tests Fail in the Age of Vibe Coding' — explores how traditional coding assessments fail to evaluate modern AI-assisted (vibe coding) workflows and why hiring needs to shift toward real-world problem solving
Improvements3
  • Article shared on LinkedIn and Twitter to increase visibility, drive initial traffic, and test early audience engagement signals
  • Continued platform promotion through active engagement on Twitter, LinkedIn, and Reddit to increase reach and strengthen distribution momentum
  • Reddit engagement activity continued with commenting to build karma, improve account credibility, and increase visibility within developer and hiring communities
Research1
  • Topic research and selection completed for 'Why Traditional Coding Tests Fail in the Age of Vibe Coding' based on hiring trends, AI-assisted development workflows, and recruiter pain points
v4.817 Jun 2026

Growth and content push — first blog article published targeting recruiter search intent, Reddit karma-building activity started, and LinkedIn outreach campaign launched with direct connection requests and community engagement on hiring-related posts.

Features1
  • Blog article published: 'Stop Reading Resumes: How to Screen Developers in 60 Minutes or Less' — targets recruiter and engineering manager search intent; covers using coding contests as a faster, more objective screening method than resume review
Improvements3
  • Reddit account karma-building started — active commenting in relevant subreddits to build account credibility and platform visibility ahead of direct promotional posts
  • LinkedIn connection outreach campaign started — sending personalised connection requests that include the platform URL and a request for feedback from hiring managers and engineering leads
  • LinkedIn community engagement — leaving relevant comments on hiring and recruiting-related posts to increase profile visibility and establish platform presence in the technical recruiting space
Research1
  • Article topic research and keyword strategy completed — identified high-intent search terms around developer screening, technical hiring, and coding assessments; mapped topics to recruiter pain points for future content pipeline
v4.716 Jun 2026

Blog system launched end-to-end — public article pages with hero, table of contents, and author panel; full SEO implementation across all public pages with JSON-LD structured data; home page and navbar made mobile-friendly; admin article editor now has a Markdown Guide modal.

Features4
  • Public blog article page at /blog/[slug] — hero section with cover image (full-width, auto-height), article title, estimated read time, and published date overlaid on a gradient; 3-column layout below with a sticky table of contents on the left, independently scrollable article content in the center, and an author card on the right
  • Table of contents auto-generated from ## and ### headings — TOC links scroll the center column to the correct heading with a phantom anchor technique that offsets for the sticky navbar so headings are never hidden behind the header
  • Admin article editor Markdown Guide modal — 'Markdown Guide' button added to the editor toolbar; opens a modal showing all 19 supported elements (headings, bold, italic, inline and block code, links, images, lists, blockquotes, tables, hr) with copy-ready syntax examples and a warning that <details>/<summary> accordions are not supported
  • Blog articles backend — GET /api/articles (paginated list of published articles), GET /api/articles/:slug (single article by slug), and admin CRUD endpoints (create, update, delete) added; Article model with title, slug, excerpt, content, coverImage, status (DRAFT/PUBLISHED), author relation, and publishedAt timestamp added to Prisma schema
Improvements8
  • HomeNav mobile hamburger menu — hamburger/close icon toggle added for small screens; dropdown panel shows all nav links (How it works, Use cases, Changelog, Blog) plus full-width Log in and Sign up buttons; desktop layout unchanged
  • Nav links work from any page — 'How it works' and 'Use cases' now detect the current pathname; if already on the home page they scroll in-page, otherwise they navigate to /#section first; clicking the logo always navigates to the home page
  • Changelog page uses shared layout — replaced the custom nav with HomeNav and PublicFooter so the changelog matches the look of the blog and home page
  • Home page mobile layout fixed across all sections — hero top padding and heading size adjusted for small screens; feature strip changed to single-column vertical list so icons and labels never wrap mid-word; How it works section vertically centered; Use cases, Testimonials, and CTA sections have reduced padding and font sizes on mobile; product preview wrapped in a horizontal scroll container so the code preview is not clipped on narrow screens
  • Full SEO implementation across public pages — Organization and WebSite (Sitelinks Searchbox) JSON-LD added to root layout; BlogPosting and BreadcrumbList JSON-LD added to each article page; generateMetadata with canonical URL, Open Graph (article type with publishedTime and modifiedTime), and Twitter card on article pages; metadata added to home, blog list, login, and register pages
  • Platform repositioned as developer hiring through coding contests — all meta titles, descriptions, and structured data updated to target recruiter and engineering manager search intent ('developer hiring', 'technical recruiting', 'coding assessments') rather than the generic 'vibe coding' framing
  • Sitemap extended with blog pages — sitemap.ts made async and fetches all published articles from the API; /blog index added at priority 0.9; each article slug added with its updatedAt date for accurate crawl scheduling
  • Content images in articles render at full natural height — plain <img> tag with w-full h-auto max-h-none prevents cropping; hero cover image uses Next.js Image with width=0 height=0 sizes='100vw' to preserve natural aspect ratio without a fixed container height
v4.615 Jun 2026

Growth and acquisition push — LinkedIn company page launched, Twitter account restored, SEO audit completed with indexing and content strategy defined, and Reddit community engagement started.

Features1
  • LinkedIn company page created — full company profile set up with name, tagline, description, industry, specialties, and branding; first post published introducing the platform and its contest workflow to a professional audience
Improvements1
  • Twitter / X account restored — account recovery completed; platform now has an active presence on Twitter for community updates and engagement
Research4
  • SEO audit completed — investigated Google organic search visibility; identified root causes: new domain age, zero backlinks, single indexable page, no structured data (JSON-LD), and highly competitive target keywords
  • Google Search Console domain verification and sitemap submission initiated — property being set up at search.google.com/search-console; sitemap.xml queued for submission at /sitemap.xml once verified
  • SEO content strategy planned — two new route groups identified (/blog and /use-cases) to build topical authority; priority pages: what-is-vibe-coding, how-to-host-a-hackathon, and dev-teams use-case landing page
  • Reddit acquisition started — account set up and posting attempted in r/webdev and r/recruiting; posts removed due to flair requirements and content policy filters; subreddit rules under review before re-posting
v4.512 Jun 2026

Admin feedback dashboard added — admins can now browse all platform reviews submitted by users in a paginated table with category badges, star ratings, and expandable messages.

Features4
  • Admin User Feedback page at /admin/feedback — paginated table showing all platform reviews with user avatar, color-coded category badge (General, Bug Report, Feature Request, UI/UX, Performance), star rating, subject, expandable message body, and submission date; accessible only to ADMIN users
  • User Feedback nav link added to admin sidebar section — Star icon link to /admin/feedback appears alongside Manage Users for all ADMIN-role users
  • platformReviewApi.list() added to frontend API client — calls the existing GET /platform-reviews admin endpoint with pagination params; returns reviews with nested user object
  • PlatformReviewWithUser type added to types/index.ts — extends PlatformReview with a nested user field (id, username, avatar) as returned by the admin list endpoint
Fixes1
  • Feedback table subject truncation fixed — review subject was cut off with an ellipsis; changed from truncate to break-words so long titles wrap to a new line and are always fully visible
v4.411 Jun 2026

Acquisition work started — social media presence established on Reddit and LinkedIn ahead of the first growth push. First LinkedIn post drafted as a PDF ready for publishing.

Research3
  • Reddit account set up — profile created and configured for organic community engagement; target subreddits identified for sharing contest links and dev-focused content
  • LinkedIn personal account set up — profile created for B2B outreach to recruiting teams and engineering managers; company page creation requires at least 1 day after account setup
  • First LinkedIn post drafted as a PDF — content written and formatted ready for publishing; focuses on the platform's recruiting and team-building angle
v4.310 Jun 2026

Team review system shipped for private contests — hosts can invite reviewers by email, reviewers can leave structured notes on each submission visible to all reviewers, and prompts are now always visible to hosts and reviewers regardless of whether participants chose to share them publicly.

Features6
  • Team review panel for private contests — a Shield 'Review' button appears on every submission card for invited reviewers and the host; opens a modal showing all reviews left by the review panel with author, date, and body; one review per reviewer per submission (subsequent submissions update rather than duplicate)
  • Invite Reviewers button for private contest hosts — opens a modal where the host pastes email addresses (comma/space/newline separated); each address is added to the ContestReviewer allowlist and receives a branded email with a direct link to the results page
  • Reviewer invite email — subject '[Host] invited you to review submissions for: [Title]'; signed with company name when set, falling back to username; includes a red CTA button linking directly to the results page and a note to sign in with the invited email address
  • POST /api/contests/:id/invite-reviewers endpoint — validates the caller is the contest creator and the contest is PRIVATE; persists reviewer emails to ContestReviewer with skipDuplicates so re-inviting is safe; fires emails via Resend with Promise.allSettled for partial-failure handling
  • Review routes — POST /api/contests/:contestId/reviews (upsert: one review per author per submission), GET /api/contests/:contestId/reviews/:submissionId (fetch all reviews for a submission); both endpoints require the caller to be a reviewer or the host
  • ContestReviewer and Review models added to Prisma schema — ContestReviewer stores an email+contestId allowlist with CASCADE delete; Review stores body, authorId, submissionId, contestId with a unique constraint on (authorId, submissionId); migration 20260610000000_add_reviews_and_reviewers applied
Improvements2
  • Prompts always visible to host and reviewers — leaderboard endpoint now computes reviewer/host status before building the submission list and exposes the prompts field to them even when promptsPublic is false; previously only the submission owner and public-sharers could see prompt data
  • reviewCount included in leaderboard entries — review button badge shows the live count of reviews on each submission so reviewers can see at a glance which submissions have already been reviewed
Fixes2
  • Host seeing both Prompt and Share prompt buttons on their own submission — hasPrompts now excludes own submissions so the host sees only the Share prompt button for their own entry and the Prompt view button only on others
  • Backend running stale compiled code after source edits — vote.service.ts changes were not picked up because backend-dev runs dist/server.js not tsx directly; rebuilt with npm run build before restarting PM2
v4.29 Jun 2026

Recruiting-focused contest features shipped — schedulable contests with optional future start times, private contests with company branding, and bulk email invites. Recruiters can now schedule a challenge in advance, brand it under their company name, and send the join link to multiple candidates in one click.

Features7
  • Scheduled contests — creator can choose 'Schedule' instead of 'Start Now' and pick any future datetime; contest stays in WAITING state with a static 'Starts at [date]' display until 2 minutes before launch, then transitions to the standard circular countdown ring; backend auto-starts at the scheduled time on the next poll
  • Private visibility with company branding — creator can set a contest to Private and enter a company name; the join page, contest lobby, and participant waiting view all show 'Hosted by [Company]' instead of the creator's username
  • Bulk email invites on the contest creation success card — after creating a contest, creator can type or paste multiple email addresses (comma/space/Enter separated, up to 50), click Send Invites, and every recipient gets a branded email with the contest join link; partial-failure handling shows how many succeeded and which addresses failed
  • Invite via Email section on the join page — contest creator visiting their own join page sees an email invite panel (hidden from other participants); same tag-input UX with paste-parsing and backspace-to-remove
  • POST /api/contests/:id/send-invites endpoint — validates the caller is the contest creator, builds the join URL from FRONTEND_URL + shareCode, and fires all emails via Resend using Promise.allSettled so one failed address never blocks the rest
  • sendContestInvite email template added — branded red CTA button, subject '[Host] invited you to a coding contest: [Title]', and a plain-text fallback URL; host name uses company name when set, falling back to username
  • ContestVisibility enum and three new columns (visibility, companyName, scheduledFor) added to the Contest table via Prisma migration 20260609000000_add_scheduling_and_branding; all existing contests default to PUBLIC with no breaking changes
Improvements4
  • Contest list page shows a blue calendar badge with the scheduled start datetime on WAITING contests that have a future scheduledFor; private contests show a lock icon and company name pill
  • Join page shows a blue 'Starts at' info box when the contest is scheduled in the future, so candidates know when to come back
  • Participant waiting view (lobby) shows the static scheduled date instead of the countdown ring when more than 2 minutes remain; switches automatically to the ring as launch approaches
  • Contest polling interval adapts to scheduling — polls every 30 s when the scheduled start is more than 5 min away, drops to 3 s within the final 5 min to catch the transition cleanly
Fixes2
  • Scheduled contest flash bug fixed — show/hide logic for the static date vs countdown ring now computed directly from contest.scheduledFor (a plain variable) instead of a state value initialised to 0, eliminating a first-render flicker that made the ring appear briefly before the static display
  • Old backend process on port 5001 shadowing the new build — an orphaned node process from before the PM2 restart was occupying port 5001; killed and PM2 backend-dev restarted so the new endpoint is actually live
v4.18 Jun 2026

Stripe Customer Portal removed — all plan management (cancel, downgrade, reactivate, upgrade) now handled entirely in-app with custom modals and direct Stripe API calls. Duplicate subscription bug fixed. Stale UI after cancel+upgrade resolved. Credit carry-over display improved with dynamic messaging. Google Tag Manager confirmed live.

Features6
  • In-app cancel flow — CancelModal with a custom dropdown reason survey (Too expensive, Not using it enough, Missing features, Found a better alternative, Technical issues, Other); calls POST /api/billing/cancel directly; reason stored as Stripe subscription metadata; Cancel button disabled until reason selected
  • In-app downgrade flow — DowngradeModal shows three impact bullets (credit drop, contest cost, mid-month reset) and a carry-over notice if credits exceed 100; calls POST /api/billing/downgrade which updates Stripe price in-place with proration_behavior: none and calls switchPlan to preserve remaining credits
  • In-app reactivate flow — Reactivate button in cancellation banner calls POST /api/billing/reactivate; clears cancel_at_period_end on Stripe and sets cancelAtPeriodEnd: false / cancelAt: null in DB immediately without leaving the app
  • POST /api/billing/cancel endpoint — sets cancel_at_period_end: true on Stripe, stores reason in subscription metadata, updates DB with cancelAtPeriodEnd: true and cancelAt from currentPeriodEnd; returns cancelAt for the frontend banner
  • POST /api/billing/downgrade endpoint — retrieves subscription, swaps price to STARTER in Stripe with proration_behavior: none, calls switchPlan to update plan label while preserving current credits; guards against downgrading from non-PRO plans
  • POST /api/billing/reactivate endpoint — removes cancel_at_period_end from Stripe, clears cancelAtPeriodEnd and cancelAt in DB; guards against calling when subscription is not cancelling
Improvements9
  • Credit carry-over display — billing page detects when remaining credits exceed the plan cap and shows a blue Carry-over chip on the credits card plus an explanatory note below the usage bar
  • Carry-over note is dynamic based on cancellation state — active subscription: 'resets to {planMax} on {date}'; cancelled subscription: 'all credits reset to 10 (Free plan) on {cancelDate} when your subscription ends'
  • Usage bar denominator fixed for carry-over state — bar uses planMax as the base so it shows 0% used when credits exceed the plan cap instead of a misleading full bar
  • Upgrade button stays visible when subscription is cancelling — retention hook; upgrading from a pending-cancel state clears the cancellation automatically
  • Downgrade button hidden when subscription is already cancelling — no point downgrading a plan already scheduled to cancel
  • Cancellation banner condition relaxed — shows on cancelAtPeriodEnd: true alone so portal-triggered cancellations (which set only cancel_at_period_end, not cancel_at) are caught correctly
  • CancelModal overflow fixed — max-h-[90vh] flex-col with scrollable body and sticky footer prevents layout breakage on small viewports
  • Custom dropdown in CancelModal — native select replaced with a styled component using ChevronDown + Check icons, outside-click handler, and red active-state to match the app theme
  • Google Tag Manager confirmed live — NEXT_PUBLIC_GTM_ID added to frontend .env.local; GTM container verified loading via Network tab (200 OK on gtm.js?id=GTM-N3WH9FB8)
Fixes6
  • Stripe Customer Portal removed — Manage button and createPortalSession no longer surfaced in the UI; all plan operations now handled in-app so users never leave the billing page
  • Duplicate subscription bug fixed — POST /api/billing/checkout now detects an existing active subscription and updates it in-place via stripe.subscriptions.update() instead of always creating a new Checkout session; returns { upgraded: true } on in-place update, { url } only for genuinely new subscriptions
  • Stale UI after cancel+upgrade fixed — in-place upgrade now calls activatePlan (resets credits to new plan level, clears cancelAtPeriodEnd and cancelAt) instead of switchPlan; frontend forces a hard reload via window.location.href = '/billing?upgraded=1' to bypass any cached plan state
  • 304 Not Modified caching on GET /api/billing/plan fixed — Cache-Control: no-store, no-cache, must-revalidate header added so the browser always fetches fresh data after an upgrade
  • Webhook handler always clears cancellation when subscription is not cancelling — changed else if (!newPlan) to plain else so any subscription.updated event with cancel_at_period_end: false clears cancelAtPeriodEnd and cancelAt in DB regardless of whether a plan change occurred
  • Stale Stripe subscription reference handled — try-catch around subscription retrieve/update in checkout handler clears stripeSubscriptionId from DB on failure and falls through to a new Checkout session so deleted test subscriptions never silently block upgrades
v4.05 Jun 2026

Full Stripe payment integration shipped — users can self-serve upgrade to Starter or Pro, manage subscriptions via Stripe Customer Portal, and cancel with automatic downgrade at period end. Webhook handlers cover the full subscription lifecycle including renewals, plan switches, and cancellations.

Features9
  • Stripe Checkout integration — POST /api/billing/checkout creates a hosted Stripe Checkout session; frontend redirects user to Stripe's payment page and back to /billing?upgraded=1 on success
  • Stripe webhook handler at POST /api/billing/webhook — verifies signature, handles checkout.session.completed (activate plan), customer.subscription.updated (plan switch + cancel at period end), and customer.subscription.deleted (downgrade to FREE)
  • Stripe Customer Portal — POST /api/billing/portal creates a portal session; Manage button on billing page redirects to Stripe's hosted portal for plan switching, card updates, and invoice history
  • Self-serve upgrade flow — Upgrade buttons on billing page and UpgradeModal redirect to Stripe Checkout; on return, plan and credits refresh immediately with a 3-second delayed re-fetch to catch late webhook activation
  • Subscription cancellation via portal — cancel_at and cancel_at_period_end detected from Stripe webhook; amber banner shown on billing page with cancel date and reactivate link; plan downgrades to FREE automatically when period ends
  • Plan switch detection in webhook — customer.subscription.updated checks new price ID against configured price IDs to detect portal upgrades/downgrades; calls switchPlan (keeps credits) vs activatePlan (resets credits) appropriately
  • cancelAtPeriodEnd and cancelAt fields added to UserPlan schema — Prisma migration applied; billing API returns both fields; frontend type updated
  • Stripe lib module — StripeClient instance created with correct API version (2026-05-27.dahlia); STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_STARTER, STRIPE_PRICE_PRO env vars added
  • Raw body middleware for webhook — express.raw() mounted at /api/billing/webhook before express.json() so Stripe signature verification receives the unmodified Buffer
Improvements9
  • activatePlan vs switchPlan split — activatePlan resets credits and billing period (used for new subscriptions, renewals, cancellations); switchPlan only updates plan label and unlimited flag, keeping remaining credits (industry standard for mid-cycle plan changes)
  • resolveUserId helper — subscription webhook handlers fall back to stripeCustomerId DB lookup if userId is missing from Stripe metadata, making all handlers resilient to metadata gaps
  • Credit usage bar fixed for carried-over credits — when credits exceed plan max after a downgrade, totalCredits uses the higher value so bar never shows negative usage
  • Billing page refetches on ?upgraded=1 query param — useEffect depends on upgraded flag; additional 3-second delayed refetch catches late webhooks; credits-updated event fires to refresh nav badge
  • Upgrade success banner on billing page — green banner with CheckCircle shown when landing from Stripe with ?upgraded=1
  • Cancellation amber banner on billing page — shows cancel date, access-until message, and inline Manage reactivation link when cancelAtPeriodEnd is true
  • Manage button on billing page — only shown for STARTER and PRO plans; opens Stripe portal; shows Loading... while session is being created
  • UpgradeModal updated — plan cards are now selectable (defaults to PRO); upgrade button redirects to Stripe Checkout instead of calling upgrade API directly; shows Redirecting... while session is being created
  • Stripe customer created and stored on first checkout — stripeCustomerId saved to UserPlan on first session creation and reused on subsequent checkouts
Fixes3
  • Webhook event logging added for debugging — event type, metadata, cancel_at_period_end, and cancel_at logged on every webhook hit to diagnose silent handler failures
  • switchPlan no longer clears cancelAtPeriodEnd — previously the cancel fields were wiped during plan detection even when no plan change occurred, preventing the cancellation banner from appearing
  • Plan switch only triggers when price actually changed — metaPlan from subscription metadata compared against newPlan from price ID lookup to avoid calling switchPlan on every subscription.updated event
v3.94 Jun 2026

Full credit-based pricing system shipped — Free, Starter, Pro, and Custom plans with per-contest credit costs based on duration. Google Tag Manager added for analytics. Pricing page, billing dashboard, and upgrade modal all live. Admin plan management endpoint added. Production database migration handled safely with no data loss.

Features12
  • Google Tag Manager added via @next/third-parties — GTM container injected into root layout using NEXT_PUBLIC_GTM_ID env var; renders nothing if the var is absent so dev environments are unaffected
  • UserPlan model added to Prisma schema — tracks plan type, credit balance, billing period dates, and Stripe fields (nullable, ready for Phase 2 payment integration); auto-created as FREE on every new registration
  • Credit enforcement middleware — checkCredits runs before POST /api/contests; blocks creation if insufficient credits with INSUFFICIENT_CREDITS error code; Free plan additionally blocked from durations over 15 minutes with PLAN_DURATION_LIMIT error code; ADMIN and SUPER_USER bypass all checks
  • Credits deducted on contest start — credits are consumed when the contest transitions from WAITING to RUNNING, not on creation, so users are never charged for contests they cancel before starting
  • Lazy period reset — credit period is automatically reset when a user hits any credit-checked endpoint after their period has expired, no cron job required
  • Admin plan management endpoint — PATCH /api/admin/users/:id/plan lets admins set any user's plan, credits, and unlimited flag; used to assign Custom plan accounts manually
  • GET /api/billing/plan endpoint — returns current plan, credit balance, period dates, and credit cost table; auto-creates FREE plan for users registered before billing was introduced
  • Pricing page at /pricing — public marketing page with plan comparison cards, credit cost table, and CTA section; linked from HomeNav; consistent with homepage design system
  • Billing dashboard at /billing — shows current plan badge, credits remaining with usage bar, reset date, credit cost reference, and plan comparison table; linked from dashboard sidebar
  • Upgrade modal — global modal triggered whenever any API call returns INSUFFICIENT_CREDITS or PLAN_DURATION_LIMIT; shows plan options and routes to /billing; mounted in root providers so it works on any page
  • Credit cost preview on contest creation — duration selector shows live credit cost and current balance inline so users know exactly what a contest will cost before submitting
  • Billing nav item added to dashboard sidebar with credit card icon; Pricing link added to public HomeNav
Improvements2
  • Database migration strategy — production had a ghost lobbyStartedAt column from out-of-order migration replay; resolved with a safe IF EXISTS drop migration marked as already-applied on dev, then clean add_user_plan migration applied to both environments with no data loss
  • 263 existing users backfilled with FREE UserPlan rows and correct starting credits; existing rows with stale 5-credit default updated to 10
Research2
  • Pricing model researched and designed — credit-based system chosen over flat per-contest pricing; 1 credit unit = 10 credits per 15-minute contest with 10 users at ~$1.30 real cost; breakeven and margin analysed across all plan tiers before finalising numbers
  • Plan tiers finalised — Free (10 credits/month, 15 min contests only), Starter ($19/month, 100 credits), Pro ($49/month, 300 credits), Custom (unlimited, contact us); credit costs: 15 min = 10 credits, 30 min = 20 credits, 1 hr = 40 credits, 2 hr = 80 credits
v3.82 Jun 2026

User role management system shipped — Admin, Super User, and Normal roles added platform-wide. Admins can view, promote, demote, and delete users from a new in-dashboard management panel. CI/CD pipeline hardened with automatic database migrations on every deploy. Monetisation and recruiting-tool strategy researched and documented.

Features8
  • Role system added — three roles introduced: NORMAL (default for all signups), SUPER_USER (elevated limits, no restrictions), and ADMIN (full platform control including user management)
  • Role field added to User model via Prisma migration — all existing users defaulted to NORMAL; Role enum created in PostgreSQL alongside the column
  • First admin bootstrap — FIRST_ADMIN_EMAIL env var promotes the matching user to ADMIN automatically on server startup; no manual SQL or database access required after initial setup
  • requireRole() middleware added — protects any route by role; returns 403 Insufficient permissions for unauthorised access; composable with existing requireAuth middleware
  • Admin API routes — GET /api/admin/users (paginated, searchable by email or username), PATCH /api/admin/users/:id/role (change any user's role), and DELETE /api/admin/users/:id (remove a user); all routes protected by ADMIN role
  • Admin Users page at /admin/users — full user management table with search, role dropdown per row (instant save), delete button with confirm modal, and pagination; accessible only to ADMIN users
  • Manage Users link in dashboard sidebar — appears only when user.role === ADMIN, under a separate Admin section label with a shield icon
  • Admin and Super User badges on dashboard greeting — red Admin badge or blue Super User badge displayed next to the username for elevated roles
Improvements6
  • Role included in JWT token payload — role is signed into the JWT at login and attached to req.user on every request with no database round-trip
  • Token refresh always pulls latest role from DB — /auth/refresh re-issues a token with the current role from the database so role changes are reflected on next page load without a logout/login cycle
  • useTokenRefresh hook updated — now forces a refresh on every app mount in addition to the existing expiry check, ensuring role changes are reflected immediately without requiring logout
  • CI/CD deploy script updated — prisma migrate deploy added between prisma generate and npm run build so every merge to main automatically applies pending database migrations to production
  • Migration file created for role column — 20260602000000_add_user_role migration documents the Role enum and role column so production database is updated cleanly on next deploy without manual SQL
  • Production .env hardened — MOCK_AI, REDIS_URL, AI_QUEUE_CONCURRENCY, and FIRST_ADMIN_EMAIL were all missing from the production environment; all added
Fixes1
  • Stale .next folder removed from backend directory and added to backend .gitignore — was accidentally created by running a Next.js command in the wrong directory
Research4
  • Monetisation strategy researched — three distinct user types identified: Solo Dev (free forever, participant not host), Internal Team (subscription ~$39/month, runs regular contests), Recruiting Company (pay-per-contest ~$59, runs hiring challenges)
  • AI cost per contest calculated — Claude Haiku 4.5 with prompt caching costs ~$0.56 for a 10-person contest at 8 prompts average; rate limit constraint (not model speed) is the real scaling ceiling for large contests
  • Recruiting tool angle defined — platform differentiated from HackerRank/Codility by AI prompt transparency (promptsPublic field already exists), peer voting as signal, and speed-under-pressure format; recruiting tier to include candidate export, apply button, and judge panel
  • Role system feature brief written for lead review — documented business case (AI cost exposure, no upgrade path, no platform control), tier feature matrix, and priority rationale; role system identified as prerequisite for all monetisation features
v3.71 Jun 2026

Backend CI/CD pipeline set up with GitLab Runner for automatic deployments on every merge to main. www.vibe-coding-game.com added to Vercel production. Full SEO overhaul: robots.txt and sitemap.xml added, metadata added to all 19 pages, protected routes marked noindex, and dynamic pages use server-side generateMetadata. Homepage converted from a client component to a Server Component for faster indexing and a smaller JS bundle.

Improvements11
  • Backend CI/CD pipeline configured with GitLab Runner — pushes to main automatically trigger a production deployment without manual intervention
  • www.vibe-coding-game.com added to the Vercel production environment
  • robots.ts added — defines crawl rules for all routes; public pages (/, /changelog, /s/, /login, /register, /forgot-password) allowed; auth callbacks, dashboard, contest rooms, and teams disallowed to avoid wasting crawl budget
  • sitemap.ts added — generates /sitemap.xml with homepage, changelog, login, and register; served as a dynamic Next.js route at /sitemap.xml
  • Root layout updated — metadataBase set to https://www.vibe-coding-game.com, title template ("%s | Vibe Coding Game") added so all pages get consistent suffixed titles, OG image URL corrected from http to https
  • Metadata (title + description) added to all 19 pages — previously only login and register had titles; all others inherited the generic root fallback
  • Protected route groups (auth, dashboard, contest layouts) marked robots: noindex — prevents search engines from indexing authenticated-only pages
  • Dynamic pages (contest/[id], results, join/[code]) use generateMetadata with server-side API fetch — browser tab title and link previews now reflect the actual contest name
  • Public share page /s/[id] gets dynamic OG metadata — title and description include the submission author's username and contest name, giving rich previews when shared on social media
  • Homepage converted from 'use client' to a Server Component — all text content (headings, paragraphs, testimonials) rendered as static HTML on the server; interactive parts (nav, CTA buttons, framer-motion animations) extracted into HomeNav, CTAButton, and AnimateSection client components; reduces JS bundle size and ensures Google indexes content on the first crawl
  • Homepage hero section animations removed — badge, h1, paragraph, CTA buttons, and ProductPreview were all wrapped in framer-motion AnimateSection with initial opacity: 0, causing a blank page until JavaScript hydrated; replaced with plain HTML that renders immediately on first paint, fixing the blank page flash and improving LCP and Total Blocking Time
v3.626 May 2026

Merged all development changes into production — Redis AI queue, session store, rate limiter, and participated contests endpoint now live. Auth interceptor deadlock bug fixed so expired tokens redirect cleanly instead of hanging the page. Queue stress-tested with 20 concurrent users via k6 (100% pass rate). Homepage HIW section upgraded from PNG to SVG images.

Improvements5
  • Merged development → main for both backend and frontend — Redis-backed AI queue, session store, rate limiter, participated contests endpoint, queue position UI, cancel AI request, and dashboard stats all promoted to production
  • 4 Prisma migrations applied to production database — add_max_participants, add_scheduled_at, remove_scheduling, add_lobby_started_at
  • HIW (How It Works) section on homepage upgraded from PNG to SVG images — sharper rendering at all screen sizes
  • HIW image wrapper cleaned up — removed border, shadow, and background colour; switched to object-cover for full-bleed display
  • bullmq version synced between dev and production (v5.77.3) — committed on development branch and merged to main to maintain clean git history
Fixes2
  • Missing bullmq and ioredis packages installed on production — build was failing with TS2307 'cannot find module' errors after the development merge
  • Auth interceptor deadlock fixed — when a token expired, the /auth/refresh call would re-enter the interceptor, see isRefreshing=true, queue itself as a subscriber, and hang the page forever; now the interceptor skips retry logic for the refresh endpoint itself so expired sessions redirect cleanly to login
Testing1
  • AI queue stress-tested with k6 — 20 concurrent VUs over 1m45s with MOCK_AI=true (no Anthropic tokens consumed); 1320/1320 checks passed, 0 HTTP failures, 0 stream misses, 38 requests correctly queued under burst load, p(95) response time 553ms
v3.525 May 2026

Full dashboard redesign with live stats, a live-contest alert, and Contests I Joined list. Code persistence fixed so reloading the contest page restores your saved work. AI chat now shows every step Claude takes in real-time with a cancel button to abort stuck requests. LLM made contest-time aware with escalating urgency as the clock runs down. AI model config corrected to Haiku primary with Sonnet fallback. Load tests verified 7 concurrent users with 100% success.

Features7
  • Dashboard fully redesigned — live contest alert with Resume button at the top, four stats cards (Wins, Games, Upvotes, Tadas), quick action cards (Host a Contest, My Contests), Contests I Joined list on the left, Platform Activity and Top Performers sidebar on the right
  • Contests I Joined section on dashboard — GET /contests/participated endpoint added to backend; lists all contests the user joined but did not create, ordered by most recent, with status badges
  • Live contest alert auto-detects the currently running contest and shows a red banner with a Resume button — prioritises joined contests over created ones
  • Code is never lost on page refresh — the coding environment now waits for your saved draft to load from the database before the editor mounts, so refreshing or accidentally closing the tab always restores exactly where you left off
  • AI chat real-time activity log — every step Claude takes (Thinking…, Creating file, Editing file, Reading file, Writing response) appears as a live entry; active step shows animated bouncing dots, completed steps show a green checkmark
  • LLM time awareness — contest time remaining is sent from VibeCodingEnv on every AI request and prepended to the user message; urgency escalates automatically: >10 min = time label only, 5–10 min = 'be concise', 2–5 min = 'FOCUS: working code only', <2 min = 'URGENT: only fix critical issues'
  • Cancel button for AI requests — if Claude gets stuck or takes too long, a Cancel button appears inline next to the active step's bouncing dots; clicking it immediately aborts the request, marks the step as 'Cancelled', and re-enables the chat input so you can try again without refreshing
Improvements4
  • Dashboard contest lists poll every 10 seconds so live contest alerts and status badges update automatically without a page reload
  • Queue vs active visual distinction in AI chat — waiting-in-queue state shows a blue pulsing Clock icon; actively processing shows amber bouncing dots; transitioning from queue to active injects a 'Starting…' entry so users can see the moment work begins
  • timeRemaining threaded end-to-end: VibeCodingEnv → AiChatPanel prop → fetch body → ai.controller → BullMQ job data → runAiChat() service parameter
  • 'Join a Contest' quick action renamed to 'My Contests' to match where it actually navigates
Fixes3
  • AI chat model name was hardcoded as 'Haiku 4.5' — now reads the model field from the session_id SSE event so the header always reflects the actual model in use
  • AI model config corrected — primary model was accidentally set to claude-sonnet-4-6 for both primary and fallback, making fallback a no-op; fixed to claude-haiku-4-5-20251001 as primary and claude-sonnet-4-6 as fallback on overload
  • My Contests card removed from dashboard — dedicated contests page already covers this; dashboard left column now shows only Contests I Joined
Testing1
  • Load test scripts rewritten with realistic file payloads — DEFAULT_INITIAL_FILES (index.html, style.css, script.js) and AFTER_FIRST_EDIT changedFiles added to match real VibeCodingEnv behaviour; queuedHits counter added to track queue engagement under load
v3.422 May 2026

Reverted contest scheduling feature after instability in the dev environment. AI queue hardened with overloaded-error detection, user-friendly retry messaging, and a 4-retry / 120 s timeout safety net. AI sessions migrated from in-memory to Redis for durability across restarts, with per-user rate limiting enforced at the queue layer.

Improvements5
  • Overloaded-error detection added to AI queue worker — when Anthropic returns overloaded_error or an 'Overloaded' message, the user receives 'Claude is busy right now. Please try again in a moment.' instead of a generic failure
  • AI client hardened with maxRetries: 4 and a 120 s timeout (down from the 600 s SDK default) — silently retries transient Anthropic errors up to 4 times before surfacing a failure, while freeing the worker slot quickly on genuinely hung requests
  • AI sessions migrated from in-process Map to Redis (key: session:{sessionId}, 2 h TTL) — conversation history survives server restarts and is safe across multiple worker processes
  • Per-user rate limiter implemented at the AI queue layer — Redis INCR/EXPIRE pattern enforces a 10-requests-per-minute ceiling per user; excess requests are rejected before entering the queue
  • AbortController wired from HTTP disconnect event through the BullMQ job to the in-flight Anthropic stream — client navigating away immediately cancels the AI request and frees the worker concurrency slot
Fixes1
  • Contest scheduling feature fully reverted — scheduled start times, slot-assignment algorithm, queue-position UI, and related DB columns (scheduledAt, lobbyStartedAt) removed from backend and frontend; original 120 s lobby countdown restored
Testing2
  • AI queue feature tested end-to-end — verified overloaded-error detection surfaces the correct user-facing message, confirmed maxRetries: 4 retries transient failures before giving up, and validated that rate limiting rejects excess requests at the queue layer
  • Contest scheduling revert tested — confirmed 120 s lobby countdown displays correctly, creator can manually start the contest, and new contests no longer jump directly to the coding environment
v3.321 May 2026

Implemented deterministic reservation-based contest scheduling: queued contests now receive an exact reserved start time rather than a vague estimate. Fixed AI chat freeze under concurrent load via Anthropic SDK timeout and immediate AbortController cancellation on disconnect. Load test email overuse resolved by auto-verifying test addresses.

Features3
  • Deterministic contest scheduler — when platform capacity is full, a new contest is assigned an exact scheduledAt timestamp via a slot-assignment algorithm that inspects all running and queued contests; BullMQ fires the start at the reserved time rather than polling
  • Redis SET NX mutex for atomic slot assignment — a 30-second distributed lock prevents concurrent scheduler calls from double-booking the same capacity slot
  • Queue position and exact start time shown to waiting participants — UI displays the scheduled start time (e.g. 'Starts at 3:45 PM'), a live countdown, and the contest's position in the queue (#1, #2, …)
Improvements4
  • Contest scheduler BullMQ worker added to server startup alongside the AI worker; delayed jobs use contestId as jobId for safe cancellation
  • rescheduleWaiting() recomputes all queued contest slots in FIFO (createdAt) order whenever a contest is deleted, keeping reserved times consistent
  • scheduledAt field added to Contest model with Prisma migration; getContestById includes a safety-net branch that starts the contest immediately if the BullMQ job was lost after a server restart
  • AI chat freeze prevention — Anthropic SDK timeout reduced from the 600 s default to 120 s to free worker slots after hung requests; AbortController signal wired from HTTP close event through the BullMQ worker to the in-flight Anthropic stream, cancelling it immediately on client disconnect
Fixes1
  • Load test Resend overuse fixed — registrations using RFC-reserved .invalid email addresses are auto-verified and skip Resend entirely; real user email verification flow is unaffected
v3.220 May 2026

Real-environment stress test with ~10 concurrent users exposed a critical AI scalability bottleneck: the LLM was silently hanging for some users due to Anthropic rate limits, a missing client timeout, and an unbounded agentic loop. Root cause fully diagnosed; a queue-based architecture planned as the fix before next test.

Improvements1
  • Frontend-dev PM2 process switched from next start (production build) to next dev — hot reload now active on port 5003 for the dev environment; package.json dev script updated to port 5003 to avoid conflict with frontend-prod on 5002
Testing2
  • Ran first real-environment test with ~10 concurrent users across active contests — confirmed core contest flow (join, code, submit, vote) works end to end in production
  • Identified LLM freezing for a subset of users under concurrent load — AI requests appeared completely stuck with no error shown, traced to silent SDK retry behaviour on Anthropic 429 rate-limit responses
Research6
  • Root cause 1 — no timeout or maxRetries cap on the Anthropic client (default timeout 600 s, 2 auto-retries): when rate-limited, the SDK retries silently for up to 60 s while the SSE heartbeat keeps the connection alive, making the UI appear frozen
  • Root cause 2 — agentic loop too aggressive for concurrent use: maxLoopIterations 8 × maxOutputTokens 6 000 means one user turn can consume up to 48 000 output tokens; at 10 simultaneous users that is 480 000 tokens/min against a Tier 2 ceiling of 50 000 tokens/min
  • Root cause 3 — no global concurrency control: all users hit the Anthropic API simultaneously with no queue, no back-pressure, and no user-facing wait indicator; first users succeed, late users hang
  • Root cause 4 — production backend logs suppressed (quiet mode): AI errors and token usage were invisible during the test, making diagnosis require post-hoc code inspection rather than live log monitoring
  • Scalability ceiling calculated for single Anthropic API key with optimised settings (maxOutputTokens 2 000, maxLoopIterations 4): Tier 2 ~40–50 users, Tier 3 ~80–100 users, Tier 4 ~150–200 users — beyond that, key pooling or an enterprise agreement is required
  • Growth path defined: launch on Tier 2 + request queue (BullMQ + Redis, 10 workers); tier upgrades happen automatically at $40 / $200 / $400 cumulative API spend; key pooling added when simultaneous user count exceeds single-key ceiling
v3.119 May 2026

Backend process management migrated to PM2 for auto-restart and reboot persistence, production PostgreSQL migrations applied, and frontend deployed to Vercel with API proxy rewrites configured.

Features1
  • Frontend deployed to Vercel — static and server-rendered pages served via Vercel's global CDN; custom domain configured and production environment variables set
Improvements3
  • Backend process management migrated to PM2 — server auto-restarts on crash; pm2 startup and pm2 save configured so the process survives server reboots without manual intervention
  • Production PostgreSQL migrations applied — latest schema changes pushed to the live database and Prisma client regenerated to match
  • Vercel API proxy configured — NEXT_PUBLIC_API_URL set to /api and BACKEND_URL pointed to the production server so all frontend API calls are proxied through Next.js rewrites to the backend rather than hitting localhost
v3.018 May 2026

Homepage 'How it works' section redesigned from a dark card grid to an alternating two-column layout with real step screenshots, max-width standardised to 52rem across all sections, and '60 seconds' copy removed in favour of cleaner language.

Features1
  • Step screenshot images added — HIW-1.png, HIW-2.png, HIW-3.png added to public/images and loaded as object-contain images in rounded bordered cards alongside each 'How it works' step
Improvements4
  • 'How it works' section redesigned — replaced the 3-column dark navy card grid with an alternating two-column layout: large red step number + icon + title + description + 'Get started' CTA on one side, and a screenshot image on the other; odd steps flip to image-left via md:flex-row-reverse
  • 'How it works' section background changed from white to #f7f8fa and header bottom margin increased from mb-12 to mb-16 for better visual breathing room; step number size bumped from text-3xl to text-5xl
  • Max-width standardised to max-w-[52rem] across hero, feature strip, 'How it works', use cases, testimonials, and footer — previously a mix of max-w-4xl and max-w-3xl
  • Copy tightened — 'Create a challenge in 60 seconds' shortened to 'Create a challenge'; hero subtitle 'Host a timed coding contest in 60 seconds' simplified to 'Host a coding challenge'; feature strip chip renamed from '60-second setup' to 'Quick setup'
Fixes1
  • code-env.png removed from public/images — replaced by the three HIW step screenshots
v2.915 May 2026

Full platform visual rebrand: CoderPad-inspired color system with exact red (#d91629) and dark navy (#0a2540) rolled out across all 36 files, homepage fully redesigned with a white background and dark card variants, logo switched to Nunito for circular warmth, and the coding environment headers aligned and given a matching dark style.

Features1
  • Homepage fully redesigned with a CoderPad-inspired layout — white page background throughout; pill-shaped buttons (rounded-full); hero badge with solid #d91629 fill; feature strip in dark navy #0a2540 with full-white text and icons; 'How it works' cards in dark navy; 'Vibe code live' step card in solid red; CTA section with a red card and a white-on-hover primary button
Improvements8
  • Global color system applied to all 36 files — red-500 → #d91629, red-600 → #c21425, red-50/100 → opacity variants, text-gray-900 → text-[#0a2540], bg-gray-900 → bg-[#0a2540]; covers all auth pages, dashboard pages, contest pages, and every UI component
  • Logo redesigned with Nunito font (extrabold weight, circular letterforms) — variant prop added (light | dark) so the logo renders correctly on both white and dark backgrounds
  • Nunito loaded via next/font/google with weights 400–900 and exposed as --font-nunito CSS variable in root layout alongside Inter and JetBrains Mono
  • Testimonials updated to real team members: Ijaz Ur Rahim (Sr. Python Engineer), Jamal Derrick (Lead Engineer), Shehriar Awan (Ex-head of Moral Support) — quotes rewritten as natural, human-sounding feedback about the platform
  • CTA section: credit card copy removed, hover state fixed so the primary button background turns red with white text instead of the previous broken state
  • Separator border added between the hero section and ProductPreview for clear visual separation, matching the border below 'How it works'
  • Coding environment: chat panel header and preview panel header height pinned to h-9 so they sit flush with each other across the two-pane layout
  • Preview panel header background changed to #1e1e1e (VSCode dark) with a #3c3c3c border, matching the AI chat panel — text brightened to gray-200 and a green pulsing dot added before the 'live' label
v2.814 May 2026

Voting timer made server-authoritative with a beep countdown, team rename and client-side refresh added, AI model name shown in chat header, contest creation gains AI task generation with sandbox-aware prompts and quick-pick chips, markdown rendering in AI chat responses, and a popular-tasks picker to reuse past contest briefs.

Features9
  • Server-authoritative voting timer — votingEndsAt stored on the Contest model and set on both the all-submit path (submission.service.ts) and the auto-end poll (contest.service.ts); formula is 5 min base + 1 min per submission; results page reads the server deadline directly so all participants see an identical countdown regardless of load time
  • Countdown beep on voting timer — 880 Hz sine beep fires at 3, 2, and 1 seconds remaining, matching in-contest timer behavior; timer highlight turns red in the last 60 seconds
  • Team rename — OWNER can click the pencil icon next to the team name to edit it inline; validates against existing team names and saves via PATCH /teams/:id; Cancel closes the editor without saving
  • Team client-side refresh — refresh button (spinning RefreshCw icon) on the My Team page re-fetches team data without a full page reload
  • AI model name shown in chat panel header — 'Claude · Haiku 4.5' displayed next to the bot icon so participants always know which model they are using
  • AI task generation on contest creation page — describe an idea in a textarea or pick a quick-chip template; Claude generates a full contest brief (title + description) with sandbox constraints baked into the system prompt; result fills the form directly and closes the panel
  • Quick-pick AI prompt chips — 10 one-click chips covering mini game, multi-step wizard, search & filter, component kit, live dashboard, puzzle, creative canvas, data viz, music/sound, and expressive timer — each designed to test build ability, architectural thinking, or creative direction rather than just visual polish
  • Reuse a popular task on contest creation page — amber 'Reuse a popular task' section with Most Played (ranked by participant count) and Most Liked (ranked by vote + plusOne engagement) tabs; clicking a row fills the form title and description and closes the panel; lazy-loads on open, re-fetches on tab switch
  • Markdown rendering in AI chat panel — assistant responses now parse and render **bold**, `inline code`, # / ## / ### headings, - bullet lists, and --- dividers instead of showing raw markdown syntax; dark-themed color palette matches the VSCode-style panel
Improvements2
  • Esc button at top of contest form card — visible when title or description is non-empty; clears both fields with shouldValidate: false so no validation errors flash, then auto-focuses the title input
  • GET /contests/recent now includes description in all query branches (latest, popular, best) so the popular-tasks picker can prefill the full contest brief
Fixes2
  • votingEndsAt not set when all participants submitted early — submission.service.ts was ending the contest without computing the voting window, causing the timer to close immediately on the results page; fixed by adding the same votingEndsAt calculation to the all-submit path
  • React hooks ordering error on results page — useCountdownBeep was called after an early return conditional, violating Rules of Hooks; moved all derived variables and the hook call above the loading/error early returns
v2.713 May 2026

Session token expiry fixed so users are never kicked mid-contest, share prompt moved from the coding environment to the results page, and a full pricing and LLM cost analysis was completed before deciding to hold off on payments and full-stack contests for now.

Features2
  • Proactive token refresh hook (useTokenRefresh) mounted globally in providers — decodes JWT expiry from localStorage on page load and every hour; silently refreshes and updates the store if less than 24 hours remain, preventing expiry from ever being hit mid-session
  • Share prompt button on results card — own submission cards show a dashed 'Share prompt' button when AI prompts are stored but not yet public; clicking calls PATCH /contests/:id/submissions/prompts/public and immediately shows the Prompt button to all viewers
Improvements3
  • Share prompt moved from the coding environment header toggle to the results page — prompts are now always stored silently on every auto-save and submit; the toggle is removed from the coding env header
  • promptsPublic boolean field added to Submission — prompts stay private until the owner explicitly shares them from the results page; leaderboard sends full prompt data to everyone if public, or only to the owner (for the share button) if private
  • Backfilled promptsPublic=true for 5 existing submissions that already had prompts stored under the old toggle system so they continue to appear on the results page
Fixes1
  • Fixed mid-contest token expiry kicks — JWT was 7 days with no refresh; if it expired during a contest the 401 interceptor immediately redirected to /login; added POST /auth/refresh endpoint and updated the interceptor to silently refresh and retry the failed request before redirecting; concurrent 401s during refresh are queued and replayed with the new token
Research2
  • Full pricing and LLM cost analysis completed — Claude Haiku 4.5 at $0.80/1M input and $4.00/1M output tokens costs roughly $0.09 per realistic user session (10 prompts, 2 tool loops); free tier of 5 AI prompts costs ~$0.03–0.05 per user; paid entry at $5 covers LLM cost ~50×; Stripe pay-per-entry with manual winner payout identified as the recommended v1 monetisation path
  • Full-stack contest feature researched and scoped — would require a sandboxed server-side execution environment (Docker containers or Firecracker microVMs), a port-proxying layer for live preview, and significant infra cost per session; decided not to implement for now; vanilla HTML/CSS/JS sandbox remains the contest format
v2.612 May 2026

Team system shipped — players can create and join teams, compete with a team badge on leaderboards and results, and browse the new Top Teams leaderboard. Auth gains a forgot-password flow with Resend reset emails, and password fields across all auth forms now have a visibility toggle.

Features12
  • Team creation — users can create a team with a unique name; creator is assigned the OWNER role and receives a shareable 8-char hex invite code
  • Join team via invite code — paste the code or click an invite link; re-joining your own team is idempotent and returns the team silently instead of erroring
  • Leave team (members) and delete team (OWNER) — owners must delete rather than leave to prevent orphaned teams
  • Invite code rotation — OWNER can regenerate the invite code at any time from the My Team page
  • Email team invites via Resend — owner enters an email address; recipient receives a branded invite email with a one-click join button that preserves the ?code= param through login and email-verification redirects
  • Team badge on results page — blue pill shown next to player name on all submission cards, the winner card, the tie section, and inside code-viewer and prompt-viewer modals
  • Players / Teams toggle in TopPerformers sidebar — Teams tab ranks by summed member tadas then upvotes; toggle only shown on the results page where submission data is available
  • Team badge next to player name in TopPerformers Players tab — blue pill shows team affiliation alongside the two-line stats row
  • Top Teams leaderboard page at /top-teams — ranked cards showing team icon, name, member count, and combined stats (wins, tadas, upvotes, games); click any card to expand the full member roster with individual stats and a Crown icon marking the owner
  • Forgot password flow — POST /auth/forgot-password sends a Resend email with a 64-char hex token link valid for 1 hour; POST /auth/reset-password validates the token and updates the password hash; endpoint always returns 200 to avoid revealing whether the email exists
  • PasswordResetToken model added to Prisma schema — stores token (unique), userId, expiresAt; cascade-deleted when the user is deleted; one active token per user (previous token deleted before issuing a new one)
  • Password visibility toggle added to all password inputs — eye / eye-off icon button on login, register, new password, and confirm password fields; tabIndex=-1 so tab navigation skips the button
Improvements5
  • TopPerformers players tab redesigned — two-line row: avatar + name + team badge on line 1, stats (wins, tadas, upvotes, games) indented pl-8 on line 2; rank badge matches results card (w-6 h-6 rounded-md with red/gray colored backgrounds)
  • TopPerformers teams tab matches player layout — h-6 w-6 rounded-md team icon, same two-line structure, font-semibold stats
  • Game count uses a Gamepad2 icon and member count uses a Users icon in both TopPerformers and the Top Teams leaderboard — icon-only compact format replaces plain text suffixes
  • ChevronDown replaces ▲/▼ text in leaderboard team cards — rotates 180° on expand via CSS transition
  • Top Teams leaderboard page uses the dashboard layout's p-8 / max-w-5xl container — no redundant padding or max-width on the page itself
Fixes5
  • New Contest nav item removed from the dashboard sidebar — route still exists at /contests/new, just not surfaced in the nav
  • EmailVerificationGuard used usePathname() which strips query params — redirects to /login lost the ?code= from team invite links; fixed to build the redirect using window.location.pathname + window.location.search
  • Join page useSearchParams() returned empty on first render due to Next.js App Router hydration timing — switched to reading window.location.search inside a useEffect
  • Re-clicking a team invite link after already joining threw 'You are already in a team' — joinTeam service made idempotent: returns the existing team when the user is already a member of the target team
  • Prisma client not regenerated after schema push — PasswordResetToken table existed in the DB but prisma.passwordResetToken was undefined at runtime; fixed by running prisma generate after db push
v2.511 May 2026

Persistent per-user stats, a Top Performers leaderboard, and a contextual sidebar on both the results page and contest creation page showing best and most-played tasks.

Features7
  • UserStats table added — tracks gamesPlayed, totalUpvotes, totalTadas, wins, and lastContestId per user; stats persist independently so they are never affected by contest deletion
  • ContestWinner table added — records the 🎉 winner of each ended contest with no FK constraints, so the record survives if the contest or user is later deleted; win counts stay accurate when TADA votes change after the contest ends
  • Top Performers sidebar added to the results page — shows the all-time leaderboard ranked by wins then tadas then upvotes, with avatar, username, and stats; refreshes every 30 seconds
  • Recent Contests sidebar added to the results page — shows latest or best-by-engagement ended contests with a Best / Latest toggle; card height is fixed with internal scroll so switching tabs causes no layout shift
  • Task Insights sidebar added to the contest creation page — two cards: Most Liked Tasks (ranked by total tadas + upvotes) and Most Played Tasks (ranked by total participants); both group by contest title across all runs so repeated tasks accumulate counts correctly
  • GET /api/users/top-performers and GET /api/users/:userId/stats endpoints added — public, no auth required
  • GET /api/contests/recent?sort=latest|best|popular endpoint added — public; best sorts by summed engagement, popular sorts by summed participants, both group by normalised title across all runs; popular counts all contest statuses while best counts only ENDED contests
Improvements3
  • Backfill script (scripts/backfill-stats.ts) written and run — populated UserStats and ContestWinner from all historical submissions, votes, and plusOnes so existing users appear in Top Performers immediately
  • Results page layout widened from max-w-3xl single column to max-w-6xl two-column flex with a w-64 sticky sidebar on lg screens
  • Contest creation page layout widened to max-w-5xl two-column flex — form on the left, Task Insights sidebar on the right on lg screens
Fixes2
  • Back to room button removed from the results page
  • Back button removed from the contest creation page
v2.411 May 2026

Tested the v2.3 refactor end-to-end and fixed the AI chat SSE stream which had stopped working after the refactor — root cause traced to Express finalhandler destroying the socket when an error arrived mid-stream.

Improvements1
  • nginx /api/ai/chat location kept as a direct proxy to the backend (port 5000), bypassing the Next.js dev rewrite proxy — Next.js dev server closes upstream SSE connections immediately after forwarding the 200 headers, making it incompatible with streaming endpoints
Fixes3
  • Fixed AI chat SSE stream silently dropping mid-response — client was disconnecting immediately after receiving the 200 OK headers; traced to req.on('close') firing when Express consumed the POST body, not when the browser actually disconnected; switched to res.on('close') which fires only when the response connection truly closes
  • Fixed ERR_INCOMPLETE_CHUNKED_ENCODING on AI chat — Express 5 was catching errors thrown during SSE streaming and passing them to the error middleware, which then called res.json() on an already-started SSE response; added a res.headersSent guard to errorHandler so it exits silently when headers are already sent, preventing finalhandler from calling socket.destroy()
  • Hardened SSE write calls against race conditions — send() and the 20s heartbeat now catch socket write errors individually so a closed-socket exception cannot propagate to Express and trigger the error handler mid-stream
Testing1
  • Tested v2.3 refactor end-to-end — contest flow, AI chat, file editing, auto-save, and submit all verified working after the large-scale code reorganisation
v2.38 May 2026

Large-scale refactor: contest page collapsed from 810 lines to a thin orchestrator, auth middleware dropped its database round-trip, backend services cleaned up with extracted constants and helpers, and all inline TypeScript types moved to dedicated domain files.

Improvements11
  • Contest page (contest/[id]/page.tsx) refactored from 810 lines to ~19 lines — all editor, AI chat, preview, file-tree, and submit logic extracted into a new VibeCodingEnv component; page is now a pure orchestrator that resolves the contest and hands it off
  • VibeCodingEnv component created as a standalone self-contained coding environment — encapsulates Monaco editor, AI chat panel, live preview iframe, file tree, task card, submit button, and countdown header in one place
  • Auth middleware changed from async to sync — removed Prisma DB lookup on every authenticated request; user identity (id, email, username) is now resolved directly from the verified JWT payload, cutting per-request auth overhead to near zero
  • req.user type in express.d.ts updated from PrismaUser to a plain { id, email, username } shape, matching the JWT-only middleware
  • GithubRepo interface moved from auth.service.ts to a new backend types/github.ts file and re-exported — keeps service files free of standalone type declarations
  • CONTEST_INCLUDE constant extracted in contest.service.ts — three repeated identical Prisma include objects replaced with a single shared constant
  • LOBBY_DURATION_SEC exported as a named constant (120) from contest.service.ts — was previously an inline magic number scattered across the service
  • saveDraft and submit controllers in submission.controller.ts simplified — no longer destructure individual fields; pass req.body directly to the service layer
  • submission.service.ts: SaveDraftInput and SubmitInput types derived from Zod schemas and used as typed data parameters; serializePrompts() extracted as a named helper to avoid repeated inline logic
  • AiChatPanel inline type definitions (ToolName, ToolCall, ChatMessage, AiEvent, AiChatPanelProps) moved to frontend types/ai.ts — component file now imports instead of declaring
  • AuthState interface moved from auth.store.ts to a new frontend types/auth.ts file — Zustand store imports from there
v2.27 May 2026

Results page gains a feedback system, a 🎉 winner confirmation modal, and toggleable ThumbsUp likes. Coding environment preview pane no longer resizes or reloads when switching between Chat and Code tabs.

Features5
  • Feedback system on results page — participants can write private feedback on any other submission via a PenLine button; feedback can be edited after submission; button turns red and shows 'Feedback given' once sent
  • Feedback reader for own submission — MessageSquare button on your own card opens a modal listing all feedback you received, with author avatars and response count badge
  • feedbackCount field added to leaderboard entry — own submission feedback badge shows the live count of responses received
  • 🎉 winner confirmation modal — casting your single 🎉 vote now shows a confirmation dialog ('Elect a winner?') naming the recipient and warning the vote is final; Cancel keeps the vote uncast
  • Share prompts toggle in the coding environment header — off by default (prompts are private); turning it on includes every AI prompt in the submission so they appear on the results page; toggling off sends an empty prompts array; state is synced via ref so auto-save and manual submit both respect the latest setting
Improvements3
  • ThumbsUp (+1) vote is now toggleable — clicking again removes your existing like; tooltip updates to 'Remove your thumbs up' when already voted
  • Chat and Code left panels both set to w-2/5 and toggled with the hidden class so the preview's flex-1 width never changes regardless of which tab is active
  • Code editor and Chat panel are both always mounted (display:none when inactive) so Monaco state and AI chat session are preserved across tab switches
Fixes2
  • Fixed preview pane resizing when switching between Chat and Code tabs — Chat tab rendered preview at flex-1 (~60% width) while Code tab used w-1/2 (50%), causing the iframe to shrink and grow on every switch
  • Fixed preview iframe reloading on tab switch — each tab previously mounted its own <iframe>, so switching destroyed and recreated it; a single shared iframe is now always mounted
v2.16 May 2026

Tested v2.0 with the team and gathered UI feedback, then shipped a redesigned coding environment: chatbot + preview are the new default view, code editor moved to a secondary tab, hide-code toggle added to results and share pages, and responsive fixes across cards and modals.

Features3
  • Chat + Preview as the default coding view — contest room now opens with the AI chatbot (40% width) alongside the live preview (60%) by default; code editor accessible via a dedicated tab icon
  • "Hide code" toggle added to the code viewer modal — collapses the editor so the preview expands to full modal width; button turns red when active to show preview-only mode is on
  • "Hide code" toggle added to the /s/[id] public share page — same full-preview behavior; Copy link and Results button labels also hidden on mobile for a cleaner header
Improvements6
  • AI chat panel promoted from the cramped 176px file-tree sidebar to a full-height 40% panel, giving messages and the input box proper space
  • Chat input height increased — defaults to 2 rows with more padding, rounded-xl container, and max-height raised to 128px so longer prompts are comfortable to type
  • Tab buttons (Chat, Code, Task) all equal width (w-12) and icon-only, with styled tooltips that drop below the header on hover — arrow points up toward the icon
  • Results page submission cards now wrap vote buttons (Code, Prompt, +1, 🎉) to a second row on mobile so the rank badge and username are never squashed
  • Results page header stacks vertically on mobile; Share page button aligns correctly without the stray mt-7 offset
  • Code viewer modal stacks editor and preview vertically on mobile (each 50% of modal height) with a horizontal divider; modal header text labels hidden on small screens to prevent overflow
Fixes1
  • Preview scrolling fixed for user CSS overrides — buildPreview now injects overflow-y: auto !important on html and body so submissions that set overflow: hidden cannot block scrolling in the viewer
Testing1
  • Tested v2.0 end-to-end with the team — gathered feedback on layout, AI chat sizing, preview responsiveness, and overall contest UX; identified the chatbot panel being too small and the preview being unscrollable as top pain points
v2.05 May 2026

HTTPS enabled via Let's Encrypt, email delivery migrated to Resend with a verified domain, AI prompts saved per submission and viewable on the results page, preview iframe scroll fixed, and results page UI polished.

Features4
  • HTTPS enabled — Let's Encrypt SSL certificate issued for vibe-coding-game.com and www.vibe-coding-game.com via Certbot; nginx auto-redirects all HTTP traffic to HTTPS
  • AI prompt history saved per submission — every prompt the user sends to Claude during a contest is captured and stored alongside the submission code
  • Prompt viewer on results page — each submission card shows a Prompt button when AI prompts exist; clicking it opens a numbered chat-style modal listing every prompt the user sent
  • prompts column added to Submission model — stored as a JSON array string; schema pushed to production database and Prisma client regenerated
Improvements7
  • Email delivery migrated from Nodemailer + Gmail SMTP to Resend — verification emails now send from no_reply@send.vibe-coding-game.com with replyTo set to the team inbox; nodemailer and @types/nodemailer dependencies removed
  • Email template refreshed — branded header with Vibe Coding Game name, verification code in a red-tinted card, cleaner footer; contact link removed
  • Results page scroll fixed — page-level overflow-y-auto wrapper added so long leaderboards scroll correctly without affecting the rest of the layout
  • Preview iframe scroll fixed — base CSS injected into buildPreview sets html/body to height:auto and overflow-y:auto, preventing content from being clipped when the generated UI is taller than the preview pane
  • ✅ vote button replaced with a ThumbsUp Lucide icon — color-controllable via CSS; fills solid when the user has already voted; hover and active states use border+text only with no background fill
  • ✅ emoji replaced with ThumbsUp icon in the voting instructions banner and winner card for visual consistency
  • Global scrollbar color reverted to the original gray theme after a red experiment; scrollbar width kept at 5px
v1.94 May 2026

GitHub submission UI removed, AI chat upgraded with live activity streaming and token optimisations, task descriptions now render markdown, copy button added to task card, and results page polished.

Features4
  • Live activity log in AI chat — each step Claude takes (Thinking…, Creating index.html, Editing style.css, Writing response…) appears as an animated entry with bouncing dots on the active step and a green checkmark when done; replaces the single spinner
  • Copy button on task card — copies the full question as clean plain text (all markdown syntax stripped) to the clipboard, ready to paste into any LLM
  • Task description markdown rendering — TaskCard now parses and renders **bold** lines as section headings, - bullet lines as custom dot lists, and --- as a divider; both template and custom host descriptions are supported
  • Sandbox rules injected into AI's initial user message — rules (no CDN, no localStorage, vanilla only, index.html entry point, exact link/script tags) are now part of the human turn rather than the system prompt, making Haiku follow them more reliably
Improvements12
  • AI max output tokens raised from 2048 to 6000 — eliminates truncated file generation that caused Claude to loop and re-create the same file repeatedly
  • Agentic loop capped at 8 iterations — prevents infinite tool-call loops when Claude gets stuck retrying failed edits
  • edit_file error now returns current file content to Claude inline — Claude can self-correct old_string without a separate read_file round trip, cutting retry cycles from 2 turns to 1
  • summarizeSession now rebuilds context from session.files (current state) instead of initialFiles — Claude no longer loses all file changes after conversation compaction
  • Tool descriptions trimmed to one line each — guidance already covered by system prompt removed to save tokens on every API call
  • Summary prompt reduced from 2-3 sentences / 256 tokens to 1-2 sentences / 128 tokens
  • Template descriptions stripped of markdown syntax when applied — textarea shows clean plain text (Problem:, Requirements:, bullet points) instead of raw **bold** and --- markers
  • Dashboard contest card strips markdown from description preview — no more **bold** syntax visible in the one-line truncated preview
  • Sandbox rules removed from template descriptions — no longer appended on apply, keeping copied question text lean for LLM use
  • Smart Todo template updated — localStorage persistence requirement and matching evaluation criterion removed; sandbox blocks localStorage
  • Results page vote button tooltips upgraded from native browser title to custom animated dark tooltips with an arrow, smooth fade+slide via framer-motion, and descriptive text explaining what each vote type means
  • Results page rank badge resized from w-10 h-10 to w-6 h-6 with rounded-md; avatar/initial sized to match; username bumped to text-base font-bold so the name is visually dominant over the position number
Fixes7
  • GitHub submission UI removed from the coding environment — Platform/GitHub toggle, GitHub submission panel, connect button, and repo picker all hidden; backend GitHub functionality remains intact for future re-enablement
  • GitHub repo link buttons removed from results page and public share page
  • GitHub mention removed from the welcome popup and landing page copy
  • Add a note input removed from the coding environment header
  • Fixed ERR_INCOMPLETE_CHUNKED_ENCODING on AI chat SSE stream — added X-Accel-Buffering: no header to bypass nginx buffering, a 20s heartbeat comment to keep proxy connections alive, and client disconnect detection to break the generator loop cleanly
  • Fixed ghFiles ReferenceError on results page — leftover useEffect referencing the deleted ghFiles state variable was removed
  • Fixed sandbox rules still appearing on existing contest task cards — TaskCard now strips everything from **Sandbox Rules onward before rendering or copying, cleaning up DB-stored descriptions at display time without a migration
v1.830 Apr 2026

Claude AI assistant integrated into the coding environment — participants can now chat with Claude to build and edit their project in real time. Streaming fixed end-to-end, nginx infra cleaned up, and the layout locked to 100vh with proper internal scrolling.

Features8
  • Claude AI chat panel built into the editor sidebar — click the Bot icon to open a persistent chat; Claude can read, create, edit, and delete project files directly during a contest
  • Agentic tool-use loop — Claude calls create_file, edit_file, delete_file, and read_file tools in sequence until the task is complete, applying every change live to the editor and preview
  • Real-time SSE streaming — Claude's response streams token-by-token into the chat bubble with an animated cursor; tool call badges (Created / Edited / Deleted) appear inline as each file operation fires
  • Session memory — conversation history and current file state are kept server-side per user; Claude has full context of what was built and changed across every prompt in a session
  • Prompt caching — static system prompt and initial file context are cached with Anthropic's ephemeral cache, reducing input token cost by up to 90% on repeated tool loops within a single prompt
  • Per-user rate limiting — capped at 10 prompts per minute per user to prevent abuse; sessions expire after 2 hours and are cleaned up automatically
  • Changed-files delta — after the first prompt, only files the user manually edited since the last AI response are sent to Claude, keeping context lean across turns
  • Session summarization — when conversation history grows beyond 20 messages, Claude summarizes what was built so far and resets the message log, preventing context window overflow
Improvements1
  • All inner panels (chat history, file tree, Monaco editor, preview iframe) now scroll independently within their allocated space rather than pushing the page taller
Fixes3
  • Fixed ERR_INCOMPLETE_CHUNKED_ENCODING on the Claude AI chat endpoint — nginx was buffering the SSE stream and closing the connection mid-response; added a dedicated location block for /api/ai/chat with proxy_buffering off, proxy_cache off, and 300s read/send timeouts
  • Fixed nginx failing to reload due to a duplicate default_server on port 80 — caused by a stale default.save file left in sites-enabled; removed the file to restore a single canonical default server
  • Fixed coding environment layout overflowing beyond 100vh — contest layout changed from min-h-screen to h-screen overflow-hidden, and intermediate flex containers given min-h-0 so the entire flex chain is properly constrained to the viewport
Research1
  • Audited Claude AI token consumption — intra-turn caching (across tool-use loops) confirmed working correctly with cache_read hitting on loop 2+ within a single prompt; cross-turn cache misses attributed to the 5-minute ephemeral TTL expiring between user prompts, which is expected behavior
v1.729 Apr 2026

First successful live contest completed with real participants. Auto-submit fallback fixed for GitHub tab with no repo selected.

Testing2
  • First successful end-to-end live contest — real participants joined, coded, and submitted within the timer; results and voting worked correctly
  • Collected player feedback and mapped next improvements: HTTPS, branded email sending, copy button on task, markdown rendering in task, Claude terminal in editor, share-your-prompt feature, team support, contest history, and tutorials for ✅ and 🎉
Research1
  • Investigated email scalability — current Nodemailer + Gmail SMTP hits a 500/day cap, has no retry logic, and sends from a personal Gmail account; decided to migrate to Resend with a verified domain (contact@vibe-coding-game.com) for reliable transactional delivery at scale
v1.628 Apr 2026

Live domain migration, auth redirect chain fixed end-to-end, auto-submit race condition resolved, waiting lobby redesigned, and a welcome popup added to the coding environment.

Features1
  • Welcome popup on coding environment entry — informs users that platform code auto-saves and is submitted when the timer ends; warns GitHub submitters to connect and select a repo early; shows a Connect GitHub button if not yet linked
Improvements6
  • Landing page copy updated — ZIP references replaced with 'vibe code live in the editor or link your GitHub repo before time runs out'
  • Hero subtitle and OG meta description updated to reflect AI-assisted vibe coding rather than manual coding
  • Open Graph and Twitter Card meta tags added to root layout — og:image, og:title, og:description, and twitter:card now populated for every page on the platform
  • Waiting lobby redesigned — circular countdown timer centered at the top, participants displayed as avatar bubbles (photo or first-letter initial) with username and host badge; side-by-side grid and TaskCard removed from this view
  • Task card capped at max-h-96 with internal scroll so long contest descriptions no longer stretch the layout
  • Verify-email page simplified — success screen and manual redirect button removed; page auto-redirects immediately after the code is verified
Fixes6
  • Fixed auto-submit race condition — frontend triggers auto-submit at remaining ≤ 5 seconds and backend accepts final submissions within a 5-second grace window, preventing the server from rejecting last-second submits while saveDraft still rejects once time is up
  • Fixed stale localStorage auth — API interceptor now catches any 401, clears token and persisted auth store, and redirects to /login with the original path preserved; covers both expired tokens and deleted users
  • Fixed post-login redirect lost when going through register → verify-email — EmailVerificationGuard passes ?redirect= into /verify-email, useAuth.register threads it through, and verify-email respects it on completion
  • Fixed verify-email always redirecting to /dashboard — all redirect points (useEffect, useAuth.register, EmailVerificationGuard) now use the ?redirect= param with /dashboard as fallback
  • Fixed CORS errors on live domain — www.vibe-coding-game.com and vibe-coding-game.com (http + https) added to allowed origins alongside the server IP
  • Fixed GitHub manage-repo and OAuth redirects pointing to the old IP — FRONTEND_URL env updated to the live domain
Testing4
  • End-to-end flow: register → verify email → auto-redirect to contest → join → code in editor → auto-submit on timer expiry → results page
  • Redirect chain: unauthenticated user hits contest URL → login or register → verify email → lands back on original contest URL
  • Stale auth: old localStorage token triggers 401 → storage cleared → redirected to login → after login returns to original page
  • Auto-submit: code present in editor at timer expiry is successfully submitted within the 5-second grace window
v1.527 Apr 2026

Email verification, auth hardening, contest flow fixes, auto-submit on timer expiry, and 10 built-in creative JS challenge templates.

Features11
  • Email verification on registration — a 6-digit OTP is sent to the user's email via Gmail SMTP; unverified users are blocked from the dashboard and contest pages
  • Resend verification code button on the verify-email page — generates a fresh code with a new 15-minute expiry
  • Login also gates unverified local accounts — redirected to /verify-email until confirmed
  • EmailVerificationGuard component wraps dashboard and contest layouts — covers every protected route in a single place
  • Auto-submit on timer expiry — when a running contest's countdown hits zero, each participant's current code is submitted automatically without any action needed
  • 10 built-in creative JS question templates on the Create Contest page — covers tag input, color palette generator, fuzzy search, slot machine, memory card game, and more
  • Template picker on Create Contest — collapsible card list; selecting a template fills the title and description (including sandbox rules) instantly
  • Contest URL sharing during WAITING state — visitors who aren't yet participants see a join screen with contest details and a Join button
  • Auto-join on RUNNING contest — visiting a live contest URL automatically adds the user as a participant and shows the coding environment
  • GitHub repo code execution on results page — submissions linked to a GitHub repo are fetched via the owner's GitHub App installation token, served into the sandbox iframe, and rendered as a live preview alongside platform-coded submissions
  • fetchRepoFiles utility reads root-level HTML, CSS, JS, TS, and JSON files from the repo's default branch using the GitHub Trees API, then inlines them into the preview builder
Improvements7
  • Results page now shows a consistent live preview for both platform-coded and GitHub-linked submissions — no more code-only fallback for GitHub entries
  • Auth guard now redirects unauthenticated users to /login?redirect=<path> — after login they land back on the page they were trying to reach (join links, contest rooms)
  • Manual code submit no longer ends the contest for other participants — redirects the submitter to results while others keep coding
  • GitHub tab restored after OAuth redirect — returnTo URL includes ?submitMode=github so the GitHub submission panel re-opens automatically
  • Duration picker on Create Contest simplified to preset buttons only (15 min, 30 min, 1 hr, 2 hr) — custom seconds input removed
  • Default contest duration changed to 15 minutes to match the vibe-coding format
  • Nodemon configured to watch .env file — server restarts automatically when SMTP or other credentials change
Fixes5
  • Fixed contest auto-ending when the first user submitted — auto-close logic now checks for isDraft: false submissions only; draft auto-saves no longer satisfy the 'everyone submitted' condition
  • Fixed dashboard accessible after logout — EmailVerificationGuard now redirects unauthenticated users to /login instead of rendering children freely
  • Fixed triple verification email on registration — removed auto-resend useEffect that fired twice under React StrictMode
  • Fixed /auth/verify-email 404 — route group (auth) does not add to the URL path; all references corrected to /verify-email
  • Fixed auto-submit firing on page load — added countdownStartedRef so the effect only fires when remaining transitions from > 0 to 0, not on initial mount
v1.424 Apr 2026

Full in-browser vibe coding environment — Monaco editor, multi-file projects, live preview, auto-save drafts, and public share pages.

Features10
  • In-browser Monaco code editor replaces GitHub-only submission — write HTML, CSS, and JS without leaving the contest page
  • Multi-file project support — create, rename, and delete files and folders in a VS Code-style file tree
  • Live preview iframe updates the rendered project in real time as you code
  • Starter project pre-loaded (index.html, style.css, script.js) so every contest starts with working boilerplate
  • Auto-save draft every few seconds — work is never lost if the tab closes
  • Public share page at /s/[id] — anyone can view code and live preview without an account
  • "View Code" button on every results card opens a read-only Monaco editor + live preview in a modal
  • Two submission modes: platform code editor (saves code to DB) or GitHub repo link — both work together
  • New PATCH /api/contests/:id/submissions/draft backend endpoint for incremental auto-saves
  • New public GET /api/submissions/:id endpoint powers the share page (no auth required)
Improvements5
  • fetch() polyfill injected into previews so local file references (style.css, script.js) resolve correctly inside the sandboxed iframe
  • Code stored as multi-file JSON; legacy single-HTML strings are still decoded correctly
  • Draft and final submit are separate actions — auto-saves never accidentally close the contest
  • Share modal includes a copy-link button, open-in-new-tab, and GitHub repo link
  • Submission model extended with code, language, and isDraft fields
v1.323 Apr 2026

Replaced GitHub OAuth with GitHub App installation flow — users now grant read-only access to selected repos only.

Features3
  • GitHub App installation flow — users select specific repos to share during setup
  • Repo picker dropdown on contest submission page loads repos from the installation
  • "Manage repos" button lets users add or remove repos after connecting
Improvements4
  • Read-only repo access — platform can never write or modify user code
  • No stored access tokens — fresh installation token generated per request via private key
  • Callback handles both install and update actions for re-configuring repos
  • Added githubInstallationId to user model
Fixes2
  • Removed encrypted githubAccessToken field from user model
  • Removed GitHub OAuth login — email and password only
v1.222 Apr 2026

Tested v1.1 end-to-end and gathered a list of improvements needed before the next build.

Testing4
  • Full end-to-end test — contest creation, joining, submitting, voting
  • GitHub connect flow found broken — root cause: OAuth App vs GitHub App architecture mismatch
  • Identified that GitHub App installation flow is required for scoped read-only repo access
  • Gathered improvements list: repo access redesign, UI fixes, submission flow cleanup
v1.121 Apr 2026

GitHub integration, repo-linked submissions, and a fully redesigned results page with consistent voting UI.

Features7
  • Connect GitHub account — platform only requests public repo access
  • Select any public GitHub repo from a live dropdown when submitting
  • Submissions linked to a GitHub repo — 'Code' button on every results card
  • Disconnect GitHub at any time from your profile
  • Dedicated /auth/github-connected callback page with success/error feedback
  • GitHub connect button shown inline on submit form if not yet linked
  • Resubmit at any time to change repo or update note during a running contest
Improvements7
  • GitHub access token encrypted at rest (AES-256-GCM) — never exposed to the client
  • Submit button disabled until GitHub is connected and a repo is selected
  • Vote button shows a spinning loader while request is in flight
  • All other vote buttons dim to 40% opacity during any active vote request
  • User avatars shown on submission cards alongside username
  • Page-level loading replaced with a centered animated spinner
  • Tooltips on all vote buttons explain exactly why they are locked or active
Fixes3
  • Fixed 3-slot action layout on every card — Code / ✅ / 🎉 always in the same position
  • Buttons no longer shift or disappear based on contest state
  • ZIP upload removed — submissions are now GitHub repo links, not local archives
v1.015 Apr 2026

Initial release — full contest lifecycle, two-tier voting, live leaderboard, and Google + GitHub login.

Features22
  • Email + password registration with validation (3–20 char username, 8+ char password)
  • Login with JWT session persisted to localStorage
  • Google OAuth and GitHub OAuth sign-in
  • Protected routes — unauthenticated users redirected to /login with return URL preserved
  • Zustand auth store with persistent state across page reloads
  • Dashboard home with greeting, quick-action cards, and recent contests preview
  • Contests list page with status filters (Waiting / Running / Ended)
  • Create contest — title, optional description, duration presets (15 min → 2 hr) + custom input
  • Share code generated on creation with one-click copy
  • Delete contest (blocked while running)
  • Sidebar nav with logo, page links, user profile, and logout
  • Join by share code at /join/[code] with contest preview
  • Lobby: circular countdown timer, live participant list with host badge, Start button
  • Running: live HH:MM:SS countdown, urgent red pulse under 60 seconds
  • Task description visible in split-screen during contest
  • Code submitted as a ZIP archive (up to 50 MB) — drag-and-drop or click to browse
  • Optional submission note (max 300 chars)
  • Two-tier voting: ✅ +1 (unlimited) and 🎉 Tada (once per contest)
  • Ranked submission cards with 1st / 2nd / 3rd badges
  • Winner banner with animated trophy when contest ends
  • Tie detection — shows all tied usernames when top Tada counts match
  • Share results page via copy-link button

Built with Next.js, Prisma, PostgreSQL, and a lot of coffee.