From the Inside Claude Code: The Claude Code Ecosystem architecture map
Both public entry points share one control-protocol engine
query() reaches Control Protocol Query through the Internal Client and ClaudeSDKClient uses it directly; the engine in turn depends on the transport, Message Parser, SDK MCP Bridge, Transcript Mirror Batcher, Task Compat, Errors and Types. Its run-end logic (session state, in-flight task ledger, a 10-minute ceiling) decides when stdin closes for both.
Impact: A change to when stdin closes, or to how a control request is answered, alters hooks, permissions and in-process MCP tools for every SDK user at once.
The engine is well commented with issue references (#1088, #1190), and test_run_end_subprocess.py exercises the run-end paths against a subprocess; keep changes here behind those tests.
The trail
Open on the mapMore from this board
- Session Store is a single point of failure for 7 modules, from the clients to the package exports
- Optional SessionStore methods raise instead of being absent, so a change in Types reaches 13 modules
- Every defence against hidden instructions rests on one small sanitizer
- Interrupt and other control calls stall when nobody is reading messages
- Day 1: start with marketplace.json, the contract everything else serves
- Day 2: follow the install path from Claude Code into one plugin
- Session Store is shared by three groups of code, so a change to it reaches all of them
- Every SDK module imports its exceptions from one file, _errors.py
- can_use_tool is a ready gate seam with subagent provenance
- PR-controlled agent config is restored from the base branch before Claude starts
- Start with the gate order in the Run Orchestrator
- Then follow untrusted text from GitHub into the prompt
- The action and the base action share an untyped prompt-file contract
- The transport is the only translator from options to Claude Code CLI flags
- Learn issue automation as one loop from GitHub event back to GitHub
- The gh.sh wrapper is the safety contract both Claude jobs rest on
- The triage and dedupe agents are boxed in identity, tools and network
- Claude jobs reach GitHub only through a read-only wrapper and event-bound writes
- Day 3: the chat-channel flow shows the only long-running code
- Day 4: CI keeps the catalogue honest and current
- Public API code reaches straight into Session Store instead of going through a session entry point
- A failed SessionStore append is retried, then dropped; mirror_error is the only signal
- Channel messages carry sender identity, giving the session provenance
- CI already gates external plugins with an agent and reverts on failure
- Sessions copies the CLI's folder-naming hash, and 6 modules depend on that copy
- Spend and run length are bounded at the transport
- Tag mode gives Claude a narrow, workspace-bound tool list
- Skip post-run reporting and branch plumbing at first
- Command-line construction is hardened against argument injection
- Tool permission requests fail closed
- Skip the engine, mods and examples when the goal is issue automation
- Lifecycle timeouts are restated outside their single source of truth
- The duplicate comment script is pinned to the upstream repository
- Every agent-driven closure has a human override before it happens
- Skip for now: config-only integrations, CI plumbing and single-file plugins
- Key vocabulary: marketplace entry, plugin, channel, pairing, permission relay, SHA bump
- Plugins are fully independent and deploy on their own
- External-PR workflows never execute contributor code
- Channel servers keep bot tokens owner-only and refuse injected pairing requests