Architecture Decision Records

ADR-001: Single-crate vs Cargo workspace

Why jackin❯ started as a single crate and when it transitioned to a Cargo workspace.

Status: Accepted Current state: Cargo workspace — 24 member crates under crates/, root jackin binary crate at crates/jackin/. Total ~170 K lines of Rust. Date: 2026-05-30 Deciders: Operator + agent

Context

Rust projects can live in one crate (src/ only) or in a Cargo workspace (workspace.members = [...]). The initial choice was to stay single-crate and delay workspace adoption until the cost was justified. The threshold was set at 150 K lines of Rust source.

Reference projects that stay single-crate at comparable scale: starship (~120 K LOC at the time of this decision), fd-find. Neither uses a workspace despite large codebases.

Decision

Phase 1 — stay single-crate while total Rust LOC < 150 K. All code lived in one crate. No crates/ directory. Fast cargo build, minimal Cargo.toml overhead, no intra-workspace dependency graph to maintain.

Trigger for workspace adoption: either (a) total Rust LOC exceeds 150 K, or (b) a type from the core codebase is needed by external tooling (an jackin-core use case). Whichever comes first.

Phase 2 — workspace adopted (current state). The codebase crossed ~170 K lines as surface-specific and subsystem crates were extracted. Product-neutral TUI components later moved to exact-revision TermRock; the jackin binary crate at crates/jackin/ remains the primary binary.

Consequences

  • Incremental builds are faster per-crate: changing jackin-capsule does not rebuild unrelated product crates.
  • The root jackin crate is still the entry point for cargo build --bin jackin; operators and CI do not change their build commands.
  • Cross-crate pub boundaries enforce the TUI architecture boundary rule: jackin-console cannot accidentally call jackin-capsule internals.
  • New code that serves only one surface starts in that surface's crate; neutral TUI code shared across consumers belongs in TermRock, while product protocol code belongs in jackin-protocol.

On this page