Layer Architecture and Import Cycle Rules in DeepSeek-Reasonix: A Complete Guide
DeepSeek-Reasonix implements a strict layered Go architecture with acyclic dependencies where higher-level packages import lower-level ones but never vice versa, enforced by a custom repolint tool.
DeepSeek-Reasonix is a layered Go application designed for predictable builds and clear separation of concerns. The codebase enforces strict layer architecture and import cycle rules that prevent circular dependencies and ensure modular evolution across CLI, agent, plugin, config, tool, and provider layers.
Understanding the Six-Layer Architecture
The architecture is documented in docs/SPEC.md and organizes the codebase into six distinct layers with unidirectional dependencies:
- CLI Layer (
internal/cli/*): Handles command-line parsing, sub-command routing, and flag handling. Imports agent, plugin, and config. - Agent Layer (
internal/agent/*): Manages session loops, message orchestration, and plan/execution coordination. Imported by CLI; imports tool and provider. - Plugin Layer (
internal/plugin/*): Implements MCP (JSON-RPC) client and transport abstraction. Imported by CLI; imports tool and provider. - Config Layer (
internal/config/*): Handles TOML loading, hierarchical overrides, and validation. Imported by CLI; imports tool and provider. - Tool Layer (
internal/tool/*): Contains built-in and plugin-provided tools with schema registry. Imported by agent, plugin, and config. - Provider Layer (
internal/provider/*): Implements model-provider backends like OpenAI. Imported by agent, plugin, and config.
According to docs/SPEC.md, the dependency direction is strictly acyclic: cli → {agent, plugin, config} → {tool, provider}【/cache/repos/github.com/esengine/DeepSeek-Reasonix/main-v2/docs/SPEC.md#L54】.
Core Import Cycle Rules
The codebase enforces six critical rules to maintain architectural integrity.
1. Strict Acyclic Dependencies
No package may import any package that eventually imports back to it. The Go compiler rejects cyclic imports at build time, but DeepSeek-Reasonix adds a custom layer of protection through the repolint tool.
2. Parents Never Import Children
Higher-level packages must never import their children. As documented in tools/repolint/layers.go, utility-layer packages "must not import children"【/cache/repos/github.com/esengine/DeepSeek-Reasonix/main-v2/tools/repolint/layers.go#L22-L25】. This ensures that business logic in lower layers cannot be contaminated by presentation or orchestration concerns.
3. Utility Layer Purity
Utility packages contain only standard-library code and must remain independent of "kernel" (core) packages. The repolint configuration explicitly states that utility layers "carry no knowledge of the kernel"【/cache/repos/github.com/esengine/DeepSeek-Reasonix/main-v2/tools/repolint/layers.go#L22-L24】.
4. Self-Registration Pattern
Built-in sub-packages may import their parent solely for self-registration via init() functions. For example, internal/provider/openai registers itself with the provider registry, but internal/provider never imports openai. The same pattern applies to internal/tool/builtin.
5. Remote-SSH Module Compliance
The Remote-SSH subsystem (remote/bootstrap, remote/forward, remote/sftpfs) forms a vertical stack that respects the same layering constraints and does not import higher-level layers like cli or agent【/cache/repos/github.com/esengine/DeepSeek-Reasonix/main-v2/docs/SPEC.md#L55-L59】.
6. Automated Enforcement with repolint
The custom repolint linter validates all import relationships against the layering graph. It checks the Go AST and reports failures such as "TypeScript imports must stay out of the layering graph" or layer violations【/cache/repos/github.com/esengine/DeepSeek-Reasonix/main-v2/tools/repolint/size_test.go#L60】. The layering rule is registered in tools/repolint/main.go【/cache/repos/github.com/esengine/DeepSeek-Reasonix/main-v2/tools/repolint/main.go#L32】.
Practical Code Examples
Correct usage follows the dependency arrow from higher to lower layers:
// internal/agent/agent.go
import (
"github.com/esengine/DeepSeek-Reasonix/internal/tool"
"github.com/esengine/DeepSeek-Reasonix/internal/provider"
)
Incorrect usage attempts to import upward, which repolint rejects:
// internal/tool/tool.go ← ❌ illegal import
import (
"github.com/esengine/DeepSeek-Reasonix/internal/agent"
)
Attempting this import causes repolint to fail with a message like "tool package must not import agent".
Summary
- DeepSeek-Reasonix organizes code into six layers: CLI, Agent, Plugin, Config, Tool, and Provider.
- Dependencies flow strictly downward:
cli → {agent, plugin, config} → {tool, provider}. - Higher-level packages must never import lower-level ones, except for self-registration via
init(). - Utility packages remain pure with no kernel dependencies.
- The
repolinttool enforces these rules at build time to guarantee acyclic graphs.
Frequently Asked Questions
What happens if I violate the import cycle rules in DeepSeek-Reasonix?
The repolint linter will fail the build with a specific error message indicating which layer violated the dependency direction. Since the rules are also enforced by Go's compiler for actual cycles, the codebase provides double protection against architectural decay.
How does the self-registration pattern work without creating import cycles?
Child packages like internal/provider/openai import their parent package to register capabilities via init() functions, but the parent never imports the child. This creates a one-way dependency edge that satisfies the acyclic requirement while allowing dynamic plugin discovery.
Why does DeepSeek-Reasonix use a custom linter instead of standard Go tools?
While Go's compiler prevents circular imports, it does not enforce architectural layering (e.g., preventing tool from importing agent). The repolint tool validates semantic layer boundaries defined in docs/SPEC.md, ensuring that business logic remains properly isolated across the six-layer stack.
Can I add new layers to the DeepSeek-Reasonix architecture?
New layers must be inserted into the dependency graph defined in docs/SPEC.md and registered in tools/repolint/layers.go. Any new layer must follow the acyclic rule: it can only import layers below it and must not be imported by layers above it unless explicitly designed as a utility layer with no kernel dependencies.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →