pycoati 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. pycoati-0.1.0/.claude/settings.json +2 -0
  2. pycoati-0.1.0/.claude/skills/rust-review/SKILL.md +88 -0
  3. pycoati-0.1.0/.github/workflows/audit.yml +29 -0
  4. pycoati-0.1.0/.github/workflows/cargo-deny.yml +35 -0
  5. pycoati-0.1.0/.github/workflows/codeql.yml +42 -0
  6. pycoati-0.1.0/.github/workflows/release.yml +114 -0
  7. pycoati-0.1.0/.github/workflows/review.yml +66 -0
  8. pycoati-0.1.0/.gitignore +74 -0
  9. pycoati-0.1.0/CLAUDE.md +84 -0
  10. pycoati-0.1.0/Cargo.lock +1042 -0
  11. pycoati-0.1.0/Cargo.toml +78 -0
  12. pycoati-0.1.0/LICENSE +21 -0
  13. pycoati-0.1.0/Makefile +60 -0
  14. pycoati-0.1.0/PKG-INFO +59 -0
  15. pycoati-0.1.0/README.md +33 -0
  16. pycoati-0.1.0/WEIGHTS.md +164 -0
  17. pycoati-0.1.0/clippy.toml +7 -0
  18. pycoati-0.1.0/deny.toml +28 -0
  19. pycoati-0.1.0/docs/plans/pycoati-bootstrap.md +249 -0
  20. pycoati-0.1.0/docs/plans/zorilla-integration.md +60 -0
  21. pycoati-0.1.0/prek.toml +35 -0
  22. pycoati-0.1.0/pyproject.toml +41 -0
  23. pycoati-0.1.0/release.toml +9 -0
  24. pycoati-0.1.0/rust-toolchain.toml +3 -0
  25. pycoati-0.1.0/rustfmt.toml +3 -0
  26. pycoati-0.1.0/src/coverage.rs +323 -0
  27. pycoati-0.1.0/src/lib.rs +724 -0
  28. pycoati-0.1.0/src/main.rs +190 -0
  29. pycoati-0.1.0/src/mock_api.rs +184 -0
  30. pycoati-0.1.0/src/parser.rs +1631 -0
  31. pycoati-0.1.0/src/pretty.rs +678 -0
  32. pycoati-0.1.0/src/pyproject.rs +373 -0
  33. pycoati-0.1.0/src/pytest.rs +528 -0
  34. pycoati-0.1.0/src/python_detect.rs +154 -0
  35. pycoati-0.1.0/src/smells.rs +299 -0
  36. pycoati-0.1.0/src/suspicion.rs +377 -0
  37. pycoati-0.1.0/src/sut_calls.rs +430 -0
  38. pycoati-0.1.0/src/walker.rs +128 -0
  39. pycoati-0.1.0/tests/cli.rs +108 -0
  40. pycoati-0.1.0/tests/end_to_end.rs +480 -0
  41. pycoati-0.1.0/tests/fixtures/hyphen_project/my_pkg/__init__.py +11 -0
  42. pycoati-0.1.0/tests/fixtures/hyphen_project/pyproject.toml +2 -0
  43. pycoati-0.1.0/tests/fixtures/hyphen_project/tests/test_greet.py +6 -0
  44. pycoati-0.1.0/tests/fixtures/project/myproj/__init__.py +9 -0
  45. pycoati-0.1.0/tests/fixtures/project/myproj/repository.py +4 -0
  46. pycoati-0.1.0/tests/fixtures/project/pyproject.toml +2 -0
  47. pycoati-0.1.0/tests/fixtures/project/tests/helpers.py +13 -0
  48. pycoati-0.1.0/tests/fixtures/project/tests/other_test.py +6 -0
  49. pycoati-0.1.0/tests/fixtures/project/tests/test_mixed.py +10 -0
  50. pycoati-0.1.0/tests/fixtures/project/tests/test_mock_only.py +11 -0
  51. pycoati-0.1.0/tests/fixtures/project/tests/test_no_asserts.py +5 -0
  52. pycoati-0.1.0/tests/fixtures/project/tests/test_overmocked.py +11 -0
  53. pycoati-0.1.0/tests/fixtures/project/tests/test_real.py +16 -0
  54. pycoati-0.1.0/tests/fixtures/project/tests/test_uses_repo.py +14 -0
  55. pycoati-0.1.0/tests/fixtures/simple/empty.py +6 -0
  56. pycoati-0.1.0/tests/fixtures/simple/test_basic.py +18 -0
  57. pycoati-0.1.0/tests/fixtures/stubs_style/test_stubs.py +42 -0
  58. pycoati-0.1.0/tests/fixtures/unittest_style/test_unittest.py +38 -0
  59. pycoati-0.1.0/tests/inventory_shape.rs +141 -0
  60. pycoati-0.1.0/tests/parse_assertions.rs +169 -0
  61. pycoati-0.1.0/tests/pytest_integration.rs +640 -0
  62. pycoati-0.1.0/tests/scoring.rs +146 -0
  63. pycoati-0.1.0/tests/stubs.rs +162 -0
@@ -0,0 +1,2 @@
1
+ {
2
+ }
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: rust-review
3
+ context: fork
4
+ description: Deep Rust code quality review. Auto-invoke when finishing a task, before marking work complete, when the user asks to review code, or when preparing a PR. Covers error handling, async correctness, duplicated logic, test quality, performance patterns, idiomatic Rust, docs-code alignment, and API design beyond what clippy catches.
5
+ ---
6
+
7
+ # Deep Code Review for pycoati
8
+
9
+ Perform a thorough code quality review of the changes in this project. Go beyond what clippy and rustfmt catch. Focus on the areas below and report findings grouped by severity: Must Fix, Should Fix, Suggestion.
10
+
11
+ First, run `cargo fmt --all -- --check` and `cargo clippy --all-targets --all-features -- -D warnings` to confirm the automated checks pass. If they don't, fix those first before proceeding with the deep review.
12
+
13
+ ## 1. Error Handling Quality
14
+
15
+ - `.unwrap()` outside tests is forbidden. Flag any instance.
16
+ - Check that `.context("message")` is used when propagating errors from external crates with `?` — bare `?` loses context about what operation failed.
17
+ - Flag `Result` return types where the function never actually returns `Err` (remove the wrapper — clippy catches this as `unnecessary_wraps` but double-check).
18
+ - Flag `.map_err(|_| ...)` that silently discards error information.
19
+ - Verify error types are specific enough — no `String` as error type, no `anyhow::Error` leaking into library-style modules.
20
+ - Check that `thiserror` error variants have `#[from]` or manual `From` impls for their sources.
21
+
22
+ ## 2. Async / Tokio Correctness
23
+
24
+ - Flag any `MutexGuard` or `RwLockGuard` held across an `.await` point — this can deadlock or prevent `Send` futures.
25
+ - Flag blocking operations (std::fs, std::process::Command, heavy computation) running directly in async context without `spawn_blocking`.
26
+ - Check that spawned tasks (`tokio::spawn`) have their `JoinHandle` either awaited or explicitly dropped with a comment explaining why.
27
+ - Verify `tokio::select!` branches handle cancellation correctly (partially completed work).
28
+ - Check for unbounded channels or queues that could cause memory issues under load.
29
+
30
+ ## 3. Duplicated Logic
31
+
32
+ - Search for functions or code blocks that do substantially the same thing with minor variations. Flag them and suggest extraction into a shared function, trait, or generic.
33
+ - Pay special attention to:
34
+ - Error handling boilerplate that could be a helper
35
+ - Serialization/deserialization patterns
36
+ - Similar match arms across different functions
37
+ - Suggest concrete refactoring: which function to extract, what parameters it should take.
38
+
39
+ ## 4. Test Quality
40
+
41
+ - **Tests that don't assert**: Flag any test that just calls code without asserting on the result. Running without panicking is not a test.
42
+ - **Tests that assert too little**: `assert!(result.is_ok())` without checking the value inside is weak. Suggest asserting on the actual content.
43
+ - **Overly verbose tests**: Tests with excessive setup that obscure what's actually being tested. Suggest extracting test helpers or fixtures.
44
+ - **Missing edge cases**: For each public function, check if tests cover: empty input, error paths, boundary conditions.
45
+ - **Test naming**: Test names should describe the behavior being tested, not the implementation (`test_returns_error_on_missing_file` not `test_function_xyz`).
46
+ - **Integration vs unit**: Verify that unit tests (in `#[cfg(test)] mod tests`) test internal logic, while `tests/` tests exercise the public CLI interface.
47
+ - Suggest adding `proptest` for any function that transforms data (roundtrip properties).
48
+
49
+ ## 5. Performance Patterns
50
+
51
+ - Flag unnecessary `.clone()` — suggest borrowing or restructuring ownership.
52
+ - Flag `.collect::<Vec<_>>()` immediately followed by `.iter()` — the intermediate collection is usually unnecessary.
53
+ - Prefer `&str` over `String`, `&[T]` over `Vec<T>`, `&Path` over `PathBuf` in function parameters when ownership isn't needed.
54
+ - Flag large structs being passed by value instead of reference.
55
+ - Check for `Arc::clone(&x)` vs `x.clone()` — the former is required by our lint config for clarity.
56
+ - In async code, flag futures that capture large amounts of data across await points.
57
+
58
+ ## 6. Idiomatic Rust
59
+
60
+ - Prefer iterator chains over manual `for` loops with `push`.
61
+ - Use `if let` / `let else` instead of `match` with a single interesting arm and a wildcard.
62
+ - Suggest `impl From<X> for Y` instead of standalone conversion functions.
63
+ - Suggest `Display` implementations instead of `to_string()` methods.
64
+ - Flag `&String`, `&Vec<T>`, `&Box<T>` in function signatures — use `&str`, `&[T]`, `&T` instead.
65
+ - Check doc comments on all public types and functions.
66
+
67
+ ## 7. Documentation ↔ Code Alignment
68
+
69
+ - **Check that `README.md` and any `docs/` content still match the current CLI and behavior.** Compare documented flags and subcommands against the `clap` definitions in `src/`.
70
+ - Flag any mismatch as Must Fix — stale docs are worse than no docs.
71
+
72
+ ## 8. API and Module Design
73
+
74
+ - Is `main.rs` thin? It should just parse args and call into library code.
75
+ - Are module boundaries clean? Each module should have a clear responsibility.
76
+ - Check for any `pub` items that don't need to be public.
77
+ - Flag circular dependencies between modules.
78
+
79
+ ## Output Format
80
+
81
+ Group all findings by severity, then by area. For each finding:
82
+ - State **what** the issue is and **where** (file:line or function name)
83
+ - Explain **why** it matters
84
+ - Suggest a **concrete fix** (not just "improve this")
85
+
86
+ End with a summary: X must-fix, Y should-fix, Z suggestions.
87
+
88
+ $ARGUMENTS
@@ -0,0 +1,29 @@
1
+ name: "Cargo Audit"
2
+
3
+ on:
4
+ push:
5
+ paths:
6
+ - "Cargo.toml"
7
+ - "Cargo.lock"
8
+ pull_request:
9
+ paths:
10
+ - "Cargo.toml"
11
+ - "Cargo.lock"
12
+ schedule:
13
+ - cron: "0 0 * * *" # Daily at midnight UTC
14
+ workflow_dispatch:
15
+
16
+ permissions:
17
+ contents: read
18
+ issues: write
19
+
20
+ jobs:
21
+ audit:
22
+ name: Security Audit
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Checkout repository
26
+ uses: actions/checkout@v6
27
+
28
+ - name: Run cargo-audit
29
+ uses: actions-rust-lang/audit@v1
@@ -0,0 +1,35 @@
1
+ name: "Cargo Deny"
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ paths:
7
+ - "Cargo.lock"
8
+ - "Cargo.toml"
9
+ - "deny.toml"
10
+ pull_request:
11
+ paths:
12
+ - "Cargo.lock"
13
+ - "Cargo.toml"
14
+ - "deny.toml"
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ deny:
21
+ name: ${{ matrix.check }}
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ check: [bans, licenses, sources]
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v6
30
+
31
+ - name: Run cargo-deny (${{ matrix.check }})
32
+ uses: EmbarkStudios/cargo-deny-action@v2
33
+ with:
34
+ command: check ${{ matrix.check }}
35
+ arguments: --all-features
@@ -0,0 +1,42 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ paths:
7
+ - "src/**"
8
+ - "Cargo.toml"
9
+ - "Cargo.lock"
10
+ pull_request:
11
+ branches: ["main"]
12
+ paths:
13
+ - "src/**"
14
+ - "Cargo.toml"
15
+ - "Cargo.lock"
16
+ schedule:
17
+ - cron: "0 6 * * 1" # Weekly on Monday at 06:00 UTC
18
+
19
+ permissions:
20
+ security-events: write
21
+ packages: read
22
+ actions: read
23
+ contents: read
24
+
25
+ jobs:
26
+ analyze:
27
+ name: Analyze Rust
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - name: Checkout repository
31
+ uses: actions/checkout@v6
32
+
33
+ - name: Initialize CodeQL
34
+ uses: github/codeql-action/init@v4
35
+ with:
36
+ languages: rust
37
+ build-mode: none
38
+
39
+ - name: Perform CodeQL Analysis
40
+ uses: github/codeql-action/analyze@v4
41
+ with:
42
+ category: "/language:rust"
@@ -0,0 +1,114 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: write
11
+
12
+ jobs:
13
+ build-wheels:
14
+ name: Build wheels on ${{ matrix.os }}
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ matrix:
18
+ os: [ubuntu-latest, macos-14, windows-latest]
19
+
20
+ steps:
21
+ - uses: actions/checkout@v6
22
+
23
+ - name: Build wheels
24
+ uses: PyO3/maturin-action@v1
25
+ with:
26
+ args: --release --strip
27
+ manylinux: 2_28
28
+
29
+ - name: Upload wheels
30
+ uses: actions/upload-artifact@v7
31
+ with:
32
+ name: wheels-${{ matrix.os }}
33
+ path: target/wheels/*.whl
34
+
35
+ build-sdist:
36
+ name: Build source distribution
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v6
40
+
41
+ - name: Set up Rust
42
+ uses: dtolnay/rust-toolchain@stable
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v6
46
+ with:
47
+ python-version: '3.11'
48
+
49
+ - name: Install maturin
50
+ run: pip install maturin
51
+
52
+ - name: Build sdist
53
+ run: maturin sdist
54
+
55
+ - name: Upload sdist
56
+ uses: actions/upload-artifact@v7
57
+ with:
58
+ name: sdist
59
+ path: target/wheels/*.tar.gz
60
+
61
+ create-release:
62
+ name: Create GitHub Release
63
+ needs: [build-wheels, build-sdist]
64
+ runs-on: ubuntu-latest
65
+ if: startsWith(github.ref, 'refs/tags/')
66
+ steps:
67
+ - uses: actions/checkout@v6
68
+
69
+ - name: Download all artifacts
70
+ uses: actions/download-artifact@v8
71
+ with:
72
+ path: dist
73
+
74
+ - name: Flatten artifacts
75
+ run: |
76
+ mkdir -p wheels
77
+ find dist -name '*.whl' -exec cp {} wheels/ \;
78
+ find dist -name '*.tar.gz' -exec cp {} wheels/ \;
79
+
80
+ - name: Create Release
81
+ uses: softprops/action-gh-release@v2
82
+ with:
83
+ files: wheels/*
84
+ generate_release_notes: true
85
+ draft: false
86
+ prerelease: false
87
+
88
+ publish-to-pypi:
89
+ name: Publish to PyPI
90
+ needs: [build-wheels, build-sdist, create-release]
91
+ runs-on: ubuntu-latest
92
+ if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
93
+ environment:
94
+ name: pypi
95
+ url: https://pypi.org/p/pycoati
96
+ permissions:
97
+ id-token: write
98
+ steps:
99
+ - name: Download all artifacts
100
+ uses: actions/download-artifact@v8
101
+ with:
102
+ path: dist
103
+
104
+ - name: Flatten artifacts
105
+ run: |
106
+ mkdir -p wheels
107
+ find dist -name '*.whl' -exec cp {} wheels/ \;
108
+ find dist -name '*.tar.gz' -exec cp {} wheels/ \;
109
+
110
+ - name: Publish to PyPI
111
+ uses: pypa/gh-action-pypi-publish@release/v1
112
+ with:
113
+ packages-dir: wheels/
114
+ skip-existing: true
@@ -0,0 +1,66 @@
1
+ name: Code Review
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - "src/**"
8
+ - "tests/**"
9
+ - "Cargo.toml"
10
+ - "Cargo.lock"
11
+ - "rust-toolchain.toml"
12
+ - "clippy.toml"
13
+ - "rustfmt.toml"
14
+ pull_request:
15
+ paths:
16
+ - "src/**"
17
+ - "tests/**"
18
+ - "Cargo.toml"
19
+ - "Cargo.lock"
20
+ - "rust-toolchain.toml"
21
+ - "clippy.toml"
22
+ - "rustfmt.toml"
23
+
24
+ concurrency:
25
+ group: ${{ github.workflow }}-${{ github.ref }}
26
+ cancel-in-progress: true
27
+
28
+ env:
29
+ CARGO_TERM_COLOR: always
30
+
31
+ jobs:
32
+ fmt:
33
+ name: Formatting
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ - uses: dtolnay/rust-toolchain@stable
38
+ with:
39
+ components: rustfmt
40
+ - run: cargo fmt --all --check
41
+
42
+ clippy:
43
+ name: Clippy
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v6
47
+ - uses: dtolnay/rust-toolchain@stable
48
+ with:
49
+ components: clippy
50
+ - uses: Swatinem/rust-cache@v2
51
+ - run: cargo clippy --all-targets --all-features -- -D warnings
52
+
53
+ test:
54
+ name: Tests
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - uses: actions/checkout@v6
58
+ - uses: dtolnay/rust-toolchain@stable
59
+ - uses: Swatinem/rust-cache@v2
60
+ - name: Set up Python
61
+ uses: actions/setup-python@v6
62
+ with:
63
+ python-version: "3.11"
64
+ - name: Install pytest
65
+ run: pip install pytest
66
+ - run: cargo test --all-features
@@ -0,0 +1,74 @@
1
+ # Rust
2
+ /target/
3
+ **/*.rs.bk
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # Virtual environments
31
+ .env
32
+ .venv
33
+ env/
34
+ venv/
35
+ ENV/
36
+ env.bak/
37
+ venv.bak/
38
+
39
+ # Claude Code
40
+ .claude/worktrees/
41
+ cf/
42
+
43
+ # IDE
44
+ .vscode/
45
+ .idea/
46
+ *.swp
47
+ *.swo
48
+ *~
49
+
50
+ # OS
51
+ .DS_Store
52
+ .DS_Store?
53
+ ._*
54
+ .Spotlight-V100
55
+ .Trashes
56
+ ehthumbs.db
57
+ Thumbs.db
58
+
59
+ # Logs
60
+ *.log
61
+
62
+ # Temporary files
63
+ *.tmp
64
+ *.temp
65
+ .cache/
66
+
67
+ # maturin / PyO3
68
+ *.so
69
+ *.pyd
70
+
71
+ # Testing
72
+ .pytest_cache/
73
+ .coverage
74
+ htmlcov/
@@ -0,0 +1,84 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ pycoati audits Python test suites for mock smells and suspicious tests. Hybrid Rust/Python project: builds a Rust binary (`pycoati`) and packages it as a Python package via maturin for distribution through pip/uv.
8
+
9
+ ## Common Commands
10
+
11
+ ```bash
12
+ # Pre-commit checks (always run before committing)
13
+ cargo fmt --check && cargo clippy --all-targets --all-features -- -D warnings && cargo test --all-features
14
+
15
+ # Run tests
16
+ cargo test
17
+
18
+ # Build and install locally for testing (puts `pycoati` on PATH at ~/.cargo/bin)
19
+ cargo install --path . --force
20
+
21
+ # Build a Python wheel (only needed when packaging for pip/uv distribution)
22
+ maturin develop
23
+
24
+ # Full review (fmt, clippy, tests, audit, deny)
25
+ make review
26
+ ```
27
+
28
+ ## Development Workflow
29
+
30
+ All features and bug fixes follow TDD (red-green-refactor). No implementation code without a failing test first. Bug fixes must include a regression test that fails without the fix.
31
+
32
+ ## Test Changes Require Deliberation
33
+
34
+ When a test fails during implementation:
35
+
36
+ 1. **Stop and diagnose.** Understand WHY it fails before changing anything.
37
+ 2. **Default assumption: the test is right.** Fix the implementation first.
38
+ 3. **If the test genuinely needs updating** (requirements changed, API evolved), explain what changed and why the old assertion is no longer correct before modifying it.
39
+ 4. **Never weaken an assertion just to make it pass.**
40
+ 5. **If uncertain, ask.**
41
+
42
+ ## Branch Hygiene
43
+
44
+ **Always merge `main` into your feature branch before creating a PR.** Run:
45
+
46
+ ```bash
47
+ git fetch origin main && git merge origin/main
48
+ ```
49
+
50
+ Then re-run the full check suite to verify the merge didn't introduce breakage.
51
+
52
+ ## When Stuck
53
+
54
+ If hitting a wall:
55
+ 1. Do not silently work around it — state the problem explicitly.
56
+ 2. Do not attempt more than 3 approaches without reporting what was tried and why each failed.
57
+ 3. Do not modify unrelated code hoping it fixes the issue.
58
+ 4. Revert to last known good state if changes made things worse.
59
+ 5. When in doubt, ask.
60
+
61
+ ## Review Before Completing Work
62
+
63
+ Before marking any task as complete:
64
+
65
+ 1. **Automated checks** (run automatically via prek pre-commit hook on `git commit`):
66
+ - `cargo fmt --all -- --check`
67
+ - `cargo clippy --all-targets --all-features -- -D warnings`
68
+ - `cargo test --all-features --bins`
69
+
70
+ 2. **Deep review** (REQUIRED for all significant changes):
71
+ - Run the `rust-review` skill (`/rust-review`) before marking work as complete
72
+ - Address all Must Fix items before completing
73
+ - Address Should Fix items unless there's a documented reason not to
74
+
75
+ 3. **Full review** (run before pushing):
76
+ - `make review`
77
+
78
+ ### Code Rules
79
+ - No `.unwrap()` outside tests — use `.context()` when propagating errors with `?`
80
+ - No `MutexGuard` held across `.await`
81
+ - Prefer `&str`/`&[T]`/`&Path` over owned types in function parameters when ownership isn't needed
82
+ - Tests must assert on values, not just "runs without panic"
83
+ - Flaky tests need root-cause analysis, not looser assertions
84
+ - No new dependencies without surfacing the choice