Documentation
Agent Handover
When the context window fills up, RaggieCode doesn't truncate or summarise blindly. It generates a structured handover document and continues in a fresh session .; with zero lost progress.
Why Handover Exists
Every LLM-based agent has a fixed-size context window (typically 8K–200K tokens). As a session progresses, each tool call, tool result, and assistant response piles up. At some point the window is nearly full, and the agent must decide what to keep and what to discard.
The naive approach .; truncating the oldest messages .; throws away the very information the agent needs to continue coherently: which files it changed, what it was about to do next, which tests failed, and why it made the decisions it did.
A more sophisticated approach is context compaction: ask the LLM to summarise the conversation so far and replace the raw history with the summary. This works for simple chat sessions, but coding tasks are different. The specifics matter:
- A summary might say “added input validation to login endpoint,” but the next agent needs to know which fields were validated, what the error format looks like, and which callers were already updated.
- A summary might say “tests failed,” but the next agent needs to know which tests, with which error messages, and what was tried that didn't work.
- A summary might say “the user requested X,” but the next agent needs to know the exact requirements, constraints, and preferences the user expressed.
RaggieCode's handover mechanism solves this by writing a structured document with predefined fields .; not a free-form summary. Each field has a specific purpose, and the next session reads these fields directly into its system prompt. Nothing is left to interpretation.
Key principle
Handover is not a fallback for when things go wrong. It is the primary mechanism RaggieCode uses to work on tasks that exceed a single context window. Large, multi-file refactors routinely require multiple sessions, and handover is what makes them possible without manual intervention.
When Handover Is Triggered
The agent checks whether a handover is needed before each LLM call. The check is simple: estimate whether the current context has room for one more full round of tool calls and responses. If not, a handover is initiated.
Automatic trigger
No manual intervention needed. The agent detects the condition and initiates the handover as part of its normal prompt loop.
Configurable threshold
The context window size is set in roles.json or via the /windowSize in-chat command. The handover margin (buffer space) is also configurable.
Also manual
The agent can also voluntarily initiate a handover mid-task by using the handover tool, e.g. when it detects the task has grown too large for a single session.
Session resumption
If a session is interrupted (terminal closed, crash, timeout), the next session checks for an incomplete handover or todo list on startup and offers to resume.
# Called before each LLM call
def check_handover_needed(session):
used_tokens = estimate_token_count(session.context_window)
max_tokens = session.config.window_size
margin = session.config.handover_margin # e.g., 4000 tokens
if used_tokens + margin > max_tokens:
handover_doc = generate_handover(session)
start_fresh_session(handover_doc)
return True
return False
The Handover Schema
The handover document is a structured JSON object with exactly 12 fields. Each field captures a specific dimension of the session state. Together they provide everything a fresh agent needs to continue the work without repeating investigation or asking the user the same questions.
| # | Field | What it captures |
|---|---|---|
| 01 | original_goal |
The user's original request that started the task. This never changes across handovers, so every session knows what the end goal is, even after multiple handovers. |
| 02 | current_objective |
What the agent was working on when the handover was triggered. This is the immediate next sub-goal, more granular than the original goal. |
| 03 | code_state |
Snapshot of the codebase at handover time: branch, last commit hash from the built-in git repo, and a summary of the current state of modified files. |
| 04 | important_files_and_symbols |
File paths, function names, class names, and variables most relevant to the current objective. The next agent can load these directly without rediscovering them. |
| 05 | decisions_made |
Design decisions, tradeoffs accepted, and rationale. For example: “chose SQLite over PostgreSQL for simplicity because data volume is low”. Prevents the next agent from re-debating settled questions. |
| 06 | changes_applied |
Every file modification made so far: which files were created, modified, or deleted, and a summary of the change. The next agent uses this to understand what has already been done so it doesn't repeat work. |
| 07 | test_results |
Which tests were run, which passed, which failed, and any relevant output (error messages, stack traces, assertion failures). Saves the next agent from re-running the same failing tests. |
| 08 | errors_and_failed_attempts |
Approaches that were tried and did not work, along with the specific errors encountered. Prevents the next agent from repeating dead ends. |
| 09 | user_constraints |
Explicit preferences, restrictions, or requirements the user stated during the session. For example: “must stay on Python 3.10”, “do not touch the API layer”, “use type hints everywhere”. |
| 10 | things_not_to_repeat |
Explicitly lists actions the next agent must avoid duplicating: already-investigated dead ends, already-checked code paths, already-verified assumptions. |
| 11 | exact_next_step |
The precise next action the agent was about to take. This is not a vague “continue working” but a concrete instruction such as “run pytest tests/test_auth.py to verify the fix” or “update the callers in src/api/routes.py to handle the new error format.” |
| 12 | relevant_messages_tool_calls |
Key excerpts from the conversation: the most important user messages, agent responses, and tool call results that are relevant to the current objective. Acts as a “highlight reel” of the session. |
Why these 12 fields?
Each field covers a distinct failure mode of naive context truncation. The original_goal prevents drift across multiple handovers. decisions_made and things_not_to_repeat prevent wasted work. exact_next_step ensures the handover is not just a log but a continuation. Fields are deduplicated .; for example, “important files” and “symbols” live under the same field because they serve the same purpose.
How a New Session Consumes the Handover
When a new session starts from a handover, the document is inserted directly into the agent's system prompt. The LLM receives the handover fields as part of its initial instructions, so it knows from the very first token what has happened and what to do next.
The process looks like this:
# Pseudocode: how handover is loaded into a new session
def start_session(handover_doc=None):
system_prompt = load_system_prompt()
if handover_doc:
# Insert the handover as structured context at the top
# of the new session's system prompt
user_prompt = """User prompt {original_last_user_prompt} ## Handover Doc ... """
# Start the prompt loop. The LLM sees the handover
session = start_prompt_loop(system_prompt, user_prompt)
return session
What the new agent does first
- Reads
original_goalto understand the overall mission - Reads
current_objectiveandexact_next_stepto know what to do immediately - Loads
important_files_and_symbolsviaGetFileCodeSemantics/GetSymbolSourceCode - Checks
code_stateto confirm the working tree - Skips everything in
things_not_to_repeat - Executes the next step
What the new agent does not do
- It does not re-index the entire codebase (the index persists in
.raggie/.code_index.raggie) - It does not ask the user “what were you working on?”
- It does not re-read files it already knows about (unless needed)
- It does not re-run passing tests
- It does not re-debate already-made decisions
- It does not repeat failed approaches
The code index survives handover
The semantic code index is stored on disk (.raggie/.code_index.raggie) and is not regenerated on handover. The new session loads the existing index and only re-indexes files that changed during the previous session. This avoids the overhead of re-parsing the entire codebase on every handover.
Handover vs Context Compaction
Context compaction (asking the LLM to summarise the conversation) and structured handover serve the same surface need .; fitting a long session into a short window .; but they differ in every important way.
| Dimension | Context Compaction | Structured Handover |
|---|---|---|
| Format | Free-form natural language summary | Structured JSON with 12 predefined fields |
| Completeness | Depends on what the LLM chooses to include; tends to lose specifics | Forces inclusion of each field; nothing is optional |
| Decisions | Rarely includes rationale; tends to summarise outcomes only | Explicit decisions_made field with rationale |
| Failures | Usually omitted; summaries focus on successes | Explicit errors_and_failed_attempts field |
| Next action | Vague continuation “continue implementing X” | Exact concrete next step, e.g. “run this specific command” |
| Duplicate prevention | Not addressed; next agent may repeat work | Explicit things_not_to_repeat field |
| User constraints | Often lost if mentioned early in the conversation | Explicit user_constraints field preserved |
| Multiple handovers | Goal drifts as summaries get summarised | original_goal never changes across handovers |
| Token cost | Cheaper up front (single summary) | Slightly larger handover document, but fewer repeated actions |
| Predictability | Each compaction may drop different details | Deterministic schema; same fields every time |
Context compaction is not always worse. For simple Q&A sessions where the user just wants an answer and the conversation is a few turns, compaction is adequate and cheaper. But for multi-file coding tasks with test failures, rollbacks, and design decisions, structured handover provides continuity that free-form summaries cannot match.
Example Handover Document
Below is a real-world example of what a handover document looks like in practice. This was generated when RaggieCode was deep into fixing resumability bugs and edge cases so the agent could resume work at any point regardless of what tool it was calling or what subagent it was running. The handover captures the complete state: user request, 7 completed fixes across 4 files, 30 new tests, all symbol references, and the exact next edge case to investigate.
## User's Most Recent Request
"check for resumability bugs and edge cases . the agent should be able to resume its work at any point if it was working no matter what tool it was calling, what subagent it was working on. it should also be able to preserve main agent-subagents hierarchy when resuming"
## Current State of the Code
### Completed fixes (all tested, 480 tests passing):
1. Partial multi-tool-call resume . Added _get_pending_toolcalls() method to Agent class.
resume_dangling_tool_work now detects and executes remaining tool calls when only some
were executed before a crash.
2. Vision tool outputs saved to DB . _execute_tool_call now calls save_message() for
vision messages (role "user" with structured content + tool_call_id). Fixed save_message/
load_messages in chat_history_db.py to JSON-serialize list/dict content and deserialize on load.
3. Handover preserves subagent metadata . _perform_handover now calls get_session_info()
to copy parent_session_id, toolcall_id, and depth into the new session, preserving the hierarchy.
4. Redirect chain resolution . Added resolve_session_id(), get_session_info().
is_session_finished now follows redirects. dispatch_subagent.handle resolves child sessions
through the redirect chain.
5. Vision messages trigger resume . _has_dangling_tool_work now detects role == "user"
with tool_call_id as a tool response. resume_dangling_tool_work uses tool_call_id presence
instead of role == "tool".
6. Agent init resolves redirects . Agent.__init__ calls resolve_session_id(session_id)
so stale redirected sessions load from the active session.
7. Changes/files across redirect chain . Added get_all_changes_by_session_chain() and
get_all_session_files_chain(). Updated dispatch_subagent.py and todo_list.py to use them.
### Files modified:
src/Agent/agent.py . _has_dangling_tool_work, _get_pending_toolcalls, _execute_tool_call,
resume_dangling_tool_work, _perform_handover, __init__
src/Agent/chat_history_db.py . save_message, load_messages, is_session_finished,
+ resolve_session_id, get_session_info, get_all_changes_by_session_chain,
get_all_session_files_chain
src/Tools/dispatch_subagent.py . redirect resolution in handle, chain functions
src/Tools/todo_list.py . get_all_session_files_chain in handle_execute_next_task
tests/test_resumability_bugs.py . 30 new tests covering all fixes
## Important Files and Symbols
src/Agent/agent.py
Agent.resume_dangling_tool_work, Agent._has_dangling_tool_work,
Agent._get_pending_toolcalls, Agent._execute_tool_call,
Agent._perform_handover, Agent.__init__
src/Agent/chat_history_db.py
resolve_session_id, get_session_info, is_session_finished,
save_message, load_messages, get_all_changes_by_session_chain,
get_all_session_files_chain
src/Tools/dispatch_subagent.py
handle, _collect_subagent_output
src/Tools/todo_list.py
handle_execute_next_task
src/interactive.py
run_interactive (calls resume_dangling_tool_work at start)
tests/test_resumability_bugs.py . all 30 tests
tests/test_dangling_tool_resume.py . existing tests (still passing)
tests/test_subagent_session.py . existing tests (still passing)
## Errors, Blockers, or Failed Attempts
No current errors. All 480 tests pass.
Initial code_search tool failed in the first session . worked around by reading files directly.
## Exact Next Step
Look for additional edge cases in the todo_list.py crash recovery path . specifically
handle_execute_next_task when a task's toolcall_id doesn't match any child session
(e.g., if the child session was deleted or the toolcall_id was never set). Also consider
testing the full end-to-end resume flow with a mock LLM to verify the agent correctly
continues after partial tool execution + handover.
What happens next
A fresh session starts with this handover in its system prompt. The new agent reads the "Exact Next Step" section, opens src/Tools/todo_list.py, investigates the handle_execute_next_task crash recovery path for edge cases where a task's toolcall_id doesn't match any child session, and writes new tests to cover the full end-to-end resume flow .; all without asking the user a single question. The user sees the handover as a brief note in the terminal and then normal output resumes.
Limitations
Structured handover is not a silver bullet. It has real constraints that you should understand.
Handover quality depends on the LLM
The handover document is generated by the same LLM that is running the session. If the model is weak at structured output, the handover may miss details or include inaccuracies. Models with strong instruction-following (GPT-4o, Claude 3.5+, DeepSeek V3+) produce reliable handovers. Smaller models may produce incomplete documents.
User context is one-shot
If the user changes their requirements or provides new information in the middle of a session, that information is captured in user_constraints and relevant_messages_tool_calls. However, some small amount of context of the chat history may be missed. the window of loss is small but non-zero and can vary with model quality but has been consistently showing better results than classic context summery used in other models for compaction.
Design tradeoff
Handover prioritises compact, structured, actionable continuity over complete, lossless replay. This is the right tradeoff for coding tasks where the next agent needs to do something, not just know everything. If you need perfect replay, keep the terminal open and scroll back through the raw output. If you need the agent to keep making progress across session boundaries, handover is the mechanism.
Todo List Recovery
The handover mechanism pairs with RaggieCode's todo list system for robust multi-session task execution. When a handover occurs mid-todo-list, the active todo list is preserved in the database. The handover document records which tasks have been completed and which one is next.
On session startup, if an incomplete todo list is detected, the agent offers to resume it:
$ raggie code /path/to/project
Raggie Agent (code) v0.1.1
Indexing codebase...
Index up to date (3 files changed since last index)
[INFO] Found incomplete todo list from previous session:
Task 2/5: "Update API routes to use new validation" (PENDING)
Resume this todo list? [Y/n]: Y
Resuming task 2/5...
GetFileCodeSemantics(file_path="src/api/routes.py")
This means even if the entire session crashes .; not just a context window fill .; the work is not lost. The next session picks up the todo list and the handover document together, providing both what to do and what was already done.
Related Resources
Learn more about how handover fits into the broader RaggieCode architecture:
Getting Started
Install RaggieCode and run your first task to see handover in action.
Architecture Overview
Understand how the handover lifecycle fits into the agent's prompt loop.
Blog: Context Handover
A technical deep dive into why handover beats compaction for coding tasks.
Blog: Compaction vs Handover
A balanced comparison of both approaches with benchmarks.