Architecture

How it works

The pieces working together — each with a distinct role. Understanding the stack means you know what breaks and why, and what it costs.

The stack

ServiceWhat it doesCost
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
Fixed costs: Render service pricing isn't re-verified on this page — check the Render dashboard for current billing per service. Anthropic API usage is variable — $50 in credits lasts months at normal usage. The Claude Pro/Max subscription is separate and covers claude.ai and Claude Code only.

AI Providers

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.


What happens when you send a message

Every message in the chat UI goes through this chain:

  1. Chat UI (browser) — you type a message and hit Send.
  2. han-solo-mcp (chat_api.py, POST /api/send) — authenticates your bearer token, calls run_turn directly.
  3. run_turn (the multi-provider agent runtime, 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).
  4. Provider API (Anthropic, Google, OpenAI, or xAI — whichever Ren is currently assigned) — generates the response.
  5. han-solo-mcp — writes the turn record directly to han-solo-data, returns the response.
  6. Chat UI — renders Ren's reply.

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.


Ren's memory layers

The core five memory objects, hub arrangementSessions, Conversations, Builds, Lessons, and Touchstones arranged around a central core memory hub, each the primary object type every agent reads and writes.Core Memorythe 5 objects everyagent reads/writesSessionsheader + 4 element typesConversationscontainer + entriesBuildsbuild → phase → deliverable → sliceLessonsgeneralizable patternsTouchstonesrelational or work snapshots
The core five memory objects, full attribute detail Five panels — Sessions, Conversations, Builds, Lessons, Touchstones — each listing every column on its table(s), including nested element types for Sessions and Builds. Sessions id lineage_id version is_current valid_from valid_to supersedes change_kind change_note created_at updated_at agent_id scope session_type title intent summary topic_tags build_lineage model_version created_by embedding metadata + Decisions decision rationale tags + Open Threads thread thread_status resolution conversation_id + Next Steps action owner step_status referent_type referent_id + Key Context context tags Conversations id scope title topic topic_tags status started_at last_active_at embedding metadata created_at updated_at + Entries (append-only) id conversation_id scope entry_at entry_type author body metadata 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 + Phases build_lineage phase_name sequence_order status notes + Deliverables phase_id title description acceptance_criteria test_result sequence_order + Slices deliverable_id title description status 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 lesson recommended_action anti_pattern origin_session outcome_type observed_outcome error_signature confidence validation_status times_applied times_successful last_applied_at status version superseded_by expires_at created_by model_version embedding metadata Touchstones id created_at updated_at agent scope type felt_weight lifecycle title fact texture structural_consequence why_it_matters values_connected arose_from_session arose_from_conversation provenance body status version superseded_by embedding metadata

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.

The memory landscape — six functional groups plus one open question Identity and Core State, Session and Continuity, Narrative and Knowledge, Work Tracking, Logs and Signals, and Working Notes, plus a flagged open question about semantic and archival passage search. 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 — semantic / archival passage search 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.

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.

Context window: a very large context window (varies by the assigned model/provider). Core blocks use ~3,000 tokens. Each message exchange uses 500–2,000 tokens. Heavy page fetches can use 10,000+ tokens in a single exchange. A warning banner appears at 60 messages; automatic rollover fires at 150 to stay well clear of the limit.

Session rollover

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.


Automated background jobs

Reflection & Jottings tick — replaces dream.py

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.

parse_transcripts.py — Claude Code session capture (every 30 min, Mac launchd)

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 integration

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.

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.


Deployment

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.