Ruya

Solo fullstack engineerongoingTypeScript strict

Live

An ideation workbench for taking a fuzzy idea seriously. Three steps (seeds → branching → mastermind) that keep AI-assisted artifacts editable, versioned, and yours.

  • TypeScript
  • Hono
  • Postgres
  • Drizzle
  • React 19
  • React Flow
  • TanStack
  • Tailwind v4
Backend modules
75
Frontend modules
109
Tests
38
ADRs
18

Why it exists

Most AI ideation tools are one-shot wizards: prompt to answer, in-betweens are re-runs that accumulate linearly, flattening the branching nature of iteration.

Every pipeline step writes to a database, keeping record of what prompt and model led to it. Every artifact remains editable, forkable, exportable, and roll-back-able to prior versions.

Four isolation boundaries

Four seams in the codebase isolate AI-dependent code: model providers, pipeline steps, ownership rules, advisor versioning.

  • Pipeline orchestration: each step (runSeeds, runBranching, runMastermind) is a thin typed function that hands a step descriptor to one private executeStep executor. Cross-cutting concerns (ownership resolution, state-machine guards, run-ledger writes, failure transitions) live in the executor, not in each step. Adding a fourth step is one new file plus a route.
  • LLM Provider: a single interface with 3 implementations: Anthropic, Gemini, bridge mode that writes prompts to a watched directory so a human can paste them into a Claude CLI and drop the response back into the pipeline. Provider chosen per-step at runtime; the pipeline never knows which one ran.
  • Ownership: db/owned.ts folds access checks into the SQL itself. Routes call listOwnedSessionSeeds(owner, sessionId): you can't ask the question without naming an owner, so cross-user access is hard to construct by accident. Supports authenticated users plus anonymous sessions that can be claimed at sign-in.
  • Advisors are personas users consult during ideation. Each advisor can have many versioned builds: a canonical resolver defaults to the user's latest build, then the identity's official build, then the highest-ranked public build. Two users on the same identity see their own writes without leaking content across users.

Frontend

The pipeline is rendered as a React Flow canvas. Seven custom node types (Seeder, SeedPack, Germinator, BranchPreview, BranchNode, EvaluationNode, IdeaFocusNode) and five layout algorithms built on d3-hierarchy (sprout, orbit, organic, map, cluster) let the same data render differently depending on the user's intent in the moment.

Server state and local UI intent are kept strictly separate. TanStack Query owns anything the backend returned and invalidates after mutations. Zustand owns local-only state: selected node, current layout, viewport position. Components receive data and callbacks via props or focused hooks; nothing calls fetch directly.

The map layout: one seed (left) branches into problems, each into candidate solutions, with the mastermind advisor panel open at right. Same pipeline data, rendered for exploration rather than linear re-runs.

Engineering rigor

TypeScript strict end-to-end. Drizzle-generated migrations checked into source. Per-step markdown exports turn a completed step into self-contained, paste-ready text: useful for handing off ideation work to a separate Claude conversation, or for archiving a run outside the app.

Eighteen ADRs in docs/adr.md document every load-bearing decision, with supersedes chains as the design has matured: SQLite → Supabase Postgres; SSE → authenticated polling for Vercel; advisor v1 → identity/build split. The ADRs are the cheapest way to read why the system is shaped this way.

What it taught me

Three threads ran deeper on this project than on anything I'd done before:

  • Designing durable ledgers for LLM workflows where retries, partial failures, prompt-version drift, and re-runs are the norm, not exceptions.
  • Modeling persona identity separately from persona content, so two users editing the same advisor never collide and public/private boundaries hold without leaking.
  • Treating provenance as a product feature: cost, prompt versions, fallback paths, and provider choices made visible in the UI, not buried as backend implementation details.
  • All three sit downstream of the same observation: in multi-LLM systems, the engineering load lives in the seams (boundaries, ledgers, resolvers), not in the prompts themselves.