Source reading and debugging minimum
Learning objectives
After this lesson, you should be able to:
- trace source from an entry point instead of guessing from class names;
- use
rgto find definitions, callers, configuration, and tests; - run the pytest test closest to a problem;
- choose meaningful breakpoints and variables to inspect;
- distinguish proven facts, reasonable inferences, and unknowns.
Prerequisites
- Use PowerShell.
- Run Python.
- Read a basic traceback.
1. Start with a traceable question
Do not begin by trying to “read the entire agent/ directory.” Choose one concrete question, such as:
- How does one CLI message enter
AgentLoop? - Where is the session key determined?
- Where is a Tool Call executed?
- Where does API streaming close?
- Why can one session not be written concurrently?
The more specific the question, the smaller the reading scope and the more reliable the resulting call chain.
2. Begin at the entry point and its callers
Use this order:
Entry point
→ Which core objects does it create?
→ Which public method does it call?
→ How does that method enter a state machine or loop?
→ Where is state saved?
→ Who receives the output?For example, to trace one CLI message:
- find the
agentcommand innanobot/cli/commands.py; - locate the
process_directcall; - enter
AgentLoop.process_direct(); - enter
_process_message(); - inspect the
TurnStatetransitions; - enter
_run_agent_loop(); - inspect
AgentRunner.run(); - return to
_state_save()and_state_respond().
3. Search with rg
Run these commands from example/nanobot/code-EN:
rg -n "class AgentLoop|def process_direct" nanobot
rg -n "process_direct\\(" nanobot tests
rg -n "session_key" nanobot/session nanobot/agent tests
rg -n "should_execute_tools" nanobot testsA useful search order is:
- definition;
- all callers;
- configuration fields;
- related tests;
- error or log text.
Search the log text
When runtime output contains a clear log or error, search for a short, stable fragment of that text. It is often faster than guessing the function name.
4. Tests are executable implementation notes
A test can prove that a boundary is deliberately protected by the project. For example:
| Question | Test to read first |
|---|---|
| How AgentLoop connects to Runner | tests/agent/test_loop_runner_integration.py |
| How Tool Calls execute | tests/agent/test_runner_tool_execution.py |
| How Runner handles errors | tests/agent/test_runner_errors.py |
| Whether Session saving is atomic | tests/agent/test_session_atomic.py |
| Runtime Event order | tests/bus/test_runtime_events.py |
| Channel plugin discovery | tests/channels/test_channel_plugins.py |
| Subagent lifecycle | tests/agent/test_subagent_lifecycle.py |
Run one test file:
Set-Location "example\nanobot\code-EN"
pytest tests/agent/test_loop_runner_integration.py -qRun one test function:
pytest tests/agent/test_runner_tool_execution.py::test_name -qFirst verify the real function name with rg -n "^def test_|^async def test_"; do not invent it from the lesson.
5. Put breakpoints at state boundaries
For a normal Turn, inspect these locations first:
| Location | Observe |
|---|---|
AgentLoop.process_direct or _dispatch | entry point, session key, metadata |
_process_message | initial TurnContext state |
_state_restore | Session, checkpoint, media |
_state_build | history, initial messages, tool context |
AgentRunner._run_core | iteration, response, tool calls |
_execute_tools | tool selection, arguments, concurrency strategy |
_state_save | new messages, stop reason, latency |
_state_respond | OutboundMessage |
Do not step into a Provider SDK or WebUI component first. Identify the core state boundary where behavior diverges from expectation.
6. Observe three message sets
When debugging context, distinguish at least:
Session.messages: the persistent conversation;ctx.initial_messages: the initial input to AgentRunner;messages_for_model: the governed and trimmed messages actually sent to the model.
Many “the model forgot” or “the tool result disappeared” problems originate in the differences among these three views.
7. Record evidence, not only conclusions
Use a note structure like this:
### Observation
- Entry: `nanobot/cli/commands.py::agent`
- Call: `AgentLoop.process_direct`
- State: `BUILD`
- Variable: `ctx.initial_messages`
- Test: `tests/agent/test_loop_runner_integration.py`
### Conclusion
Source fact: ...
### Not yet proven
- Whether every Channel supplies the same metadata
- Whether external plugins guarantee the same lifecycle8. Do not modify the source snapshot to test a guess
During the core learning path, prefer:
- breakpoints;
- existing logs;
- temporary interactive inspection;
- existing tests;
- minimal read-only scripts.
In the Source Practice extension tasks, make explicit, local, verifiable changes only inside learning-EN/examples or an isolated copy. Keep the code-EN source snapshot read-only throughout.
Windows notes
This workspace uses PowerShell. When reading and running nanobot:
- reason about paths with
pathlib.Path; do not assume/; - nanobot’s ExecTool uses
cmd /con Windows; - the CLI handles UTF-8 stdout/stderr;
- MCP stdio commands include Windows path normalization;
- do not apply Linux bubblewrap behavior to Windows.
Related notes:
example/nanobot/code-EN/.agent/gotchas.mdexample/nanobot/code-EN/.agent/security.md
Self-check task
Without modifying code:
- find every production caller of
AgentLoop.process_direct; - find the definition of
InboundMessage.session_key; - find where
ToolLoaderscans external entry points; - find where Session atomically replaces its target file;
- locate at least one matching test.
Completing this task gives you the minimum source-reading ability needed to begin tracing nanobot.
Next step
Return to the learning roadmap and continue to Runtime and Source Map.