ADR-005: adopt async gradually — sync core, then threads, then tokio
Context
Ranger's fatal flaw is a synchronous loop that stats every file inline (research/ranger-deep-dive).
yazi proves tokio solves it but is a lot of machinery to learn while also learning Rust.
lf (Go goroutines) shows threads alone already keep a UI responsive.
Options considered
Option
Pros
Cons
Gradual: sync → std threads + channels → tokio
learnable steps; working product at every stage
one refactor of the preview pipeline in Sprint 8
tokio from day 1
final architecture sooner
async lifetimes + borrow checker + TUI at once = frustration barrier
threads-only forever
simpler mental model
ecosystem (notify bridges, timeouts, process spawning) is tokio-first
Decision
Sprints 1–5: synchronous core. The TUI loop owns all state; directory loading and file
ops are fast enough synchronously for a first product.
Sprints 6–7: previews move to std::thread + std::sync::mpsc — UI stays responsive,
ownership lessons are explicit.
Sprint 8: adopt tokio for the full preview/media pipeline (yazi architecture:
runtime workers marshal results to the single-threaded UI actor via a channel). Introduce
cancellation via a generation-counter ("ticket") pattern before adopting tokio abort.
Consequences
Positive: each concurrency model is learned in isolation; no big-bang async rewrite of the UI.
Negative: preview pipeline is written twice (thread → task) — accepted as a deliberate
learning repetition (Kaufman: quantity of practice beats premature perfection).
Mitigations: keep the preview pipeline behind a small interface (Previewer trait, ADR-002)
so the swap is local, not global.