- Order:
core::→std::→ external crates (alphabetical) → internalnativelink-*crates - Group proto imports by module, not alphabetically
- Use
use crate::...for same-crate modules
make_err!(Code::..., "message")for internal errors;make_input_err!("message")for bad inputerror_if!(condition, "message")for early validation returns.err_tip(|| "context")to chain diagnostic context onto Results- Never panic or unwrap in library code; always return
Result<T, Error> - Use
{:?}for debug formatting of upstream errors in messages
use tracing::{debug, error, info, trace, warn};- Structured fields:
%for Display,?for Debug —info!(%key, ?value, "message") info!for state transitions, transfer completions with throughput/durationwarn!for performance anomalies (slow ops, contention, early evictions)trace!for hot-path / repetitive loops; avoid logging inside tight loops- Messages: lowercase, no trailing period, describe why not what
#[async_trait]on trait definitions and implsPin<&Self>forStoreDrivertrait methodsspawn_blocking()for CPU-bound or sync filesystem work; avoid async recursiontokio::join!for fixed concurrent work;FuturesUnorderedfor variable-countparking_lot::Mutexfor sync contexts; never hold locks across.await
#[serde(deny_unknown_fields)]on config structs#[serde(default)]or#[serde(default = "fn_name")]for optional fields#[serde(deserialize_with = "convert_string_with_shellexpand")]for paths#[serde(rename_all = "snake_case")]on enums
#[derive(MetricsComponent)]on public structs#[metric(help = "...")]on fields;#[metric(group = "...")]for nesting
- Functions:
snake_case; types:PascalCase; constants:UPPER_SNAKE_CASE - ~100 char soft line limit; readability over rigid length
- Blank line between logical sections; single blank line between items
Cow<'_, T>in hot paths to avoid allocation
///doc comments on public items explain why and show examples//inline comments only for non-obvious logic or workaroundsTODO(...)with issue number when possible for known issues
#[cfg(feature = "...")]at definition site#[cfg(target_os = "...")]for OS-specific code (Linux vs macOS)
- Test-first development: when implementing any new feature, write tests first (unit, integration, and cross-component interaction tests). Verify they fail before implementing the feature, then make them pass. Include fakes/mocks for hardware-interaction tests where needed.
- Bug fixes require a failing test first: when fixing a bug, write a test that reproduces the failure, verify it fails, then implement the fix and show the test passes. Never fix a bug without a regression test.
- Integration tests in
tests/directory; minimal inline#[cfg(test)]modules - Use
nativelink-macrotest harness (#[nativelink_test])
- Chesterton's Fence: before modifying or removing any behavior, always check
git log,git blame, andgit log -Sto understand why the code exists. If a commit message or comment explains the reason, evaluate whether that reason still applies before making the change.
- Before committing any change, send the changes to a code review agent and a performance review agent. Work to obtain their sign-off before committing. Fix any issues they identify. Only commit after both reviews pass with no blocking issues.
- Journal all git operations: append every
git commit,git push,git revert,git stash, and any other state-changing git command to.claude/git-journal.mdin the working directory. Each entry should include the timestamp, command, and a one-line description. This prevents losing track of what was done across context compressions.
- Always verify
pwdbefore git operations. Agent worktrees (.claude/worktrees/) have separate git branches. Commits in a worktree do NOT go tomain. The Bash tool may silentlycdinto a worktree after an agent runs. Alwayscd /path/to/nativelinkbefore anygit commit,git push, orgit status. - Never use
git stash pop— it can cause merge conflicts thatgit checkout --resolves by reverting uncommitted edits. Usegit stash apply+git stash dropseparately. - Commit early, commit often. After each logical change compiles, commit immediately. Don't accumulate multiple uncommitted changes across a session — context compression or worktree confusion can lose them.
- After editing files, verify with
git diff --stat HEADthat the expected changes appear before moving on to the next task.