- Read
docs/PROGRESS.mdfirst to see the current state (Done / Missing / Now). - Work in small, testable increments. Prefer TDD for pure logic (see below).
- Before committing:
cargo fmt --all,cargo clippy --all-targets -- -D warnings,cargo test. - Update
docs/PROGRESS.mdat the end of your session (the ritual inCLAUDE.md). - Use Conventional Commits (
feat:,fix:,docs:,refactor:,test:,chore:).
engine/andstats/are pure: noratatui/crossterm/I/O, and noInstant::now()— time is passed in aselapsed: Duration. This keeps tests deterministic and Phase 3 ghost-replay simple.- No
crosstermoutsideinput.rsandmain.rs. Keystrokes becomeActions ininput.rs. ui/never mutates state — it's a pure render of&Appinto aFrame.- Index text by grapheme, never by byte.
- Any change to the stack or a core boundary needs a new ADR in
docs/adr/.
- Pure modules (
engine,stats,sources::normalizer) are unit-tested first: write a failing test for the rule, then implement it. Tests are colocated in each module under#[cfg(test)]. tests/holds integration tests: full typing sessions, the import normalizer on real fixtures, and (from Phase 2) migrations from an empty database.- Metrics are tested by injecting
elapsed— never by sleeping. - UI is smoke-tested with ratatui's
TestBackendwhere useful.
- A theme: drop a
*.tomlinassets/themes/(embedded default) or in your config dir (override). - A test preset: edit
[[presets]]inconfig.toml. - A challenge source: implement the
Sourcetrait insrc/sources/.