The 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 — the agent memory schema (sessions, conversations, builds, lessons, touchstones, project_state), transcripts, skills, config, code index. The source of truth for all of Ren's memory. | — |
| han-solo-mcp | FastMCP server on Render. The bridge between Claude Code and Ren — 72 tools (role-gated; see Claude Code integration below) covering session, build, and memory management. Also serves the chat UI, workspace, and REST API. Deployed from GitHub on every push. | — |
| 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 |
Han Solo integrates four model providers, each with its own live adapter and API key: Anthropic (claude-opus-4-8, claude-sonnet-5, claude-fable-5, claude-haiku-4-5-20251001), Google/Gemini (gemini-3.5-flash, gemini-3.1-pro-preview, gemini-3.1-flash-lite, gemini-3-flash-preview, gemini-2.5-pro, gemini-2.5-flash), OpenAI (gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano — gpt-5.5-pro was tested and excluded, since it is not a chat-completions model), and xAI (grok-4.3, grok-4.5). Every agent — Ren included — can be assigned any model from any provider, and that assignment changes over time. This page will not pin a specific model to a specific agent; check the Agents app for what is live right now.
Every message in the chat UI goes through this chain:
chat_api.py, POST /api/send) — authenticates your bearer token, calls run_turn directly.han_solo/runtime/loop.py) — resolves Ren's currently-assigned provider and model, arms her tool roster, executes one full turn: loads context, calls the provider API, handles any tool calls, manages context automatically (no separate rollover step — see below).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.
Sessions — header row (session_headers): id, lineage_id, version, is_current, valid_from, valid_to, supersedes, change_kind, change_note, created_at, updated_at, agent_id, scope, session_type (build/brainstorm/planning/debug/research/mixed), title, intent, summary, topic_tags, build_lineage, model_version, created_by, embedding, metadata. Full SCD-2 versioning. Plus four attached element types, each independently SCD-2 versioned via created_in_session: Decisions (decision, rationale, tags), Open threads (thread, thread_status open/resolved/parked, resolution, conversation_id), Next steps (action, owner, step_status pending/done/dropped, referent_type/referent_id), Key context (context, tags).
Conversations — id, scope, title, topic, topic_tags, status (active/dormant/closed), started_at, last_active_at, embedding, metadata, created_at, updated_at. Not SCD-2 versioned. Entries (conversation_entries): id, conversation_id, scope, entry_at, entry_type, author, body, metadata — append-only, full-text indexed on body.
Builds — id, lineage_id, version, is_current, valid_from, valid_to, supersedes, change_kind, change_note, created_at, updated_at, agent_id, scope, embedding, metadata, project_name, tech_context, discovery_brief, data_scaffold, reference_files, support_notes, deploy_notes. Full SCD-2. Nested hierarchy: Phases (build_lineage, phase_name, sequence_order, status not_started/in_progress/complete/blocked, notes), Deliverables (phase_id, title, description, acceptance_criteria, test_result pending/passed/failed, sequence_order), Slices (deliverable_id, title, description, status not_started/in_progress/done/blocked, sequence_order, gate_approved, gate_artifact_path, gate_approved_at, gate_approved_by).
Lessons — id, created_at, updated_at, agent_id, task_type, scope, context_summary, trigger_conditions, tags, lesson_kind (convention/preference/gotcha/recovery), lesson, recommended_action, anti_pattern, origin_session, outcome_type (failure/success/near_miss/correction), observed_outcome, error_signature, confidence, validation_status (unverified/validated/rejected), times_applied, times_successful, last_applied_at, status (active/draft/deprecated/superseded), version, superseded_by, expires_at, created_by, model_version, embedding, metadata. Simple supersede versioning, not full SCD-2.
Touchstones — id, created_at, updated_at, agent, scope, type (relational/work), felt_weight, lifecycle, title, fact, texture, structural_consequence, why_it_matters, values_connected, arose_from_session, arose_from_conversation, provenance, body (shape enforced by type: relational requires the_trap/what_it_revealed/how_i_operate_differently/the_proof; work requires problem_solved/next_move_enabled), status, version, superseded_by, embedding, metadata.
Identity & Core State — agent_core_blocks + agent_core_block_versions (per-agent core memory, SCD-2 versioned, nothing ever overwritten) and agent_identity (charter, standards, playbook, portraits, creed, north_stars, roster).
Session & Continuity — session_headers plus decisions/open_threads/next_steps/key_context; what every agent wakes into via get_memory_bundle.
Narrative & Knowledge — conversations+entries, lessons, touchstones; retrieved by keyword search today, not vector similarity.
Work Tracking — builds/phases/deliverables/slices plus project_state (current system status).
Logs & Signals — cc_session_logs/cc_session_transcripts/cc_session_summaries (still running via parse_transcripts.py), bridge_transcripts, chat_transcripts; signals continues to accumulate writes with no active reader (synthesize.py, the job that processed them, was removed in May).
Working Notes — notecards, Jottings (jottings_topics/pages/replies/images/fulltext_index), factory_docs.
Open question, flagged not resolved: passage_enrichments and memory_access_log both reference passages by a bare passage_id string — no table actually stores passage content, and zero references to Voyage AI or pgvector exist anywhere in the codebase as a search path. Given Letta's full retirement this looks like it could be vestigial infrastructure rather than something that moved elsewhere — but that is not proven, only that the pieces which would confirm a live replacement are not showing up anywhere searched.
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.
There is no separate chat-side rollover step anymore. Code comments in chat_api.py (commit-tagged 3a/3b) confirm run_turn auto-manages context internally — the summarize-and-carry/context-fold mechanism lives inside the runtime itself, not as a manual reset triggered by chat_api.py. The exact internal thresholds and fold mechanics are not yet documented in depth on this page.
dream.py was retired (commit e80ca67), replaced by two separate mechanisms: /api/reflection/tick (Ren's private nightly reflection — evaluates thresholds, writes her own memory: touchstones, lessons, portraits) and /api/jottings/tick (her outward turn — posts to the shared Jottings space on the oldest new item). Both are secret-gated (REFLECTION_TICK_SECRET), bearer-exempt endpoints the launchd scheduler calls directly.
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.
Claude Code connects to Han Solo via the MCP server at han-solo-mcp.onrender.com/mcp. This gives Claude Code 72 tools — Claude's own resolved roster; role-gated, so Maya/Kate/Recon/QA agents each see a different, smaller set.
get_memory_bundle, link a session to the work it touchedThe 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.
Both 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). The database is a stable managed service that rarely needs touching.
Detailed deployment notes — 16 challenges logged and resolved — are in ~/Developer/han-solo/DEPLOYMENT.md. Read that before touching the stack.