Case Study

Thinking Space

Consumer SaaS · Digital WellbeingFive-Channel MemoryTheme ArcsVoice In / Voice OutBuild · Multi-Phase

A chat-based journaling app is only useful if the AI remembers. Generic top-K-by-similarity retrieval forgets what the user themselves flagged as important, surfaces nothing on a short prompt, and produces a thinking partner with the memory of a goldfish. We built a private journaling app where memory is five purpose-built channels, themes are computed with momentum and trajectory, reflection cards are generated by a daily cron with rotation rules, and the codebase is multi-tenant from day one. Built single-tenant for the founder, ready to open up.

Home · entry point and recent activity

Home · entry point and recent activity

Navigation · sidebar and global shortcuts

Navigation · sidebar and global shortcuts

Focus threads · pinning what you want to think about

Focus threads · pinning what you want to think about

Check-ins · the AI returning to a thread you parked

Check-ins · the AI returning to a thread you parked

Split-screen · chat alongside context

Split-screen · chat alongside context

Turns · pivots the AI flagged as moments your thinking shifted

Turns · pivots the AI flagged as moments your thinking shifted

Reflection cards · daily, surfaced when the moment fits

Reflection cards · daily, surfaced when the moment fits

Reflection cards · pulling insights out of the past month

Reflection cards · pulling insights out of the past month

Theme map · what you keep coming back to

Theme map · what you keep coming back to

Theme map · alternate view

Theme map · alternate view

Theme analysis · momentum, trajectory and co-occurrence

Theme analysis · momentum, trajectory and co-occurrence

Theme map · merge or find connected themes

Theme map · merge or find connected themes

Investigating a theme · its full history surfaced

Investigating a theme · its full history surfaced

Timeline · how a theme has moved over time

Timeline · how a theme has moved over time

Profile · 20-question progressive disclosure, unlocks with use

Profile · 20-question progressive disclosure, unlocks with use

Dark mode

Dark mode

Neo light theme

Neo light theme

At a glance

Industry
Consumer SaaS · digital wellbeing / private journaling
Team
1 founder-operator · plus the user as first paying customer
Mode
Build · multi-phase, single-tenant → multi-tenant SaaS
Status
Live in personal use · pre-launch checklist complete · ready for sign-ups
Friction type
Memory without shape, top-K-by-similarity surfaces what lexically rhymes, not what mattered. Themes without arc are just tag clouds. Reflection without rotation either buries the user or repeats. Profile up-front is a cliff. Voice without rate-limiting is a denial-of-wallet attack.

The problem

Chat with an AI for long enough and the conversation forgets itself. The model has a context window, the user has a life, and the gap between what was said three weeks ago and what the model can see today is the gap between a real thinking partner and a stranger that nods politely. Stuff a transcript into a vector store, fetch the top five hits per turn, and you get an AI that recalls things that sound like the current message but cannot tell which moments the user themselves flagged as important.

The solution

A Next.js App Router app where every chat goes through a single streaming POST. The system prompt is assembled fresh from up to seven sources, memory is retrieved across five purpose-built channels, long histories are hybrid-summarised, the response is streamed, and four to five background extractors fire after the stream closes, themes, memories, people and turn detection all happen without blocking the user's next message.

  • Five-channel memory · the system's centre of gravity

    Resonance (what sounds like what they just said), Anchors (what they themselves flagged, turns, engaged reflection cards, completed check-ins), Arc (what else is happening in the story they're in the middle of, weighted by theme momentum), State (what's been on their mind in the last 14 days), Echo (an older thought that rhymes, fired probabilistically 40% of the time so it stays a surprise). One query embedding, reused across channels.

  • Theme arc · computed, not predicted

    Every theme carries momentum_score (exponential decay, 14-day half-life), layer_velocity (layers per week over the last 30 days), sentiment_trajectory (flat, rising, falling, oscillating, V-shaped, peak-shaped), co-occurring themes, and arc_computed_at. Recomputed after each new layer. The Arc memory channel reads momentum_score directly to pick which themes are still hot.

  • Daily reflection cron with rotation

    Vercel cron runs nightly with a CRON_SECRET bearer token. Per-user interval overrides, 30-day rotation window per theme, 14-day duplicate suppression. Card types: recurring, absent, positive_shift, emotional_pattern, focus_related, the type drives the prompt. Cards are embedded on insert so they're ready for the Anchors channel the moment a user engages.

  • Profile that unlocks with use

    20 questions defined as constants. The first two unlock immediately. Each subsequent question unlocks after one qualifying chat (≥4 messages). Most have curated pill options plus free-text. Profile answers are injected into the system prompt only if the user has actually filled any in.

  • Voice in and voice out · safely

    whisper-1 transcription with 25 MB max upload, MIME validation with codec params stripped, an explicit allow-set, 10/min rate limit, empty-file guard, and a steering prompt that nudges Whisper away from hallucinating numbers on short clips. tts-1 playback with four voice options (US/UK male/female).

  • Stream trailers · UX shortcut, not a second round-trip

    __TS_CHATID__ is sent first so the sidebar shows the new chat instantly, before the model response arrives. __TS_TURN__ markers stream inline so turn pivots render as they happen. __TS_META__ JSON is appended at the end with title, summarisation info and (for opted-in users) the retrieved memory bundle for debug.

  • Multi-tenant from day one · not retrofitted

    Every user table has RLS enabled and a "users can read own X" policy. Service-role client only used for genuinely cross-user operations (cron, fire-and-forget extractors). Middleware enforces auth for /chat, /themes and /admin. Admin route additionally checks an ADMIN_EMAILS env allowlist plus a super_admins DB table. createServerSupabaseClient() is the only sanctioned factory.

  • lib/constants.ts · 265 lines of explicit thresholds

    Hardcoded model names, magic numbers, .select(*), and "as any" are all banned in code review. Every threshold (memory similarity floor, Echo probability, Echo age cutoff, channel slot counts, momentum half-life) lives in one file. Tuning is a code change in one place, not a hunt.

What it deliberately isn't

A generic chatbot. There's no model picker, no personality selector, no temperature slider. The product is a single AI character with an opinionated memory architecture and an opinionated way of seeing themes. That choice cuts the surface area to what actually matters and lets the engineering go deep on the few things that determine whether the app feels like a real thinking partner.

The outcome

  • Memory that has shape. Five channels with explicit jobs replace top-K guesswork. The right kind of memory surfaces for the moment.
  • Themes that have arc. Momentum, velocity, sentiment trajectory and co-occurrence, the AI can say "this is the one you've been circling all month" because it can compute it.
  • Reflection that rotates. Daily cron with 30-day rotation and 14-day duplicate suppression. Cards arrive when they fit.
  • Onboarding that doesn't cliff. Profile unlocks as the user uses the product. Each new question is a small reward, not an entry tax.
  • Voice without exposure. Whisper rate-limited, MIME-validated, size-capped. tts-1 keyed to four named voices.
  • Multi-tenant ready. RLS on every user table from day one. Going public is a switch, not a rebuild.
Sketcha

Got an idea like this in your own business?

Sketcha is a quick, no-pressure way to talk it through. Tell me where things are sticking and you'll walk away with a sketch of how a system could look, including a flow diagram of the AI bits.

Example Sketcha flow diagram of an AI workflow
SKETCHASKETCHA