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.