Above all: consolidate key understandings and new findings as inline Rust documentation
(//! module docs and /// item docs) close to the relevant code.
Inline Rust documentation is the canonical documentation source for this repository.
Actively write documentation for the program. All written documentation must be concise, clear, and accurate. No emojis unless strictly necessary. All documentation should be written in English unless explicitly stated.
- Prefer inline Rust documentation in
src/**/*.rs. - Keep design rationale near the owning modules and types.
- Do not rely on a standalone
docs/tree as the canonical source.
Prefer declaration instead of manual implementation. For example,
- Utilize
thiserrorcrate for error messages instead of manual implementations. - Encode invariants into Rust's type system, so that they are enforced by the compiler.
- Write documentation about invariants per struct, field, function, and method.
- Use "constructor" or "builder" pattern for creating instances that satisfy the invariants.
- Prefer
serdefor serialization and deserialization instead of manual parsing and pretty printing. - Prefer derive-style
clapfor command-line argument parsing.
Always prefer typed data structures over strings + parsers, and Never be afraid of defining too many types. For examples,
- Include specific types of errors when creating an error type, not just strings.
- User input should be parsed to be structured data as soon as possible.
- Never use strings to represent states in the software's state machine.
- Never pass strings between internal components when the message could be typed.
- Whenever a hashmap of strings is created, think twice. Is it really relying on string deduplication? Or it's actually a "dynamic object", that might be concluded by a few traits?
Prefer to use structs to pack a group of useful functions; prefer methods over functions. Rust structs have better namespace-ish features than Rust modules. Never write plain functions that are not wrapped in a struct with your best effort unless there's no way around otherwise. When wrapping the functions, abide by the following rules:
- Mention
selfin the signature if the methods are built around the struct type.- Take ownership (
self) if being the elimination form of the struct type, namely consuming the struct. - Take reference (
&selfor&mut self) if the struct only needs to be borrowed.
- Take ownership (
- Use associated functions (similar to static methods) when the struct is purely a namespace;
specifically, write
fn newfor "constructors" with no perspective, andfn with_*for "constructors" that hints how the struct is created.
For builder patterns, pick receivers based on whether the finalizer must move owned fields out. If build/finish consumes,
- Use
fn build(self) -> Tfor the builder. - Make all setter methods take and return self
fn with_*(mut self, ...) -> Selffor easy chaining. If build can borrow, - Prefer setters
fn set_*(&mut self, ...) -> &mut Self, and - Prefer a finalizer
fn build(&self) -> Tso the builder can be reused. Expose an associated entry pointfn new(required, ...), and usewith_*/set_*names consistently for optional configuration.
Add concise yet critical documentation for structs, fields, and methods. Ensure that documentation is clear, concise, and accurate. No emojis unless strictly necessary.
When adding new features, record and observe details with the tracing crate.
This project uses jujutsu, which is compatible to git.
Format: prefix: lowercase description
No capitalization after the colon. No trailing period. One line. The description should say what changed, not why (the diff shows what; the description names it).
| Prefix | When to use |
|---|---|
feat |
A user-visible capability that did not exist before. |
incr |
Incremental progress on an existing feature: bug fixes, polish, tuning, small additions. |
sisy |
Mechanical changes: formatting, linting, renaming passes, internal restructuring with no behavior change. |
vibe |
Exploratory, prototype-quality work. Expect rough edges; may be revised or replaced. |
repo |
Repository housekeeping: migrations, dependency changes, formatter config, file reorganization, one-off maintenance. |
docs |
Documentation-only changes (AGENTS.md, README, inline Rust docs/comments). |
test |
Adding or updating tests without changing production code. |
- One logical change per commit. If two things can be reverted independently, they are two commits.
- Pair implementation files with their tests in the same commit.
- Order commits by dependency level: types and utilities first, then logic, then UI, then config.
- Prefer many small commits over one large commit. Rule of thumb: a reviewer should understand a commit in under 30 seconds.