- Keep cognitive complexity below the threshold of 25 (see Complexity and linting below).
- Keep functions under 150 lines (enforced by Clippy
too_many_lines). - Prefer straightforward data flow (fewer threaded parameters, clearer state boundaries). This is a design guideline—there is no compiler lint for it; match patterns used in existing modules.
- Add
///rustdoc to all new public and private items (missing_docs+missing_docs_in_private_itemsare both enforced; see Lint configuration). - Use the What / Inputs / Output / Details rustdoc layout for non-trivial APIs (see Documentation for the template).
- Add focused unit tests for new logic.
- Add integration tests when behavior crosses modules or the CLI boundary.
- Identify the root cause before writing code.
- Write or adjust a test that fails on the bug.
- Run the test — it must fail. If it passes, the test does not reproduce the issue; adjust it.
- Fix the bug.
- Run the test again — it must pass. If not, iterate on the fix.
- Add edge-case tests when they reduce future regressions.
Run from the repository root, in this order:
cargo fmt --allcargo clippy --all-targets --all-features -- -D warningscargo checkcargo test -- --test-threads=1
CI note: .github/workflows/rust.yml runs cargo build and cargo test (with --test-threads=1). .github/workflows/lint.yml runs cargo fmt --check and cargo clippy ... -D warnings on pushes/PRs to main. .github/workflows/security.yml runs supply-chain and secret scans (no clippy). cargo check is still required locally before considering work done.
Cargo.toml — [lints.clippy] (excerpt; see file for full list):
[lints.clippy]
cognitive_complexity = "warn"
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }
unwrap_used = "deny"
missing_docs_in_private_items = "warn"Cargo.toml — [lints.rust]:
[lints.rust]
missing_docs = "warn"clippy.toml:
cognitive-complexity-threshold = 25— used by Clippy'scognitive_complexitylint (not cyclomatic complexity; that is a different metric).too-many-lines-threshold = 150— used by Clippy'stoo_many_lineslint.
With cargo clippy ... -- -D warnings, all warnings become errors. That means cognitive_complexity, missing_docs, and missing_docs_in_private_items violations will fail the Clippy run.
Before completing any task, ensure all of the following pass:
- Format:
cargo fmt --allproduces no diff. - Clippy:
cargo clippy --all-targets --all-features -- -D warningsis clean. - Compile:
cargo checksucceeds. - Tests:
cargo test -- --test-threads=1— all tests pass. - Complexity: New functions stay under cognitive-complexity threshold (25).
- Length: New functions stay under too-many-lines threshold (150).
- Exceptions: If a threshold cannot be met, add a documented
#[allow(...)]with a justification comment. Use sparingly.
- All new public and private functions, methods, structs, enums, traits, and modules must have
///rustdoc comments.missing_docsfires on public items.missing_docs_in_private_itemsfires on private items.- Both are warn, promoted to error by
-D warnings.
- For non-trivial APIs, use the structured rustdoc layout with What, Inputs, Output, and Details sections:
/// What: Brief description of what the function does. /// /// Inputs: /// - `param1`: Description of parameter 1 /// - `param2`: Description of parameter 2 /// /// Output: /// - Description of return value or side effects /// /// Details: /// - Additional context, edge cases, or important notes pub fn example_function(param1: Type1, param2: Type2) -> Result<Type3> { // implementation }
- Documentation must include all four sections: What, Inputs, Output, and Details.
For bug fixes:
- Create a failing test that reproduces the issue.
- Fix the bug.
- Verify the test passes.
- Add additional edge-case tests if applicable.
For new features:
- Add unit tests for new functions/methods.
- Add integration tests for new workflows.
- Test error cases and edge conditions.
- Ensure tests are meaningful and cover the functionality.
Test guidelines:
- Tests must be deterministic and not rely on undeclared external machine state.
- For code paths that would mutate the system, exercise dry-run behavior via the
dry_runbool field (wired from the CLI--dry-runflag through the app), or use equivalent test doubles. Do not blindly pass--dry-runto every shell command. - Always run tests with
--test-threads=1to avoid parallel interference.
- Edition: Rust 2024 (see
Cargo.toml). - Naming: Clear and descriptive; clarity over brevity.
- Errors: Prefer
Result; never useunwrap()orexpect()outside tests (enforced byunwrap_used = "deny"). - Control flow: Prefer early returns over deep nesting.
- Logging: Use
tracing; avoid noisyinfo!in hot paths.
- All code paths that modify the system must respect the application's dry-run mode.
- The CLI
--dry-runflag wires into adry_runbool through the app. - In dry-run mode, do not execute mutating commands; simulate or no-op as existing executors do.
- When implementing new features that execute system commands, always check the
dry_runflag first.
- Do not assume
pacman, AUR helpers (paru,yay, etc.), or other external tools are installed. - Handle their absence with clear, actionable error messages.
- Never crash or panic when a tool is missing.
- User-facing errors must say what failed and what the user can do next.
- Provide clear, actionable guidance — not raw error codes or stack traces.
If config keys or schema change:
- Update shipped examples under
config/(settings.conf,theme.conf,keybinds.conf, and related files as needed). - Ensure backward compatibility when possible.
- Do not edit wiki or
README.mdunless the user explicitly asks (see Documentation policy).
- Keyboard-first: Design for minimal keystrokes, Vim-friendly navigation.
- Help overlay: If default keybinds change, update the in-app help overlay.
- Keybind consistency: Maintain consistency with existing keybind patterns.
- Do not create or edit
*.mdfiles (includingREADME.md) unless explicitly requested. - Do not edit wiki content unless explicitly requested.
- Prefer rustdoc for code documentation.
- Use internal
dev/docs only when the user explicitly asks.
- Template:
.github/PULL_REQUEST_TEMPLATE.md. - Creating: If no PR file exists in
dev/PR/for the current branch, create one based on the template. Name itPR_<branch-name>.mdorPR_<short-description>.md. - Updating: If a PR file already exists, always update it when changes are made to the codebase.
- Content: Document only changes that differ from the main branch. Remove entries for changes that were reverted. Focus on the final state, not intermediate iterations.
| Concern | Enforcement | Threshold |
|---|---|---|
| Cognitive complexity | Clippy cognitive_complexity + clippy.toml |
25 |
| Function length | Clippy too_many_lines + clippy.toml |
150 lines |
| Data flow / coupling | Manual design review; no lint — match existing module patterns | N/A |
These rules exist to prevent specific vulnerability classes identified in dev/SECURITY_AUDIT_REPORT.md. They are mandatory — not suggestions.
- Never interpolate package names, file paths, or user input directly into shell command strings. Always pass them through
shell_single_quote()fromsrc/install/utils.rsfirst. - When building shell commands with multiple package names, quote each name individually before joining:
let quoted: Vec<String> = names.iter().map(|n| shell_single_quote(n)).collect(); let names_str = quoted.join(" ");
- When adding a new function in
src/install/that constructs shell commands, verify that every variable fragment interpolated intoformat!()is either:- A constant/static string, or
- Passed through
shell_single_quote(), or - Validated against
^[a-z\d@._+-]+$before use.
- Never use
format!()withsh -cand unsanitized user data. PreferCommand::new().arg()argument passing over string interpolation when possible —Commandarguments are not interpreted by a shell. - On Windows, never pass unescaped strings into
cmd /Corcmd /K. Use PowerShell with proper quoting or escape&,|,>,<,^for cmd.exe.
- Never log passwords. Any function that writes command strings to disk (log files, temp scripts) must redact password pipe patterns (
printf '%s\n' '...' | sudo -S) before writing. Replace the password with[REDACTED]. - Never store passwords in plain
String. UseSecureString(zeroize-backed) fromsrc/state/types.rsfor all password fields. IfSecureStringdoes not exist yet, useStringbut add a// TODO: migrate to SecureStringcomment and azeroizecall on the containing struct'sDrop. - When creating files that might contain sensitive data (log files, temp scripts), use
OpenOptions::mode(0o600)on Unix to prevent world-readable permissions. - Never include passwords or credentials in
tracing::info!,tracing::debug!, ortracing::warn!output. If you need to log that a password was provided, logpassword_provided = true— never the value.
- All curl invocations must go through
curl_args()insrc/util/mod.rs. Do not construct raw curl commands. curl_args()must include--max-filesize 10485760(10 MB) to prevent memory exhaustion from oversized responses. If this is not yet present, add it.- Never disable TLS certificate verification (
-k/--insecure) on Linux builds. The Windows-kflag is a known issue tracked for removal. - URLs constructed from user input or API data must be validated to start with
http://orhttps://before passing to curl. Seelooks_like_http_url()insrc/logic/repos/apply_plan.rsfor the pattern.
- Validate all paths before writing. Use or extend
is_safe_abs_path()fromsrc/logic/repos/apply_plan.rsfor any privileged file write. Reject paths containing... - Create temp files atomically. Use
OpenOptions::create_new(true).mode(0o700)instead offs::write()followed byset_permissions(). Thecreate_newflag prevents symlink-following TOCTOU races. - When writing files under
~/.config/pacsea/, create parent directories withcreate_dir_allbut do not set overly permissive modes. Default umask-derived permissions are acceptable for non-sensitive files; use0o600for anything that could contain credentials.
- Functions that check
PACSEA_INTEGRATION_TESTorPACSEA_TEST_*environment variables must not bypass security checks in release builds. Gate them with#[cfg(debug_assertions)]or#[cfg(test)]so they compile to no-ops in release mode. - Never add new environment-variable-based test overrides that skip authentication, privilege checks, or input validation without a
#[cfg(debug_assertions)]guard.
- Run
cargo auditafter adding or updating dependencies. Resolve all critical and high advisories before merging. - Prefer direct dependencies over transitive ones for security-sensitive functionality. If a transitive dependency has an advisory, check if the parent crate can be updated to pull a fixed version.
- Do not add dependencies that require
unsafefor their core functionality unless there is no safe alternative and the crate is well-maintained.
Full findings and remediation steps: dev/SECURITY_AUDIT_REPORT.md and dev/SECURITY_REMEDIATION_GUIDE.md.
- No unsolicited
*.md/ wiki / README edits. - Preserve dry-run semantics and graceful handling of missing external tools.
- Keep PR description files in
dev/PR/in sync with the branch when that workflow applies.