Status: Draft
This RFC describes a modular target architecture for jcode that matches the current codebase, preserves the existing product model, and gives us a safe migration path from today's mostly-monolithic root crate to a layered workspace.
It is intentionally aligned with:
REFACTORING.mdCOMPILE_PERFORMANCE_PLAN.mdSERVER_ARCHITECTURE.mdMULTI_SESSION_CLIENT_ARCHITECTURE.md
- Document the architecture that exists today, not an idealized version.
- Define a target layered and crate architecture that improves maintainability and compile times.
- Establish dependency rules that prevent the workspace from collapsing back into a monolith.
- Provide a phased migration plan that fits the refactoring roadmap and compile-performance plan.
- Preserve runtime behavior: one shared server, reconnecting clients, session-local self-dev capability, and stable tool/provider flows.
- A big-bang rewrite.
- Renaming every module or crate immediately.
- Forcing every subsystem into a separate crate before its boundaries are ready.
- Changing the core product architecture from single-server, multi-client.
Today, jcode is best described as a modular monolith with a growing workspace shell:
- The root
jcodecrate still owns most runtime orchestration and product behavior. - Several heavy or relatively self-contained subsystems have already moved into workspace crates.
- The codebase has strong module-level separation in some areas, but several broad root modules still act as architectural chokepoints.
The target architecture is a layered workspace:
- Foundation layer for stable shared types and runtime primitives.
- Domain/runtime layer for session, agent, provider, and server logic.
- Interface layer for CLI, TUI, self-dev, and optional heavy integrations.
- Composition layer where the top-level
jcodepackage wires the product together.
The most important design rule is this:
High-churn orchestration code must depend on stable lower layers, while stable lower layers must never depend back on runtime/UI/product-specific code.
That rule serves both architecture quality and compile-speed goals.
At the product level, the runtime architecture is already clear:
jcodeis a single-server, multi-client application.- The server owns sessions, swarm state, background tasks, provider state, and shared services.
- Clients are primarily TUI frontends that attach to server-owned sessions.
- Self-dev is session-local capability on the shared server, not a separate architecture.
That model should stay intact.
The current code organization is mixed:
- Root crate
jcodestill contains most product logic. - Workspace crates already isolate several heavy or stable seams.
- Subdirectories under
src/increasingly reflect domain boundaries, especially foragent,cli,server,tool, andtui.
Current workspace members from Cargo.toml are grouped roughly as follows:
- root package:
jcode - foundation/runtime support:
jcode-agent-runtime,jcode-core,jcode-storage,jcode-terminal-launch,jcode-tool-core - data-contract crates:
jcode-ambient-types,jcode-auth-types,jcode-background-types,jcode-batch-types,jcode-config-types,jcode-gateway-types,jcode-memory-types,jcode-message-types,jcode-selfdev-types,jcode-session-types,jcode-side-panel-types,jcode-task-types,jcode-tool-types,jcode-usage-types - protocol and planning:
jcode-protocol,jcode-plan - heavy or optional integrations:
jcode-embedding,jcode-pdf,jcode-notify-email - auth and providers:
jcode-azure-auth,jcode-provider-core,jcode-provider-metadata,jcode-provider-openrouter,jcode-provider-gemini - TUI extraction seams:
jcode-tui-core,jcode-tui-markdown,jcode-tui-mermaid,jcode-tui-render,jcode-tui-workspace - product surfaces outside the main TUI binary:
jcode-desktop,jcode-mobile-core,jcode-mobile-sim
The root crate still directly owns most of the following concerns:
- CLI parsing and dispatch
- server orchestration and socket lifecycle
- session state and persistence
- agent turn execution and tool orchestration
- provider implementation composition and runtime provider wiring; the shared
Providertrait now lives injcode-provider-core - protocol/message/config types
- tool registry and many tool implementations
- TUI application state and rendering
- auth, memory, safety, ambient mode, and product glue
This is why the root crate is still the primary compile and architecture hotspot.
These splits already exist and should be treated as real architectural footholds, not temporary accidents:
| Crate | Current role |
|---|---|
jcode-agent-runtime |
shared interrupt and lightweight runtime primitives for agent execution |
jcode-ambient-types |
usage and rate-limit records shared by ambient/background flows |
jcode-auth-types |
provider-neutral auth state and credential metadata |
jcode-background-types |
background-task status and progress DTOs |
jcode-batch-types |
batch tool progress DTOs, currently depending only on message types internally |
jcode-config-types |
stable configuration data contracts |
jcode-core |
low-level utilities such as IDs, env helpers, fs helpers, stdin detection, and formatting |
jcode-gateway-types |
gateway-facing data contracts |
jcode-memory-types |
memory subsystem data contracts |
jcode-message-types |
message content and transport-adjacent data contracts |
jcode-protocol |
client/server protocol surface built from stable type crates and provider-core values |
jcode-plan |
plan/task graph data model shared across coordination flows |
jcode-selfdev-types |
self-development request/status data contracts |
jcode-session-types |
session DTOs, currently depending only on message types internally |
jcode-side-panel-types |
side-panel page and update data contracts |
jcode-task-types |
task/tool scheduling data contracts |
jcode-tool-core |
runtime tool contracts such as the Tool trait and execution context |
jcode-tool-types |
stable tool output/image DTOs |
jcode-usage-types |
usage accounting data contracts |
jcode-storage |
storage helpers layered on jcode-core |
jcode-embedding |
ONNX/tokenizer-based embedding implementation and heavy inference deps |
jcode-pdf |
PDF text extraction |
jcode-azure-auth |
Azure bearer token retrieval |
jcode-notify-email |
SMTP/IMAP/mail transport |
jcode-provider-metadata |
provider/login catalog and profile metadata |
jcode-provider-core |
shared provider contract (Provider/EventStream), value types, route/cost/model helpers, shared HTTP client, schema helpers |
jcode-provider-openrouter |
OpenRouter-specific catalog/cache/support helpers |
jcode-provider-gemini |
Gemini schema/model/support helpers |
jcode-tui-core |
low-level terminal UI primitives that do not need full app state |
jcode-tui-markdown |
markdown wrapping/rendering, layered on mermaid/workspace support |
jcode-tui-mermaid |
mermaid parsing, rendering, caching, viewport, and widget support |
jcode-tui-render |
reusable TUI layout/render helpers |
jcode-tui-workspace |
workspace-map data/model/widget rendering |
jcode-terminal-launch |
terminal process launch helpers |
jcode-mobile-core |
shared headless mobile simulator state/protocol/visual model |
jcode-mobile-sim |
mobile simulator CLI/app surface layered on jcode-mobile-core |
jcode-desktop |
desktop app surface and session/workspace rendering experiments |
These are already aligned with the compile-performance plan's strategy: isolate heavy dependencies and stable helper surfaces first.
The root crate still has several broad, high-fanout modules that make both maintenance and incremental compilation harder. Current sizes observed from the tree:
src/server.rs: ~1731 linessrc/provider/mod.rs: ~2283 linessrc/session.rs: ~2730 linessrc/protocol.rs: ~1198 linessrc/main.rs: ~55 lines
This supports the current plan direction:
- CLI decomposition is already mostly underway and should continue.
- Server, provider, session, and TUI state boundaries remain the most important structural work.
- The top-level binary entrypoint is already close to the desired thin composition shape.
flowchart TD
J[jcode root crate]
J --> CLI[CLI and startup]
J --> Server[Server orchestration]
J --> Session[Session and persistence]
J --> Agent[Agent turn loop and tools]
J --> Provider[Provider trait and runtime impls]
J --> TUI[TUI app and rendering]
J --> Coreish[Protocol, message, config, ids]
J --> Product[Auth, memory, safety, ambient, notifications]
J --> AR[jcode-agent-runtime]
J --> Emb[jcode-embedding]
J --> PDF[jcode-pdf]
J --> Azure[jcode-azure-auth]
J --> Mail[jcode-notify-email]
J --> PMeta[jcode-provider-metadata]
J --> PCore[jcode-provider-core]
J --> POR[jcode-provider-openrouter]
J --> PGem[jcode-provider-gemini]
J --> TW[jcode-tui-workspace]
Today the root crate acts as all of the following at once:
- domain model holder
- runtime orchestrator
- UI host
- provider abstraction layer
- integration shell
- compile boundary for unrelated edits
That makes it hard to reason about ownership and easy to create accidental coupling.
Broadly reused types like protocol structures, message forms, IDs, route metadata, and config types should be more stable than server, TUI, or provider orchestration logic. Today many of these still live in the same crate and sometimes in the same dependency fanout path.
The existing workspace crates are good first splits, but they mostly isolate leaves. The center of gravity is still inside the root crate, especially around:
- session state
- provider runtime behavior and concrete provider composition
- server lifecycle
- tool registry wiring
- TUI app state and reducers
The compile-performance plan is correct that crate boundaries matter most. The same boundaries that reduce invalidation pressure also improve ownership and testability.
The target is a layered workspace with a thin composition root. Arrows below mean "depends on".
flowchart TD
App[jcode top-level package]
subgraph L2[Layer 2: interfaces and product surfaces]
TUI[jcode-tui]
SelfDev[jcode-selfdev]
CLI[jcode-cli or root CLI modules]
end
subgraph L1[Layer 1: domain/runtime]
Server[jcode-server]
Agent[jcode-agent]
Provider[jcode-provider]
Session[jcode-session]
end
subgraph L0[Layer 0: foundation and support]
Core[jcode-core]
AR[jcode-agent-runtime]
Emb[jcode-embedding]
PDF[jcode-pdf]
Azure[jcode-azure-auth]
Mail[jcode-notify-email]
PMeta[jcode-provider-metadata]
PCore[jcode-provider-core]
POR[jcode-provider-openrouter]
PGem[jcode-provider-gemini]
TW[jcode-tui-workspace]
end
App --> Server
App --> TUI
App --> SelfDev
App --> CLI
CLI --> Server
CLI --> TUI
CLI --> Core
TUI --> Core
TUI --> TW
SelfDev --> Server
SelfDev --> Core
Server --> Agent
Server --> Provider
Server --> Session
Server --> Core
Server --> Mail
Agent --> Provider
Agent --> Session
Agent --> Core
Agent --> AR
Provider --> Core
Provider --> PCore
Provider --> PMeta
Provider --> POR
Provider --> PGem
Provider --> Azure
Session --> Core
Session --> Emb
Session --> PDF
The exact crate names can evolve, but the dependency direction should not.
The optimal crate structure is not "one crate per folder". The target should optimize for three forces at the same time:
- Invalidation boundaries: high-churn edits should not rebuild unrelated stable subsystems.
- Dependency weight boundaries: heavy dependencies should sit behind leaf crates or opt-in features.
- Ownership boundaries: each crate should have one reason to change and a small public API.
The current root-crate size distribution makes the main opportunity clear: src/tui, src/server, src/tool, src/provider, src/cli, and src/auth dominate root-crate lines. Splitting only tiny helpers is useful as a safe staging tactic, but the long-term win is moving these high-churn domains behind stable lower-layer contracts.
These crates should be small, low-dependency, and slow-changing. They are allowed to be depended on broadly.
Existing examples:
jcode-message-typesjcode-tool-typesjcode-session-typesjcode-config-typesjcode-protocoljcode-provider-corejcode-planjcode-*-types
Target direction:
- Keep these crates boring and DTO-heavy.
- Prefer
serde,chrono, and small utility dependencies only. - Avoid
tokio,reqwest,ratatui, provider SDKs, storage paths, and product orchestration. - If a type requires a service handle, task runtime, channel sender, or filesystem layout, it is probably not a pure contract type.
Compile-time reason:
- These crates will be rebuilt whenever public contracts change, so they must change rarely.
- They allow
server,tui,agent, andprovidercrates to talk without depending on the root crate.
These own product behavior but should depend only downward on contracts/support crates.
Target crates:
jcode-provider: provider composition, provider routing, streaming contract adapters, and concrete runtime implementations layered on thejcode-provider-coretrait.jcode-agent: turn loop, compaction orchestration, provider/tool interaction, recovery logic.jcode-session: session model, state transitions, persistence-facing session operations.jcode-server: daemon lifecycle, client attachment, swarm/background coordination, service registries.jcode-toolsor narrowerjcode-tool-coreplusjcode-tool-impl: tool registry contracts and tool implementations.jcode-auth: root auth orchestration after provider-neutral data lives injcode-auth-typesand heavy leaf SDKs stay separate.jcode-memory: memory graph/log/search orchestration once its contracts are stable enough.
Compile-time reason:
- These are the main root invalidation hotspots.
- They should become independent enough that an edit in TUI rendering does not rebuild provider implementations, and an edit in provider routing does not rebuild server socket lifecycle.
These are high-churn application surfaces and should sit above runtime/domain crates.
Target crates:
jcode-cli: parsing and command dispatch if CLI keeps growing.jcode-tui: app state, reducers, key handling, command/input handling, UI orchestration.jcode-desktop: already a separate surface.jcode-mobile-*: already split.jcode-selfdev: self-dev build/reload/customization workflows if they remain a substantial product surface.
Compile-time reason:
- UI and CLI are edited frequently. Their churn should not force recompilation of stable server/provider/session internals.
- TUI should depend on protocol/service contracts, not on concrete server internals.
These should remain isolated and often feature-gated.
Existing examples:
jcode-embeddingjcode-pdfjcode-azure-authjcode-notify-emailjcode-tui-mermaid- provider support crates such as
jcode-provider-openrouterandjcode-provider-gemini
Target direction:
- Keep heavy dependencies out of the root crate and out of broadly shared contracts.
- Prefer opt-in features when the product can degrade gracefully.
- Keep a thin root/domain facade when runtime integration still belongs at a higher layer.
Compile-time reason:
- Heavy crates are fine when cached, but terrible when dragged into unrelated rebuilds.
- Feature-gated leaves make local inner loops cheaper without removing full-product builds.
The top-level jcode package should eventually become mostly:
- binary entrypoints
- feature defaults
- runtime graph assembly
- compatibility re-exports/facades during migration
- product configuration and packaging defaults
It should not be the long-term home of large implementation modules.
A healthy final graph should look like this:
jcode binary/composition
-> jcode-cli, jcode-tui, jcode-server, jcode-selfdev
jcode-cli / jcode-tui
-> jcode-protocol, jcode-*-types, jcode-server-client contracts
jcode-server
-> jcode-agent, jcode-session, jcode-provider, jcode-tools, jcode-storage
jcode-agent
-> jcode-provider, jcode-tools, jcode-session, jcode-agent-runtime
jcode-provider
-> jcode-provider-core, jcode-provider-* leaves, jcode-auth-types
jcode-session
-> jcode-session-types, jcode-message-types, jcode-storage, optional leaf adapters
contract/type crates
-> serde and small support crates only
The forbidden direction is just as important:
- contract crates must not depend on runtime/domain crates
- provider crates must not depend on TUI or server crates
- TUI crates must not depend on concrete server internals when protocol/client contracts are sufficient
- leaf adapter crates must not become backdoors into the root crate
- the root crate should not be required by workspace peers except temporarily during migration
A root module is ready to become a crate when most of these are true:
- Its public API can be described in less than a page.
- It does not need to call back into arbitrary root modules.
- Its dependencies are either lower-layer contracts or intentionally owned leaf adapters.
- Tests can run at the crate level without booting the full product.
- A touched-file benchmark shows it is on a meaningful invalidation path.
- It has a stable facade in the root crate for compatibility during migration.
If these are not true yet, keep decomposing internally first.
Avoid these tempting but harmful structures:
- One mega
jcode-commoncrate. It becomes the new root crate and invalidates everything. - One crate per source directory. This creates noisy APIs and dependency cycles without compile wins.
- Moving high-churn traits too early. A poorly stabilized trait crate can become worse than the monolith.
- Moving UI-adjacent state into core. This contaminates lower layers with
ratatui/terminal concepts. - Provider leaf crates depending on root. That prevents the root from ever becoming a composition shell.
- Splitting by dependency weight only. Heavy leaf isolation is good, but ownership and API stability matter too.
Based on the current root size and existing footholds, the best next work is probably:
- Provider contracts: keep shrinking
src/provider/mod.rsuntil ajcode-providertrait/runtime crate can depend only onjcode-message-types,jcode-provider-core, and small runtime primitives. - Server core: extract protocol-independent pieces of
src/server/such as client lifecycle state machines, swarm/background coordination DTOs, and reload/update policies behind server-local contracts. - TUI reducer/state core: extract non-rendering app state transitions from
src/tui/app/*before moving the whole TUI crate. - Tool contracts and registry shape: separate tool definitions, schemas, execution context, and registry metadata from individual tool implementations.
- Session domain: isolate session state transitions and persistence-facing operations from server/TUI/provider orchestration.
- Auth facade: keep provider-neutral auth data in
jcode-auth-types, heavy SDKs in leaf crates, and move root auth orchestration only after provider contracts stabilize.
A useful near-term policy: every time a large root file is touched, ask whether some pure table, DTO, parser, reducer, classifier, or state transition can move downward into an existing support crate without pulling runtime dependencies with it.
Each structural phase should record at least:
- touched-file
cargo checkfor the edited hotspot - touched-file selfdev build for the edited hotspot
cargo tree -p jcode --edges normal --depth 1before/after for dependency surprises- crate-level test coverage for newly extracted crates
A split is successful if it either:
- lowers warm touched-file times for common edits, or
- prevents unrelated heavy crates from rebuilding when the root changes, or
- makes the next larger extraction materially safer.
A split should be reconsidered if it adds public API churn, creates cycles, or requires broad root re-exports that hide the actual dependency direction.
Purpose: stable shared types and utilities with minimal dependencies.
Should contain:
- IDs and naming primitives
- protocol DTOs that are not server-implementation-specific
- message/content/tool-definition types shared across runtime layers
- config primitives and enums that do not require runtime services
- small shared utility types with high reuse
Should not contain:
- TUI code
- server lifecycle code
- provider network code
- tokio task orchestration unless truly unavoidable
- product-specific wiring
Notes:
- This is the most important future extraction because it enables the rest.
src/protocol.rs,src/id.rs, and carefully selected parts ofconfig.rsandmessage.rsare the likely first feeders.
Purpose: session domain model, persistence, and state transitions.
Should contain:
- session model and persisted metadata
- session storage/loading/snapshot logic
- reducer-like state transitions for session-owned data
- memory extraction hooks that are session-domain concerns
Should not contain:
- socket handling
- TUI state
- provider HTTP details
- direct server daemon lifecycle logic
Notes:
- This crate is not explicitly named in the current compile-performance plan, but the current size and fanout of
src/session.rsmake session extraction a natural stabilizing move. - If introducing
jcode-sessionfeels too early, the same boundary should still be established internally first and extracted later.
Purpose: provider contracts and runtime-facing provider orchestration.
Should contain:
- the
Providertrait once it depends only on lower-layer types - provider routing abstractions
- runtime-facing provider composition
- shared streaming abstractions for provider results
Should not contain:
- provider-specific heavy catalogs and schema helpers that already live well in leaf crates
- server or TUI logic
Notes:
- Existing crates
jcode-provider-core,jcode-provider-metadata,jcode-provider-openrouter, andjcode-provider-geminiremain useful under this layer. - The key migration step is shrinking the
Providertrait's dependency surface so it no longer depends on root-crate-only message/runtime types.
Purpose: agent turn engine and tool orchestration.
Should contain:
- turn-loop engine
- stream handling and response recovery
- tool execution orchestration
- compaction integration
- prompt assembly inputs that are agent-domain concerns
Should not contain:
- server socket lifecycle
- TUI state
- provider-specific leaf implementations
Notes:
- This aligns directly with the refactoring roadmap's "Agent Turn-Loop Unification" phase.
jcode-agent-runtimeremains the low-level runtime primitive crate below it.
Purpose: daemon lifecycle and multi-client coordination.
Should contain:
- socket listeners and debug socket handling
- client attach/detach lifecycle
- swarm coordination
- reload/update server behaviors
- server-owned registries and shared service wiring
Should not contain:
- TUI rendering
- provider implementation details beyond service interfaces
- session persistence internals that belong in
jcode-session
Notes:
- The current
src/server/submodule tree is already the right shape for this extraction. src/server.rsshould continue shrinking into a facade/composition module.
Purpose: client UI state, reducers, and rendering.
Should contain:
- app state and reducers
- remote client behavior and reconnect logic
- renderer/widget orchestration
- TUI-specific command/input handling
Should not contain:
- server daemon code
- session persistence internals
- provider network logic
Notes:
- This aligns directly with the refactoring roadmap's "TUI State/Reducer Split" phase.
jcode-tui-workspacecan remain a leaf crate or become a child dependency ofjcode-tui.
Purpose: self-dev workflows, customization records, reload/build productization.
Should contain:
- self-dev state and tooling policy
- build/reload orchestration specific to self-dev workflows
- customization record and migration logic as it lands
Should not contain:
- generic server lifecycle not specific to self-dev
- general TUI rendering
Notes:
- This aligns with the compile-performance plan's issue-#32 direction and with the already-unified shared-server model.
Purpose: composition root and shipping product package.
Should eventually be responsible for:
- binary entrypoints
- feature/default selection
- wiring the runtime graph together
- packaging and product defaults
It should not remain the long-term home of most implementation logic.
These rules are the core of the RFC.
A higher layer may depend on a lower layer. A lower layer may not depend on a higher layer.
- foundation cannot depend on domain/runtime, interfaces, or product crates
- domain/runtime cannot depend on TUI or self-dev UI/product layers
- leaf adapters must not pull UI or server concerns downward
ratatui,crossterm, renderer state, viewport state, widget models, and clipboard/image/UI helper types must stay out of server, agent, provider, and core crates- server-to-client data crosses the boundary via protocol/event types, not TUI structs
- socket/session attachment state, fanout senders, debug socket helpers, and daemon lifecycle code must not appear in
jcode-core,jcode-provider-core, or provider leaf crates
- provider leaf crates may depend on
jcode-core,jcode-provider, andjcode-provider-core - they must not depend on
jcode-serverorjcode-tui
jcode-core should stay cheap to compile and highly reusable.
Avoid putting these there unless absolutely necessary:
reqwest- provider SDKs
- UI crates
- ONNX/tokenizer stacks
- mail/PDF dependencies
Before extracting a crate, first shrink and stabilize its public surface.
Examples:
- move pure data types before moving stateful runtime code
- move pure helper functions before moving integration shells
- keep facades in the root crate during transitions if they reduce churn
Do not create a dumping-ground crate.
If code has a clear owner, it belongs with that owner:
- protocol/data types ->
jcode-core - session persistence ->
jcode-session - provider route/schema helpers -> provider crates
- rendering helpers ->
jcode-tui
The top-level jcode package can wire multiple domains together. Peer crates should not casually depend on each other sideways when a lower-level contract would do.
A crate split is worth doing when it improves at least one of these substantially, and ideally both:
- clearer ownership and testability
- lower compile invalidation for common edits
During migration, it is acceptable for the root crate to keep temporary facade modules that re-export or forward into extracted crates. That is preferable to risky behavior changes.
This is the recommended direction from the current tree, not a one-shot move list.
| Current area | Likely target |
|---|---|
src/id.rs, protocol/message/config primitives |
jcode-core |
src/session.rs, parts of storage, restart snapshot concerns |
jcode-session |
src/agent/*, parts of compaction, tool orchestration seams |
jcode-agent |
src/server/ + shrinking src/server.rs facade |
jcode-server |
src/provider/mod.rs trait/contracts plus provider composition seams |
jcode-provider |
| existing provider helper crates | remain leaf/provider support crates |
src/tui/* + jcode-tui-workspace |
jcode-tui + leaf workspace widget crate |
src/cli/* |
stay in root initially or become jcode-cli later if justified |
src/tool/selfdev/*, self-dev workflow/productization |
jcode-selfdev |
This migration is intentionally incremental and aligned with existing docs.
Deliverables:
- this RFC
- cross-links from refactoring and compile-performance docs
- dependency rules documented before more splits land
Why now:
- the repo already has enough workspace structure that undocumented drift is becoming more expensive
Aligns with REFACTORING.md phases 2 through 6.
Focus areas:
- continue CLI decomposition until
main()stays parse + runtime bootstrap only - continue shrinking
src/server.rsinto a thin facade oversrc/server/* - unify agent turn-loop variants behind one engine
- continue TUI state/reducer separation
- continue provider state isolation and pure helper extraction
Exit criteria:
- root modules are organized by ownership, not by convenience
- candidate extraction seams are obvious and lower-risk
This is the highest-leverage shared boundary.
First moves should be narrow and stable:
- IDs
- small protocol DTOs
- tool definition and message content forms that are broadly shared
- config enums/primitives that do not need runtime services
Avoid moving unstable orchestration APIs too early.
Exit criteria:
- server, agent, provider, and TUI code can all depend on the same lower-level shared types without depending on the root crate
Primary targets:
jcode-providerjcode-agentjcode-serverjcode-session
Recommended order:
- start with whichever boundary is already most internally modular after Phase 1
- in practice, provider and server look like the strongest current candidates because they already have meaningful submodule trees and leaf support crates
- session may remain internal slightly longer if its public surface is still too entangled
Exit criteria:
- the root crate no longer defines the main provider, server, and agent contracts directly
Focus:
- move client app/reducer/rendering code out of the root crate once protocol and runtime service boundaries are stable
- keep server events and client view-state concerns separated by protocol types
This phase should happen after enough shared contract extraction exists to avoid TUI depending back on root implementation details.
Exit criteria:
- TUI can evolve rapidly without dragging broad server/provider recompilation
Focus:
- isolate self-dev workflow code and future customization/productization work
- keep shared-server runtime behavior intact
- move issue-#32 style no-rebuild customization logic here when it becomes concrete
Exit criteria:
- self-dev product behavior is explicit and no longer scattered across server/CLI/tool glue
Desired end state:
src/main.rsremains thinjcode::run()is mostly wiring- the top-level package primarily assembles runtime services and default product configuration
These should continue throughout the migration:
- keep carving heavy leaf dependencies into workspace crates where boundaries are safe
- measure touched-file compile timings after structural changes
- protect behavior with facades, tests, and refactor verification scripts
- prefer data-driven customization over source edits where issue #32 applies
If we must prioritize, use this order:
- stabilize and extract shared lower-level types
- keep shrinking server/provider/session/agent hotspots internally
- extract runtime contracts and orchestration crates
- extract TUI
- extract self-dev productization
This ordering gives the best overlap between architecture safety and compile-speed payoff.
We should consider this RFC materially implemented when most of the following are true:
- the root package is primarily a composition shell
- shared cross-cutting types live in a lower-level crate rather than the root crate
- server, agent, provider, and TUI have clear ownership boundaries
- provider support crates no longer need root-crate-only types
- TUI depends on protocol/service contracts rather than runtime internals
- common self-dev edits avoid recompiling unrelated heavy subsystems whenever possible
- architecture docs match the actual crate graph
When deciding where new code should go:
- Ask who owns the behavior.
- Ask which layers should be allowed to know about it.
- Ask whether putting it in the root crate will increase invalidation for unrelated edits.
- Prefer the narrowest stable owner that does not create an artificial abstraction.
Short version:
- if it is shared data, push downward
- if it is orchestration, keep it above stable contracts
- if it is UI, keep it out of runtime crates
- if it is heavy and isolated, make it a leaf crate
These do not block the RFC, but they should be revisited as migration proceeds:
- Should
jcode-sessionbecome an explicit crate, or remain an internal boundary until later? - Should CLI remain in the top-level package permanently, or eventually become
jcode-cli? - Should
messageandprotocolremain together injcode-core, or split into separate contract crates if they evolve at different rates? - Should
jcode-tui-workspaceremain a separate leaf crate long-term, or fold intojcode-tuionce the larger TUI extraction lands?
Adopt this RFC as the architectural north star for refactors and crate splits.
In practice that means:
- keep following the current refactoring roadmap
- keep using the compile-performance plan's measured, crate-boundary-first strategy
- treat every new extraction as part of one layered architecture, not as an isolated cleanup