Inspiration
Every chat tool I used forced my thinking into a straight line but real thinking branches. When I'm researching, drafting, or weighing a decision, I want to try one direction, back up, try another, and keep them side by side. In a normal transcript, the moment you follow one path, every other path you considered gets buried below an endless scroll.
There was a second, quieter problem. To continue any of those directions, most apps resend the entire history to the model every turn, including the tangents that have nothing to do with what you're asking now. That's slower, more expensive, and it dilutes the model's focus.
What it does
traceback stores every conversation as a tree. You can:
- Branch from any earlier reply into a new direction,
- Switch between branches and compare their answers,
- Return to the exact point you left, with nothing lost.
The key move is on the backend: when you continue from a branch, Traceback sends the model only that branch's root-to-node lineage, the direct line of ancestors, instead of the whole tree.
For a node n at depth d in a conversation of N total messages, a linear chat pays for every message it has ever seen:
Linear Cost ∝ Σ t_i
Traceback pays only for the active lineage A(n), where |A(n)| = d + 1 ≪ N:
Traceback Cost ∝ Σ t_m
You keep your full exploration history, but each reply stays focused and cheap.
How I built it
Traceback is a TypeScript monorepo:
- Server : Express + Prisma + PostgreSQL (hosted on Neon). Messages self-reference via a
parentId, so the table is the tree. Fetching a branch's context is a single recursive CTE that walks root-to-node, so the pruned lineage never has to be reassembled in application code. - Web : React + Vite + Tailwind, with React Flow rendering the live conversation graph. A shared client package speaks to the server so the same engine drives the app and the landing-page demo.
- Models : an OpenAI-compatible provider layer routes to OpenRouter (chat, web search) and Groq (fast speech-to-text), so switching models is config, not code.
- Realtime : replies and agent steps stream token-by-token over Server-Sent Events.
- Auth & sessions : Google OAuth via Passport, with Postgres-backed sessions so a user's tree survives restarts.
The landing page renders the real app inside a scroll-driven MacBook, on canned data, so the demo can never drift from what actually ships.
Challenges I faced
- Modeling a tree in a relational database. Getting lineage queries right, and fast, with a recursive CTE, instead of N round-trips per branch.
- Pruning context without breaking coherence. Sending only the ancestry meant being precise about what "the conversation so far" means on a fork.
- Sessions that survive deploys. An in-memory session store silently wiped everyone's login on each redeploy; moving to a Postgres-backed store fixed a bug that looked random from the outside.
- One responsive codebase. The tree, the branch controls, and the landing demo all had to work from a 375px phone to a wide desktop, including making the in-frame app render its desktop layout even when viewed on a phone.
What I learned
Recursive CTEs, SSE streaming, the real cost/UX tradeoffs of context windows, and how much a data model shapes a product. As opposed to other chat UIs, the tree and the lineage pruning of traceback basically are the whole point, and building them actually made me treat the database schema as the main feature.
Built With
- express.js
- google-oauth
- groq
- katex
- monorepo
- neon
- node.js
- openrouter
- passport.js
- postgresql
- prisma
- railway
- react
- react-flow
- react-markdown
- server-sent-events
- sql
- tailwindcss
- typescript
- vercel
- vite
- web-speech-api
Log in or sign up for Devpost to join the conversation.