Five pieces working together — each with a distinct role. Understanding the stack means you know what breaks and why, and what it costs.
| Service | What it does | Cost |
|---|---|---|
| han-solo-db | PostgreSQL 16 + pgvector on Render. Stores everything — Letta agents, memory blocks, archival passages, conversation history. The source of truth for all of Ren's memory. | $7/mo |
| han-solo-letta | Letta v0.16.8 on Render. The AI memory runtime — manages Ren as a MemGPT agent, handles tool calls, conversation storage, and memory compression. Ren's brain lives here. | $7/mo |
| han-solo-mcp | FastMCP server on Render. The bridge between Claude Code and Letta — 67 tools covering memory, portraits, signals, T4 project data, notecards, search, and session briefs. Also serves the chat UI, workspace, and REST API. Deployed from GitHub on every push. | $7/mo |
| Google AI API | Gemini 2.5 Flash (BYOK via Google AI) — the model powering Ren's responses. Every message you send hits this API. Pay-per-token, billed separately from the Claude Pro subscription. | Variable |
| Voyage AI | Embedding model (voyage-3, 1024-dim) for archival memory search. When Ren searches her memory, Voyage converts the query to a vector and finds semantically similar passages. | Minimal |
Every message in the chat UI goes through this chain:
POST /api/send, authenticates your bearer token, forwards to Letta.Tool calls (when Ren searches memory, writes to a block, or fetches a page) add extra round-trips to Anthropic. Each tool call is a separate LLM inference — this is why sessions with heavy archival searching or page fetching cost more.
Ten or more blocks loaded into every prompt. This is the baseline context cost that Ren carries into every message whether she uses it or not. Portrait blocks grow as signals accumulate — forming layers promote to trusted when patterns are confirmed across multiple sessions.
| Block | What it holds | Limit |
|---|---|---|
always_loaded_core | Framework context, working norms, Scott's profile summary, memory use instructions, session close-out ritual | 10,000 chars |
pending_thoughts | Session brief — what happened last session, what's open, what's next. Written nightly by the dream script. | 8,000 chars |
project_state | Current in-flight project context (JSON). Phase, active slice, project metadata. | 10,000 chars |
scott_portrait_forming | Ren's evolving interpretation of Scott — specific, dated observations about how he thinks and what he values | 20,000 chars |
scott_portrait_trusted | Patterns confirmed across multiple sessions — promoted from forming when a signal recurs | 20,000 chars |
ted_portrait_forming | Ren's evolving interpretation of Ted — same signal model as Scott's portrait | 20,000 chars |
ted_portrait_trusted | Confirmed patterns about Ted | 20,000 chars |
ren_portrait_forming | Ren's self-portrait — what she got right, what she missed, what she wants to develop | 20,000 chars |
ren_portrait_trusted | Confirmed self-observations | 20,000 chars |
seed_signals | Early-session observations, dated signals, relational notes not yet moved to archival | 20,000 chars |
214+ passages stored as vector embeddings in pgvector (Voyage AI, voyage-3, 1024 dimensions). Ren searches this with archival_memory_search when she needs context that isn't in her core blocks. Every search is logged to memory_access_log — the query, what was returned, and whether it was used. This is the Memory MRI: a diagnostic record of whether her memory is actually being accessed and whether the right things surface.
Passages are written by Ren and Claude Code during sessions — specific, real captures written in the moment they matter. Session close-outs write structured content directly to archival without an intermediate processing step.
Every message stored in PostgreSQL. Letta loads recent messages into context on each send. This is what fills the context window over time and causes the crash if not managed.
The context window crash that happened on 2026-05-13 (the day this docs site was built) exposed a gap: no way to reset a Letta conversation without losing everything. The fix is now live.
When the session rolls over — either automatically at 150 messages, or manually via the "New session" button:
Ren's memory is never lost in a rollover — only the raw conversation thread resets. The nightly dream captures session content before that happens.
Every night at 2:00am, a script on Scott's Mac sends Ren a structured reflection prompt via Letta's REST API. Ren uses her own built-in tools to reflect and write a fresh session brief. She:
pending_thoughtsRuns via launchd (com.scotth.rendream.plist). No external dependencies — stdlib Python only.
han-solo-letta.onrender.com with the dream prompt. Scott's Mac just fires that request — everything after that (Letta processing, Ren calling her own tools, memory writes) happens entirely on Render's servers. The Mac doesn't need to stay connected or wait. If the Mac is asleep at 2am and wakes up later, launchd runs the job when it can. If it misses a night entirely, Ren skips that reflection — no retry.
Every 30 minutes, a script on Scott's Mac reads Claude Code session JSONL files from ~/.claude/projects/, parses them into structured entries, and pushes them to the Han Solo database. Only sessions from the last 45 days are kept. Ren can search these via the search_transcripts MCP tool — raw session history without any Anthropic API calls.
dream.py respects a jobs_paused flag in the han_solo_config Postgres table. Toggle it from the Memory panel in the chat UI.
Claude Code connects to Han Solo via the MCP server at han-solo-mcp.onrender.com/mcp. This gives Claude Code 67 tools:
The Solo Hook (~/.claude/hooks/framework-skill-inject.sh) fires before every Claude Code session. It calls get_session_brief, pulls Ren's pending thoughts and always-loaded context, and injects them into the session — including the current date and time. Claude Code starts every session already oriented from where Ren left off, with no manual briefing required.
All three Render services deploy from github.com/scoots31/han-solo. A push to main triggers automatic redeploy of han-solo-mcp (the only service with code that changes). Letta and the database are stable services that rarely need touching.
Detailed deployment notes — 16 challenges logged and resolved — are in ~/Developer/han-solo/DEPLOYMENT.md. Read that before touching the stack.