At commit 9d6afd8, nanobot’s highest activity-weighted risk is concentrated in two functions that are structurally extreme and actively changing: _process_message in nanobot/channels/weixin.py scores a cyclomatic complexity of 127, and agent in nanobot/cli/commands.py was touched as recently as today with 4 commits in the last 30 days. nanobot is a Python AI agent framework with 1,709 analyzed functions, 385 of which sit in the critical band — meaning nearly one in four functions warrants immediate attention. Every top hotspot falls in the fire quadrant: high structural complexity combined with ongoing commit activity, making these live regression risks rather than deferred cleanup.
The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).
Top 5 Hotspots
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
_process_message | nanobot/channels/weixin.py | 19.6 | 127 | 7 | 29 |
agent | nanobot/cli/commands.py | 19.6 | 33 | 7 | 59 |
run | nanobot/agent/runner.py | 18.6 | 81 | 5 | 45 |
consume_sdk_stream | nanobot/providers/openai_responses/parsing.py | 18.5 | 79 | 8 | 14 |
run_interactive | nanobot/cli/commands.py | 18.2 | 30 | 6 | 35 |
Hotspot Analysis
_process_message — nanobot/channels/weixin.py
Based on its name and file path, _process_message is almost certainly the central dispatch handler for inbound WeChat (Weixin) messages — a function responsible for routing, interpreting, and responding to a wide variety of message types and events. Its cyclomatic complexity of 127 is extreme by any standard: it represents at least 127 independent execution paths, each a potential bug surface and a required test case. A max nesting depth of 7 compounds this, meaning some logic is buried seven control-structure levels deep, and fan-out of 29 distinct callees means a change here sends ripple effects across nearly 30 downstream functions. With an activity-weighted risk score of 19.6 and a commit touching it just 7 days ago, this is a live regression risk — every edit navigates 127 branches with no obvious safety net.
Recommendation: Before any further changes, add characterization tests that exercise the major message-type branches to establish a behavioral baseline. Then begin decomposing _process_message using extract-method refactoring: each message type or event category is a natural extraction boundary, which will immediately reduce CC and ND while making individual paths independently testable.
agent — nanobot/cli/commands.py
The agent function in nanobot/cli/commands.py is almost certainly the top-level CLI command handler for launching or configuring an agent — likely parsing arguments, wiring up dependencies, and orchestrating the agent lifecycle from the command line. Its fan-out of 59 is the most striking structural signal in the dataset: this single function calls 59 distinct downstream functions, making it the highest-coupling point in the analyzed codebase and a hub whose changes ripple into a vast surface area. A cyclomatic complexity of 33 and nesting depth of 7 add significant branching and readability burden on top of that coupling. Critically, this function was modified today and has been touched 4 times in the last 30 days — making it the most actively changing high-complexity function in the project and an immediate live regression risk.
Recommendation: Map the 59 fan-out callees to identify which argument-parsing, configuration, and orchestration concerns can be extracted into focused helper functions — reducing coupling and making the command handler itself a thin coordinator. Prioritize this before the next feature addition, as each new commit to a CC-33, FO-59 function raises the probability of an unintended interaction.
run — nanobot/agent/runner.py
Based on its name and location in the runner module, run is the core agent execution loop — orchestrating the lifecycle of an AI agent turn: invoking tools, handling responses, and managing iteration until completion or a stop condition. Its cyclomatic complexity of 81 reflects the branching logic required to handle the many ways an agent turn can proceed — tool calls, streaming responses, errors, and termination. A nesting depth of 5 and fan-out of 45 indicate both layered conditional logic and broad coordination across the provider, tool, and state management layers. With an activity-weighted risk score of 18.6, any change to this function risks introducing subtle behavioral regressions across the core agent execution path.
Recommendation: Decompose the execution loop by concern — tool dispatch, response handling, and loop termination are natural extraction boundaries. Reducing fan-out from 45 toward a narrower set of well-defined collaborators will make the function easier to test and reason about as the framework evolves.
consume_sdk_stream — nanobot/providers/openai_responses/parsing.py
The consume_sdk_stream function handles streaming responses from the OpenAI Responses API — parsing incremental SDK events as they arrive and assembling them into structured output. Its nesting depth of 8 is the highest in the top 5, indicating deeply nested conditional logic for handling the variety of event types in a streaming protocol — each missed or mishandled event type is a potential silent data-loss bug. A cyclomatic complexity of 79 means there are at least 79 distinct paths through the parsing logic, and an activity-weighted risk score of 18.5 confirms this is an actively evolving function.
Recommendation: Map each streaming event type to an explicit handler function, collapsing the nested conditionals into a dispatch table or match statement. This reduces nesting depth substantially while making each event path independently testable — critical for a parsing function where correctness is hard to observe at runtime.
run_interactive — nanobot/cli/commands.py
The run_interactive function handles interactive CLI sessions — likely managing the REPL-style loop that allows users to interact with nanobot from the command line. Its cyclomatic complexity of 30 and nesting depth of 6 indicate meaningful branching over user input, command parsing, and session state. A fan-out of 35 means it coordinates across a broad set of downstream functions for each interaction. Sharing a file with the agent function (also in nanobot/cli/commands.py), the two command handlers are at risk of entanglement as CLI behavior grows.
Recommendation: Separate input parsing from session orchestration — run_interactive likely mixes “what did the user type” with “what should happen next.” Extracting the input loop into a thin coordinator that delegates to typed handlers will reduce both cyclomatic complexity and the nesting depth driven by input-type branching.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
complex_branching | 5 |
deeply_nested | 5 |
god_function | 5 |
long_function | 5 |
exit_heavy | 4 |
These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.
Key Takeaways
_process_messageinnanobot/channels/weixin.pyhas a cyclomatic complexity of 127 — add characterization tests covering its major branches before any further edits to avoid blind refactoring.- The
agentfunction innanobot/cli/commands.pycalls 59 distinct downstream functions and was committed to today — its fan-out makes it the single highest-coupling point in the analyzed codebase and the most urgent candidate for decomposition. - The 385 critical-band functions in nanobot are both structurally complex and recently active; the top hotspots show that the WeChat channel and CLI orchestration layers are where that risk is most concentrated right now.
Reproduce This Analysis
git clone https://github.com/HKUDS/nanobot
cd nanobot
git checkout 9d6afd86b58f46437e796f586cd9be834b1db2ea
hotspots analyze . --mode snapshot --explain-patterns --force
To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.
Hotspots highlights structural and activity risk — not “bad code.” Findings are a prioritization aid, not a bug predictor. Editorial policy →