Aporia: an AI debate App that makes your beliefs argue back
Aporia started as a simple loop: pick a proposition, state what I believe, and let Claude argue the other side. The thing that made it feel alive was the Intellectual Ladder, a 1-7 meter that scores the Playerâs reasoning from Reaction to Truth-Seeking after each turn.
Over the build, the App grew from a one-screen MVP into a more complete thinking tool: bilingual EN/KR debates, Friendly and Intense modes, magic-link accounts, saved debate history, replay, AI review, and a skills dashboard.
The biggest shift was realizing the App was not just âchat with an opponent.â It needed memory, friction, and consequence. A guest preview lets a Player feel the argument immediately, but signing in turns the debate into something they can revisit, study, and improve from.
Why I built this
I wanted an App that does the opposite of validation. Aporia is for Players who want to test whether a belief survives pressure: students, debaters, writers, founders, researchers, or anyone curious enough to let an AI disagree with them seriously.
Stack used
Next.js 16 App Router, React 19, TypeScript, Tailwind CSS v4, Anthropic SDK, Claude Sonnet 4, Claude Haiku 4.5, Railway Postgres, Resend, Vercel.
Key prompts / AI tools
The App uses Claude Sonnet for the visible debate and post-debate review, and Claude Haiku for the faster ladder evaluation. I split the prompts by job: debate prompt, evaluation prompt, and review prompt.
A key prompt decision was mode-specific behavior. Friendly mode is generous and conversational, like a smart friend over coffee. Intense mode is sharper, demanding evidence and attacking weak assumptions. Another important prompt constraint: the AI should not introduce itself as Aporia. It should just argue.
Architecture decisions
I kept the App as one Next.js deployment with serverless API routes instead of adding a separate backend. Guests never write to the database; signed-in Players get persisted debates.
Auth is in-house magic link: Resend email, HMAC-signed tokens, single-use nonce rows, and an HttpOnly session cookie. I chose Railway Postgres over Vercel Postgres to match the rest of my stack and avoid tying the database to the host.
Debate turns are committed atomically: user message, assistant message, and daily usage counter are written together only after the stream completes cleanly. Reviews are generated lazily and cached as JSONB so replay pages stay light.
Hardest problem
The hardest part was making the App feel immediate while still becoming durable after sign-in. Streaming tokens, evaluating the ladder in parallel, enforcing turn limits, saving clean transcripts, and not burning quota on failed streams all had to line up.
What I would improve next
I want the next version to make progress feel more visible. The skills dashboard already hints at this, but I would make it more alive: show how a Playerâs reasoning changes over time, which topics stretch them the most, and where they keep getting stuck.
Iâd also love to add more ways back into old debates: highlights, bookmarks, shareable excerpts, and ârematch this argumentâ flows where a Player can return to the exact moment they almost changed their mind.
Lessons learned
A small App gets much better when every constraint has a product reason. The 10-turn guest limit is not just abuse control; it makes the preview feel finite. The 30-turn signed-in limit is not just cost control; it makes accounts meaningful.
The model split mattered too. Sonnet belongs where the Player reads the result. Haiku is enough for fast classification. And documentation had to become part of the App once auth, persistence, review, and design all landed in quick succession.


comments (0)
Quiet ·No comments yet. Be the first to add one.