a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
221 lines
7.5 KiB
Markdown
221 lines
7.5 KiB
Markdown
# Compile-Time Isolation Refactor
|
|
|
|
This is the active migration plan for making full-feature debug/selfdev builds faster without removing features from the developer binary.
|
|
|
|
## Goal
|
|
|
|
Keep the normal debug/selfdev binary production-like, including PDF, embeddings, providers, update/selfdev tooling, and other integrations, while reducing the amount of Rust code that must be recompiled after common edits.
|
|
|
|
The target is not just "more crates". The target is a wider dependency DAG with smaller serial front-end units and cleaner invalidation boundaries.
|
|
|
|
## Current diagnosis
|
|
|
|
The workspace already has many crates, but the critical path is dominated by a small number of large crates stacked linearly:
|
|
|
|
```mermaid
|
|
graph LR
|
|
base["jcode-base\n~100k+ LOC"] --> appcore["jcode-app-core\n~100k LOC"]
|
|
appcore --> tui["jcode-tui\n~100k+ LOC"]
|
|
tui --> rootlib["jcode lib"]
|
|
rootlib --> bin["jcode bin"]
|
|
small["50+ smaller crates"] -. mostly parallel .-> base
|
|
```
|
|
|
|
From the last available Cargo timing report parsed with `scripts/compile_time_probe.sh --skip-build`:
|
|
|
|
- Cargo timing wall: **16.00s**
|
|
- Known jcode serial stack span: **14.72s**
|
|
- Known jcode serial stack summed unit time: **17.36s**
|
|
- Known jcode serial stack frontend time: **11.99s**
|
|
|
|
Slowest units from that timing report:
|
|
|
|
| Unit | Total | Frontend | Codegen |
|
|
|---|---:|---:|---:|
|
|
| `jcode-app-core` | 4.73s | 3.82s | 0.91s |
|
|
| `jcode-base` | 4.34s | 3.63s | 0.71s |
|
|
| `jcode-tui` | 4.18s | 3.14s | 1.04s |
|
|
| `jcode` bin | 2.34s | n/a | n/a |
|
|
| `jcode` lib | 1.77s | 1.40s | 0.37s |
|
|
|
|
This means the main bottleneck is rustc front-end serialization in a few mega-crates, not linker choice or third-party cold compile.
|
|
|
|
## Measurement
|
|
|
|
Use the focused timing probe for each phase:
|
|
|
|
```bash
|
|
scripts/compile_time_probe.sh --json target/compile-time-probe.json
|
|
scripts/compile_time_probe.sh --touch crates/jcode-tui/src/tui/app/input.rs
|
|
scripts/compile_time_probe.sh --touch crates/jcode-app-core/src/server.rs
|
|
scripts/compile_time_probe.sh --touch crates/jcode-base/src/provider/mod.rs
|
|
```
|
|
|
|
For broader repeated measurements, continue using:
|
|
|
|
```bash
|
|
scripts/bench_compile.sh selfdev-jcode --runs 3 --touch <path> --json
|
|
scripts/bench_selfdev_checkpoints.sh --skip-cold --touch <path> --runs 1
|
|
```
|
|
|
|
Track at least:
|
|
|
|
1. Full-feature selfdev build wall time.
|
|
2. Cargo timing wall time.
|
|
3. `jcode-base -> jcode-app-core -> jcode-tui -> jcode lib -> jcode bin` stack span.
|
|
4. Sum of frontend time in the serial stack.
|
|
5. Incremental rebuild after touching representative high-churn files.
|
|
6. Static report drift from `scripts/compile_isolation_report.py`: LOC, inline tests, `async_trait`, and target-state dependency advisories.
|
|
|
|
## Target architecture
|
|
|
|
```mermaid
|
|
graph TD
|
|
bin["jcode binary\ntiny composition root"] --> cli["jcode-cli"]
|
|
bin --> tui["jcode-tui"]
|
|
bin --> server["jcode-server"]
|
|
bin --> providers["provider leaf crates"]
|
|
bin --> tools["tool leaf crates"]
|
|
|
|
cli --> api["jcode-client-api / app-api"]
|
|
tui --> api
|
|
server --> api
|
|
|
|
api --> protocol["protocol + view models"]
|
|
protocol --> types["small stable type crates"]
|
|
|
|
server --> agent["jcode-agent"]
|
|
server --> registry["jcode-tool-registry"]
|
|
server --> auth["jcode-auth-core"]
|
|
server --> session["jcode-session-core"]
|
|
server --> memory["jcode-memory-core"]
|
|
|
|
providers --> provider_core["jcode-provider-core"]
|
|
tools --> tool_core["jcode-tool-core"]
|
|
```
|
|
|
|
Rules:
|
|
|
|
- TUI and CLI depend on client API, protocol, view models, and small type crates, not full server/provider/tool implementations.
|
|
- Provider implementations are leaf crates. AWS/Bedrock dependencies live only in the Bedrock provider crate.
|
|
- Tool implementations are leaf crates. Heavy tools like PDF/browser/Gmail/search are isolated behind tool-core interfaces.
|
|
- Shared bottom crates are small and stable. Avoid putting high-churn behavior in protocol/type crates.
|
|
- Avoid broad `pub use whole_crate::*` compatibility ladders in final architecture.
|
|
|
|
## Migration sequence
|
|
|
|
### Phase 0: measurement and guardrails
|
|
|
|
Status: started.
|
|
|
|
Deliverables:
|
|
|
|
- `scripts/compile_time_probe.sh`
|
|
- `scripts/compile_isolation_report.py`
|
|
- this document
|
|
- dependency boundary checks/advisory reports
|
|
|
|
Success criteria:
|
|
|
|
- Every structural phase has before/after timing.
|
|
- The timing report makes the serial stack visible.
|
|
|
|
### Phase 1: widen the god-crate critical path
|
|
|
|
Split the three long-pole crates into sibling domain crates. Priority is widening the graph, not extracting more tiny type crates.
|
|
|
|
Likely first splits:
|
|
|
|
- From `jcode-base`:
|
|
- `jcode-auth-core`
|
|
- `jcode-session-core`
|
|
- `jcode-memory-core`
|
|
- provider implementation crates, especially Bedrock/AWS as a leaf
|
|
- From `jcode-app-core`:
|
|
- `jcode-server`
|
|
- `jcode-agent`
|
|
- `jcode-tool-registry`
|
|
- service crates for background/swarm/update/selfdev as needed
|
|
- From `jcode-tui`:
|
|
- `jcode-client-api` / view-model boundary first
|
|
- then move reusable client-side state logic out of the terminal rendering crate only when it creates a real parallel unit
|
|
|
|
Success criteria:
|
|
|
|
- Touching common TUI code no longer recompiles app-core/provider/server implementation crates.
|
|
- Touching a provider implementation no longer recompiles TUI or broad base code.
|
|
- Cargo timing shows multiple medium-sized Jcode crates running in parallel instead of one 4-deep mega-crate ladder.
|
|
|
|
### Phase 2: kill glob re-export ladders
|
|
|
|
Current compatibility layering preserves the old monolith shape:
|
|
|
|
```rust
|
|
pub use jcode_base::*;
|
|
pub use jcode_app_core::*;
|
|
pub use jcode_tui::*;
|
|
```
|
|
|
|
Migration approach:
|
|
|
|
1. Keep compatibility re-exports temporarily while moving code.
|
|
2. Convert high-churn modules to explicit imports from leaf crates.
|
|
3. Remove glob re-exports once downstream imports are explicit.
|
|
|
|
Success criteria:
|
|
|
|
- New code does not rely on whole-layer prelude-style re-exports.
|
|
- Dependency direction is visible in imports and Cargo manifests.
|
|
|
|
### Phase 3: move inline tests out of hot crates
|
|
|
|
Problem:
|
|
|
|
- Inline `#[cfg(test)]` modules make `cargo test` compile large production crates plus large test bodies as one rustc unit.
|
|
|
|
Target:
|
|
|
|
- Integration tests or dedicated `*-test-support` crates for broad behavior tests.
|
|
- Keep tiny unit tests inline only when they are genuinely local and cheap.
|
|
|
|
Success criteria:
|
|
|
|
- Targeted tests no longer require monolithic test cfg builds for unrelated domains.
|
|
|
|
### Phase 4: reduce front-end macro tax
|
|
|
|
Targets:
|
|
|
|
- Replace `async_trait` with native `async fn` in traits where the trait is not used as `dyn`.
|
|
- Keep `async_trait` only at object-safe plugin/interface boundaries where boxed futures are intentional.
|
|
- Avoid adding derive-heavy types to broad shared crates unless the type is stable and necessary.
|
|
|
|
Success criteria:
|
|
|
|
- Fewer proc-macro expansions in the hot crates.
|
|
- No object-safety regressions.
|
|
|
|
## Anti-goals
|
|
|
|
- Do not make fast debug builds incomplete by default.
|
|
- Do not split code into tiny crates unless the split creates a real invalidation or parallelism boundary.
|
|
- Do not move high-churn behavior into low-level type/protocol crates.
|
|
- Do not do a single giant rewrite. Each phase should build and be measurable.
|
|
|
|
## Validation checklist per phase
|
|
|
|
Before committing a phase:
|
|
|
|
```bash
|
|
scripts/compile_time_probe.sh --skip-build
|
|
scripts/compile_isolation_report.py
|
|
scripts/check_dependency_boundaries.py
|
|
cargo check --profile selfdev -p jcode --bin jcode
|
|
```
|
|
|
|
For code-moving phases, also run the relevant targeted tests for the moved domain, plus one full selfdev build through the coordinated selfdev path when practical:
|
|
|
|
```bash
|
|
selfdev build target=tui
|
|
```
|