This document defines how work on hum is done: the Rust toolchain policy,
workspace conventions, dependency discipline, local validation, debugging and
logging workflow, the relationship between documentation, issues, and code, the
architecture-change process, the unsafe-code policy, Git conventions, and the
Definition of Done for both contributors and coding agents.
hum is a native Rust modular monolith with a Cargo workspace. The internal
crates are hum-core, hum-audio, hum-library, hum-config, hum-ui, and
hum-platform, plus a root executable / composition root. The workspace,
Cargo.lock, and a pinned toolchain now exist (Milestone 0); the subsystems
themselves are still mostly unimplemented, and there is no CI yet. This document
describes the intended workflow and uses "will", "must", and "once code exists"
deliberately for the parts not yet built.
It owns engineering workflow. Subsystem specifications live in their own documents (architecture.md, audio-engine.md, testing.md, distribution.md, roadmap.md, and the rest) and are not duplicated here.
Planning documents describe the intended system. They do not grant permission to implement work outside the current issue.
A contributor or coding agent may read the roadmap, the architecture, and future subsystem documents without treating every planned capability as current implementation scope. Documentation answers how should this system work? The active issue answers what should be implemented now? These are never confused.
docs/ project specification — the intended system
▼
GitHub issues scoped implementation work
▼
source code implementation
Documentation describes the intended system. Issues describe the work required to make that system real. Code must conform to accepted documentation unless the task explicitly changes the architecture or specification.
Accepted documentation is authoritative. Code must not silently contradict it. When an implementation would change documented behavior, the owning document is updated in the same change (see Documentation changes).
The active GitHub issue or task defines what is implemented now. Adjacent roadmap
items are not implemented merely because they look easy. For example, if the
issue is "add the Symphonia decoder", that issue does not authorize also adding
CPAL output, the queue, the TUI, or a config redesign. This rule is restated
operationally in a future AGENTS.md.
A contributor working an issue inspects, roughly in order: the issue itself; the directly relevant subsystem document(s); architecture.md; related cross-subsystem documents; existing implementation and tests; and recent relevant Git history where useful. The rule is read enough accepted specification to understand the boundary being changed — not every file for every trivial change.
If an issue appears to contradict accepted documentation, do not silently pick one. Determine whether the issue intentionally changes the design, the issue is stale, the documentation is stale, or the scope is ambiguous. For architectural contradictions, stop and surface the discrepancy before inventing a new direction. Documented invariants are not silently overridden.
Rust is the canonical implementation language. Another application language is
not introduced because one subsystem seems easier in JavaScript, TypeScript,
Python, Go, C++, or Java. Native platform bindings and dependencies may
internally contain other languages, but hum's application implementation stays
Rust. A language change would be a major architecture decision.
Stable Rust is the default. Nightly Rust is not required for ordinary development unless a specific, documented technical requirement justifies it — in which case the reason is documented, the dependency is isolated, and the portability/tooling cost is weighed against the feature. Nightly is not used because it is interesting.
The workspace uses Rust edition 2024, selected at workspace initialization as the current stable edition with verified toolchain support. This is workspace policy; a change of edition is a deliberate decision.
The project uses one explicit Rust toolchain policy, pinned in the root
rust-toolchain.toml to a specific stable channel with the rustfmt and
clippy components. The frozen principle: developer, CI, and release
validation agree on the intended Rust toolchain, and local environments do not
silently use incompatible compiler generations. Raising the pinned version is a
deliberate change, kept consistent across rust-toolchain.toml, any CI, and this
document.
The canonical implementation structure is a Cargo workspace:
hum/
├── Cargo.toml
├── Cargo.lock
├── src/
│ └── main.rs
└── crates/
├── hum-core/
├── hum-audio/
├── hum-library/
├── hum-config/
├── hum-ui/
└── hum-platform/
This is planned. It is not initialized in this task. The physical structure may be refined if implementation evidence requires, but the architectural crate boundaries from architecture.md remain authoritative.
Per distribution.md, hum is an application. Once Cargo
exists, Cargo.lock is committed and is not .gitignored. Dependency updates
are deliberate and reviewed.
A dependency is added intentionally. Before adding a crate: What capability does
it provide? Is it already provided by an accepted dependency? Does it belong in
this crate? Does it leak infrastructure into hum-core? What runtime, build, and
platform implications does it introduce? Is it maintained enough for the
responsibility? What license does it use? Is it necessary now for the current
issue? Dependencies are not added for hypothetical future features.
The accepted stack (versions not claimed):
| Concern | Crate |
|---|---|
| TUI rendering | Ratatui |
| Terminal I/O | Crossterm |
| Audio output | CPAL |
| Audio decoding | Symphonia |
| Resampling | Rubato |
| Real-time PCM SPSC buffer | rtrb |
| Ordinary control channels | crossbeam-channel |
| Metadata | Lofty |
| Local persistence | SQLite + rusqlite |
| Image decode/transform | image |
| Terminal image integration | ratatui-image |
| Filesystem watching | notify |
| Serialization | Serde |
| CLI parsing | clap |
| Structured logging | tracing |
| Library/domain errors | thiserror |
| Composition root / CLI errors | anyhow (where appropriate) |
Alternative libraries duplicating these responsibilities are not added unless an accepted architecture change replaces them.
Dependencies live only where their responsibility exists, following the dependency direction in architecture.md:
hum-coremust not depend on CPAL, Ratatui, rusqlite, Lofty, image,notify, Tokio, or any other infrastructure crate. Before importing an external crate into core, confirm it is a domain/value-type utility, not infrastructure disguised as convenience. Core abstractions stay small and stable.hum-uimay depend on TUI-specific libraries;hum-audioon audio processing/output libraries;hum-libraryon rusqlite/Lofty/notify;hum-configon parsing and file-watching libraries;hum-platformon path-resolution and platform libraries.- Internal crate dependencies stay acyclic. If two crates appear to need each other, reconsider ownership, a shared abstraction, or a command/event boundary — not merging responsibilities into core.
Cross-crate types live where their semantics naturally belong (core domain IDs in
hum-core, audio-device types in hum-audio, terminal widget state in
hum-ui). Boundary conversion is preferable to moving infrastructure-specific
types into core.
Once Cargo exists, common dependency versions may be managed through workspace dependency declarations where that reduces drift. A dependency is not centralized merely because Cargo permits it when only one crate uses it. The goal is coherent management, not configuration aesthetics.
Unrelated dependencies are not updated during a scoped feature or fix unless required:
dependency update ─▶ review diff / release notes ─▶ run relevant validation ─▶ commit intentionally
A broad cargo update is not run as incidental cleanup, and lockfile churn is
kept out of unrelated issues.
Per distribution.md, dependency-license compatibility is verified before a public release. During development, a dependency whose license would conflict with the eventual project license/distribution model is not knowingly introduced. Because the project license is currently unresolved (see License status), licensing relevance is recorded without inventing compatibility conclusions.
rustfmt is the canonical formatter. Once code exists, formatting validation is
conceptually cargo fmt --all -- --check (or the workspace equivalent). Code is
not hand-formatted around rustfmt for preference. This command does not run
yet because Cargo does not exist.
Clippy is the canonical Rust lint tool. A future baseline is conceptually
cargo clippy --workspace --all-targets --all-features -- -D warnings (or an
appropriate equivalent; --all-features is not frozen if future
mutually-exclusive features make it invalid). Frozen: Clippy is required,
warnings are treated seriously, and knowingly introduced warnings are not
accepted casually. A large deny-list of stylistic lints is not created before
code exists.
New code does not knowingly introduce compiler warnings. "Warning ignored because
tests pass" is not normal practice. If a warning must be allowed, the allow is
scoped narrowly and documented when non-obvious; broad warning classes are not
disabled globally for convenience.
Per architecture.md: thiserror for library/domain-specific
error types; anyhow only at the composition root / CLI where broad contextual
errors are appropriate. anyhow::Error is not propagated through core subsystem
APIs by default. Errors preserve useful semantic categories. Expected filesystem,
config, decode, device, database, and malformed-input failures are not handled
through panic.
Panics represent programming defects and invariant violations, not ordinary
runtime errors. unwrap() and expect() are not used on user- or
environment-controlled operations for convenience. expect may be appropriate
when a genuine internal invariant has already been proven and the message
explains it. Tests may use unwrap/expect where failure should abort the test
and the context is obvious; production code deserves stricter scrutiny, but tests
are not contorted into verbose error plumbing to eliminate every unwrap.
unsafe is not prohibited, but it is exceptional. Before adding it: show why
safe Rust is insufficient; keep the block/module narrow; document the safety
invariants; test the surrounding behavior; and do not expose unsafe requirements
to unrelated code. Audio real-time performance is not automatically sufficient
justification — safe libraries are used first. Every unsafe block is reviewable
in isolation; giant unsafe regions are avoided; SAFETY: comments explain
non-obvious invariants without merely restating the code. If unsafe is
introduced, tools such as Miri may be used where applicable (see
testing.md).
Internal crate boundaries have intentional APIs. A type or function is not made
pub merely because another crate might someday need it; the narrowest practical
visibility is used. Large shared utility crates are not allowed to become
dependency magnets.
Per architecture.md, commands mean something should happen and events mean something has happened. A generic enterprise event bus is not created. Direct calls are allowed inside clean boundaries; channels are used where concurrency requires them; not every function call becomes asynchronous messaging.
- Bounded
crossbeam-channelchannels for ordinary control where appropriate;rtrbonly for the real-time PCM SPSC boundary. Unbounded queues are not used for high-frequency data;crossbeamis not the PCM callback buffer;rtrbis not a generic app event bus. - Tokio is not introduced as the default runtime. The architecture prefers OS threads, normal channels, and real-time-safe buffers. Tokio may fit later inside a genuinely network-heavy adapter if a future feature explicitly requires it — not for local SQLite, filesystem scans, config reload, or ordinary TUI work.
- One OS thread is not spawned per music file, artwork request, or watcher event. Concurrency stays bounded with workload-aware pools/workers consistent with subsystem docs; exact worker counts are implementation decisions.
- Blocking work (SQLite, metadata parsing, image decode, filesystem scanning) belongs off the TUI/render loop and off the real-time callback. The UI receives prepared state and results. Blocking work is not hidden behind an innocent-looking function called during draw.
The CPAL callback is a protected boundary. Any change touching it is reviewed against audio-engine.md. Prohibited steady-state callback behavior: filesystem I/O, SQLite, metadata parsing, image work, terminal work, decoding, resampling, logging, blocking, contended locks, unbounded allocation. These rules are not "temporarily" violated for convenience.
The real-time callback is not debugged with ordinary logging inside it. Use real-time-safe counters, atomics, and bounded telemetry, observed and logged from non-real-time code.
Conditional compilation stays near platform/integration boundaries. Before adding
#[cfg(target_os = "...")] inside a non-platform crate, ask whether the behavior
belongs in hum-platform, a dependency adapter, or a platform-specific module.
Domain semantics do not differ by OS without explicit architecture approval.
Platform-specific behavior is validated on the actual OS before it is declared
supported; cross-compilation is not native validation (see
platform-support.md).
- Cargo feature flags stay sparse and justified — genuinely optional integration
or a build-time capability with clear product meaning. They are not used to
hide broken code, to select OS behavior
cfgalready handles, or to maintain many untested combinations. Every supported combination is test surface. build.rsis avoided unless there is a concrete need (build metadata, native compilation). It is not added just to generate constants ordinary Cargo metadata can provide.- Code generation is not introduced without clear value. Generated files have explicit ownership (generated at build time, or generated and committed, never hand-edited). Opaque generation pipelines are not created for simple domain types.
The simplest architecture consistent with the documented future boundaries is implemented. A plugin system, networking, daemon mode, a generic event bus, an enormous abstraction layer, unused traits, or a generic persistence framework are not added "for later" without a current requirement. Both short-term hacks that violate architecture and speculative enterprise architecture are avoided.
An abstraction is introduced when it protects a real boundary — the physical audio sink seam, platform paths, application command interfaces, the terminal backend/test seam. It is not introduced around every struct, as a repository trait over logic that never needs substitution, or as a wrapper over standard library APIs with no platform/test reason. Ports and adapters are tools, not ceremony.
Testing policy is owned by testing.md; this section covers the developer workflow.
For ordinary Rust changes, the eventual baseline is at least format, lint, and tests:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
These do not run yet — Cargo does not exist. The normal test suite does not
require real audio hardware. cargo test is the valid baseline runner;
cargo nextest is not mandated.
Validation is proportional to the affected boundary, not "run everything for every change":
| Change | Validation |
|---|---|
hum-core |
core tests, workspace tests, lint/format |
| layout | layout tests / property tests, TUI tests, workspace tests |
| audio pipeline | audio fixture/state/buffering tests, workspace tests, relevant manual/device validation when behavior requires it |
| docs-only | link checks, configured Markdown checks, cross-document consistency review |
The issue/task states any additional validation. Expensive hardware/manual tests are not run for every tiny change.
Per testing.md: a confirmed bug receives a regression test where practical, reproduced before the fix when practical, at the smallest meaningful boundary. Speculative patches whose effect cannot be demonstrated are avoided.
New binary fixtures require known expected properties, legal redistribution
rights, small size, and a clear purpose. Commercial music and artwork are not
used casually. A large fixture is not added just because a user's local file
reproduced a bug — a safe fixture is reduced or generated where practical.
Repository test fixtures are kept separate from developer local music and from
hum runtime database/cache; runtime-generated state is never committed as a
fixture.
tracing is used for structured diagnostics. Logs carry useful context —
fields such as track_id, root_id, path, device, operation,
error_category where appropriate. Logs do not contain PCM buffers, artwork
bytes, whole-library dumps, or secrets. Missing artwork and expected empty
states do not flood logs.
Log levels are used deliberately: trace for very detailed development
diagnostics, debug for useful subsystem diagnostics, info for meaningful
lifecycle state, warn for a recoverable unexpected condition, error for an
operation/subsystem failure. error is not used for expected normal states such
as no artwork, an empty queue, or a missing optional config file. Exact messages
are not frozen.
Developers primarily use normal debug builds for iteration, but performance and real-time behavior are also validated in release-like builds where optimization materially changes behavior — audio performance is not concluded from debug mode alone. Release builds are not required for every unit test.
Temporary development instrumentation (verbose per-frame logs, PCM dumps, giant trace files, debug overlays) must not remain enabled in normal release behavior. If a permanent diagnostic is useful, it is designed intentionally with bounded output.
println! / eprintln! debugging is not left in the active full-screen TUI
path — it corrupts terminal rendering. Use tracing / log files or an explicit
debug view. CLI and pre-TUI paths may legitimately use stdout/stderr.
Per filesystem.md:
- Automated tests and scripts never use the developer's real music library,
real
HOME, real XDG directories, or an existinghumdatabase as a target. They use isolated temporary roots. - Development experiments may explicitly point
humat local music manually, but temp artifacts are never created inside library roots. - Parser and reload changes are tested against fixture config, an alternate config path once supported, or a temporary config directory — never by editing the developer's canonical real config. A parser bug is reproducible without damaging dotfiles.
- Running
humlocally may create normal app data under platform paths. Developers should have a way to use isolated development/test paths where needed; the exact mechanism (a CLI flag, an env override, a test mode) is not frozen here. Wiping real user state is not required for development. - The developer's machine configuration is not the product specification.
/home/<name>,/Users/<name>, personal Music directories, a specific audio device, a terminal name, a distro, or a username are never hardcoded — use platform/config/test abstractions.
- Migrations (once persistence exists, per library.md): every
schema change uses a migration; migration files are immutable after a public
release except to correct a never-released state; upgrade paths receive tests;
non-rebuildable user data is preserved. The user's real database is not
manually edited for development tests, and
DROP DATABASE-style reset workflows are not run against user data — temporary DBs and controlled migration fixtures are used. - Configuration (once public syntax exists, per configuration.md): breaking changes are deliberate, docs are updated, release notes include migration guidance, deprecated fields may bridge transitions, and config is never silently rewritten. Before a public release, syntax may evolve faster, but accepted docs stay aligned.
Measure before optimizing. Complex caches, unsafe code, lock-free structures,
parallelism, and micro-optimizations are not added without a measured reason
(the real-time PCM path is an already-justified exception). Performance goals are
tied to actual user behavior — uninterrupted playback, a responsive TUI, bounded
memory, reasonable scans — with no fabricated benchmark numbers.
When a performance problem appears, profile the correct build and workload. A performance change identifies the measured problem, the changed behavior, and the validation. Code unrelated to a real bottleneck is not micro-optimized. Tools are not frozen. Benchmarks may be added after meaningful implementation exists (never against placeholder code), use representative workloads, report context, and do not treat one machine's result as universal. Large generated profiling artifacts (flamegraphs, perf recordings, heap dumps) are not committed by default.
Before modifying code: inspect git status, understand existing changes, and
avoid overwriting unrelated work. If the working tree already contains unrelated
modifications, leave them alone, stage only issue files, and mention interference
if it prevents safe completion. Destructive cleanup — git reset --hard,
git clean -fd — is never run without explicit permission. git add . is not
used blindly when unrelated working-tree changes may exist.
- All commit messages are in English and follow Conventional Commits.
- Types:
feat,fix,test,docs,refactor,perf,build,ci,chore. Scopes are used where they improve clarity (feat(audio):,fix(library):), not forced for every repository-wide change. - Subjects are concise, imperative, lowercase where natural, no trailing period, preferably under 72 characters. "Updated stuff", "final fixes", and "misc changes" are not acceptable.
- Commit messages contain no mention of AI, Claude, Codex, prompts, or generated code, and no AI co-author attribution.
- A commit represents one coherent purpose. Feature implementation, dependency upgrades, architecture refactors, repository-wide formatting, and unrelated cleanup are not combined in one commit. Multiple coherent commits for one issue are fine; meaningful history is not artificially squashed.
- A commit is never known to fail required tests, fail compilation, fail formatting, violate documented architecture, or leave an issue intentionally half-implemented without explicit task design. This matters especially for agent-created commits.
Example subjects:
chore: initialize cargo workspace
feat(audio): add symphonia decoder
feat(library): add sqlite track index
test(audio): add decoder fixtures
fix(artwork): handle missing embedded covers
docs: define project roadmap
Existing shared history is not rewritten casually. A coding agent does not amend, squash, or rebase unless the task explicitly requests it; new work creates new commits, which matters when an issue is implemented incrementally. Work is not pushed unless explicitly requested — local completion and commit are distinct from publication. A future issue workflow may explicitly request pushes/PRs.
A complex branching model is not invented before it is needed. Normal development can use short-lived task branches; Git Flow is not required. Branch naming is decided by the workflow system later and is not frozen here.
When pull requests are used, they explain the problem, the approach, important architecture decisions, validation, and known limitations. Massive raw logs are not pasted. A PR is not required for every local exploratory commit before the collaboration workflow is established.
If an implementation changes documented behavior, the owning document is updated
in the same issue/change — code says A, docs say B is never knowingly merged.
If behavior is purely an implementation detail that stays within the spec, a docs
change may not be needed.
The document that owns the concept is updated (audio callback semantics → audio-engine.md; filesystem path policy → filesystem.md; keybinding context → keybindings.md). The same full specification is not duplicated across documents — links are used.
All project technical documentation is in English: README, docs, issue technical descriptions where practical, commit messages, code comments, and user-facing config/reference names. Portuguese is not mixed into canonical technical docs unless an explicit localization feature is being implemented.
Comments explain why, invariants, non-obvious constraints, unsafe
justification, real-time requirements, and external quirks — not obvious syntax.
Rustdoc documents important contracts (invariants, ownership, threading, callback
restrictions, error semantics) where useful, with effort following architectural
significance rather than covering every private helper. A TODO references an
issue, an explicit deferred decision, or a short explanation; unrelated roadmap
work is not implemented just to remove a TODO, and large planned work is
tracked as GitHub issues.
An implementation task may reveal that accepted architecture is inadequate. It is not worked around silently:
implementation evidence
▼
identify the documented assumption
▼
propose an architecture / documentation change
▼
review / accept
▼
implement
Documentation is updated when a change affects subsystem responsibility, an invariant, a technology choice, persistence semantics, config semantics, a concurrency boundary, support policy, or user-visible stable behavior. A local helper refactor does not require documenting.
A normal implementation issue is complete when the applicable items are true:
- the issue scope is implemented;
- no unrelated roadmap work was added;
- architecture boundaries are preserved;
- relevant errors, empty states, and invalid input are handled;
- tests are added/updated at the smallest appropriate boundary;
- a confirmed bug has regression coverage where practical;
- relevant existing tests pass;
- formatting passes;
- Clippy / static validation passes as applicable;
- documentation is updated if documented behavior changed;
- no secrets or user-local artifacts are included;
- the complete diff has been reviewed;
- only intended files are staged;
- commit(s) follow repository conventions;
- unresolved scope questions are reported rather than invented;
- nothing is pushed unless requested.
A physical hardware test is not required when the issue does not affect hardware behavior.
A documentation-only task is complete when:
- the target document is correctly updated;
- canonical names and links are valid;
- no unrelated docs were expanded;
- cross-document contradictions were checked;
- configured Markdown checks were run if available;
- the diff was inspected;
- the correct Conventional Commit was created when requested.
Irrelevant Rust validation is not run before the workspace exists.
A reviewer asks: is the scope correct? Is the architecture preserved? Do tests prove the behavior? Are errors handled? Is the real-time boundary respected? Are filesystem and user data safe? Are platform assumptions justified? Are docs aligned? Is the diff focused? Review is about architecture and behavior, not only style.
- Security scrutiny for changes touching config parsing, filesystem path
construction, image/media decoding boundaries,
unsafeRust, release credentials, process execution, or future network code.hum's local-first nature reduces network attack surface but does not make local files trusted. - Performance scrutiny for changes touching the CPAL callback, the decoder/render pipeline, library scanning, artwork decoding/cache, the TUI redraw loop, large-library search, or layout hot paths. Not every string helper is benchmarked.
- Platform validation for changes affecting application paths, signals, the
terminal lifecycle, terminal protocol detection, keyboard normalization, CPAL
integration, or
notifybackend assumptions. A Linux-local fix is not assumed to work identically on macOS.
Developer setup is kept small. Once code exists, the expected baseline is Git, a
stable Rust toolchain, the platform build dependencies required by the chosen
Rust crates, a usable terminal, and an optional audio device for manual audio
testing. Docker, Node.js, PostgreSQL, Redis, and a JVM are not required for
ordinary hum development unless a future explicit tool requires one.
Docker is not part of the canonical runtime or ordinary development requirement; it may later help reproduce a build environment or CI but is not inherently useful for audio-device or terminal-protocol testing.
The project requires no specific editor or IDE; instructions work from the shell. VS Code, IntelliJ, and Neovim are not required. Editor configuration files may be added later only if they encode project-wide formatting/tooling rather than personal preference.
Local Git hooks are not required initially — they are local, bypassable, and another setup step. Canonical correctness comes from documented commands and CI, not from assuming a hook ran. A future optional hook may be provided.
CI will automate portions of the same documented local validation; a CI-only model where contributors cannot run checks locally is not created. The CI provider and matrix are intentionally deferred (see platform-support.md and testing.md). Development work prepares a repository state that distribution can release; contributors do not manually create release artifacts during normal feature work unless the issue is specifically about distribution (see distribution.md).
The repository's LICENSE file is empty and no software license has been
chosen. Per distribution.md, this is not changed here, and no
public release occurs while it is unresolved. Development may proceed before
license selection, but contribution and public-release policy may need revisiting
once outside contributors are invited. Contributor licensing terms are not
invented.
A detailed external-contributor governance model is not built yet; there is no
CONTRIBUTING.md. If external contribution volume grows, contribution guidance
can be introduced explicitly later. For now, this document provides the
engineering workflow.
AGENTS.md will later turn this policy into concise operational rules for coding
agents, referencing the docs and extracting hard rules. CLAUDE.md will later
hold Claude-specific working instructions. Neither is populated now. This
document is the human-readable engineering specification and stays
tool/agent-neutral; the entire future AGENTS.md is not duplicated here.
These hold unless changed through the architecture-change process.
- Rust is the canonical application implementation language.
- Stable Rust is the default toolchain direction.
- The project uses a Cargo workspace once implementation begins.
Cargo.lockis committed for the application.rustfmtis the canonical formatter.- Clippy is the canonical Rust lint tool.
- New code does not knowingly introduce compiler warnings.
- Tests exercise the smallest meaningful boundary.
- Normal automated tests do not require real hardware.
- Documentation is the project specification.
- GitHub issues/tasks define implementation scope.
- Planning documents do not authorize implementation outside the active issue.
- Code must not silently contradict accepted documentation.
- Architecture changes are proposed explicitly rather than hidden in implementation.
hum-coreremains infrastructure-independent.- Dependency direction remains acyclic and follows architecture.
- Dependencies are added only for current, justified capabilities.
- Dependency updates are deliberate, not incidental lockfile churn.
- Tokio is not introduced as the default application runtime.
rtrbremains the real-time PCM boundary, not a generic event bus.- Bounded
crossbeamchannels remain the ordinary control direction where appropriate. - The CPAL callback never gains blocking or infrastructure work.
unsafeRust is exceptional, narrow, justified, and documented.- Expected runtime errors are not handled through panic.
anyhowdoes not become the default error type across library boundaries.- Structured logging uses
tracing. - TUI code does not leave
stdout/stderrdebug prints in active rendering paths. - Automated tests never destructively use real user music/config/data.
- Database schema changes use migrations once persistence exists.
- User configuration is not silently rewritten.
- Platform conditionals stay near platform/integration boundaries.
- Cross-compilation is not treated as native platform validation.
- Feature flags remain sparse and justified.
- Build scripts and code generation require a concrete need.
- Public visibility is kept as narrow as practical.
- Current issue scope is not expanded with unrelated roadmap work.
- Working-tree changes unrelated to the issue are not discarded or staged.
- Destructive Git cleanup is never used without explicit permission.
- Commits use English Conventional Commits.
- Commit messages contain no AI attribution.
- Existing commits are not amended, rebased, or squashed unless explicitly requested.
- Work is not pushed unless explicitly requested.
- Relevant validation runs before a commit.
- Known failing required validation is not committed as finished work.
- Bug fixes receive regression tests where practical.
- Documentation changes accompany changes to documented behavior.
- User-local paths, devices, and platform assumptions are never hardcoded.
- Secrets and release credentials are never committed.
- Generated build/runtime artifacts are not source.
- A normal implementation is complete only when its applicable Definition of Done is satisfied.
Deliberately deferred, pending implementation evidence:
- the exact workspace physical organization if implementation reveals a better canonical shape;
- the exact Clippy command and any
allowpolicy once features exist; - the exact CI validation command set;
- the developer setup package names per platform;
- the branch naming convention;
- the PR template/process;
- optional pre-commit hooks;
- benchmark and profiling tooling;
- the isolated dev-data/config override mechanism;
- the external contribution workflow;
- whether
CONTRIBUTING.mdis introduced later.
These are deferred decisions, not gaps in the workflow.
- vision.md — the product identity the work serves
- architecture.md — crate boundaries, dependency direction, the command/event model, and the architecture-change process
- audio-engine.md — the real-time callback rules
- library.md — migration discipline and non-rebuildable data
- artwork.md — the artwork subsystem boundaries
- tui.md — TUI rendering and terminal lifecycle
- layout-system.md — the layout resolver's contract
- configuration.md — configuration evolution discipline
- keybindings.md — the input-command model
- filesystem.md — filesystem safety during development
- platform-support.md — what native validation requires
- testing.md — the full testing strategy
- distribution.md — release policy and the
Cargo.lock/ license gates - roadmap.md — planning context only, not implementation authorization