4.7 KiB
| title | date | category | module | problem_type | component | severity | applies_when | tags |
|---|---|---|---|---|---|---|---|---|
| Working Tree Hygiene — Dependency Groups and Gitignore | 2026-04-24 | docs/solutions/best-practices | development_workflow | best_practice | development_workflow | low | [Reviewing uncommitted changes before committing Adding new dependencies to pyproject.toml Updating .gitignore with new ignore patterns] | [dependencies pyproject gitignore hygiene code-review dev-tools] |
Working Tree Hygiene — Dependency Groups and Gitignore
Context
A code review of uncommitted changes on main caught three preventable hygiene issues:
pyright(a static type checker) was added to[project] dependenciesinpyproject.tomlinstead of[dependency-groups] dev.gitignorecontained a duplicate.worktreesentry- A blog post included a hardcoded correlation coefficient with no reproducible source (documented separately in
blog-numbers-from-pipeline-outputs)
All three were caught before commit, but they illustrate a pattern: small working tree cleanups accumulate friction when not reviewed systematically.
Guidance
Dependency classification
When adding a package to pyproject.toml, ask: does this run in production?
| If... | Put it in... |
|---|---|
| The app imports it at runtime | [project] dependencies |
| It is a type checker, test runner, linter, or dev server | [dependency-groups] dev |
| It is only used in build scripts or CI | [dependency-groups] dev |
Concrete check: search the codebase for import <package> or from <package>. If it only appears in tests/, scripts/, or type stubs, it belongs in dev.
Gitignore hygiene
Before committing a .gitignore change, run:
sort .gitignore | uniq -d
If anything prints, you have duplicates. Remove them.
Also check that your new entry does not overlap with an existing pattern:
.worktrees/and.worktreesare redundant — keep the slash form for directoriesdata/*.jsonalready coversdata/motions.json— do not add the specific file
Pre-commit audit checklist
For every set of uncommitted changes:
- Dependencies: Any new packages in the right group?
- Gitignore: Any duplicates or redundant patterns?
- Dev tools in venv: Is the linter/formatter also listed in
[dependency-groups] devsouv run <tool>works? (A tool in pre-commit but not inpyproject.tomlwill fail for developers who run it manually.) - Blog/docs: Any hardcoded numbers without canonical sources? (see
blog-numbers-from-pipeline-outputs) - Config: Any secrets or local paths committed by accident?
Why This Matters
These issues are individually trivial, but together they create a "broken windows" effect. A pyproject.toml with dev tools in runtime dependencies signals that the project does not distinguish between production and development concerns. Duplicate .gitignore entries suggest the file is append-only and never reviewed. Small hygiene lapses compound into larger maintainability debt.
The fix is cheap: a 30-second scan of the diff before committing prevents all of them.
When to Apply
- Before every commit that touches
pyproject.toml,.gitignore, oruv.lock - When onboarding a new dependency
- During code review of any PR that adds build tools, test frameworks, or local config
Examples
Dependency misclassification:
# ❌ Before
[project]
dependencies = [
"duckdb>=1.3.2",
"pyright>=1.1.408", # dev tool in runtime deps
]
# ✅ After
[project]
dependencies = [
"duckdb>=1.3.2",
]
[dependency-groups]
dev = [
"pytest>=9.0.2",
"pyright>=1.1.408",
]
Gitignore duplicate:
# Worktrees
.worktrees/
# Generated analysis files
thoughts/explorer/*.json
-
- # Stray temp files
- .worktrees # ← duplicate, remove
**Dev tool missing from venv:**
A linter was configured in `.pre-commit-config.yaml` and `pyproject.toml`'s `[tool.ruff]` section, but it was not listed in `[dependency-groups] dev`. Developers running `uv run ruff check .` got a "command not found" error even though pre-commit worked.
```toml
# ❌ Before — tool configured but not installable
[tool.ruff.lint]
select = ["T20", "BLE"]
[dependency-groups]
dev = ["pytest>=9.0.2"]
# ✅ After — tool is both configured and installable
[tool.ruff.lint]
select = ["T20", "BLE"]
[dependency-groups]
dev = [
"pytest>=9.0.2",
"ruff>=0.11.0",
]
Related
docs/solutions/best-practices/blog-numbers-from-pipeline-outputs-2026-04-16.md— companion guidance on keeping quantitative claims reproducibledocs/solutions/workflow-issues/verify-session-artifacts-against-canonical-sources-2026-04-24.md— same verification principle applied to session artifacts