You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
motief/docs/solutions/best-practices/working-tree-hygiene-depend...

140 lines
4.7 KiB

---
title: Working Tree Hygiene — Dependency Groups and Gitignore
date: 2026-04-24
category: docs/solutions/best-practices
module: development_workflow
problem_type: best_practice
component: development_workflow
severity: low
applies_when:
- Reviewing uncommitted changes before committing
- Adding new dependencies to pyproject.toml
- Updating .gitignore with new ignore patterns
tags: [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:
1. `pyright` (a static type checker) was added to `[project] dependencies` in `pyproject.toml` instead of `[dependency-groups] dev`
2. `.gitignore` contained a duplicate `.worktrees` entry
3. 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:
```bash
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 `.worktrees` are redundant — keep the slash form for directories
- `data/*.json` already covers `data/motions.json` — do not add the specific file
### Pre-commit audit checklist
For every set of uncommitted changes:
1. **Dependencies**: Any new packages in the right group?
2. **Gitignore**: Any duplicates or redundant patterns?
3. **Dev tools in venv**: Is the linter/formatter also listed in `[dependency-groups] dev` so `uv run <tool>` works? (A tool in pre-commit but not in `pyproject.toml` will fail for developers who run it manually.)
4. **Blog/docs**: Any hardcoded numbers without canonical sources? (see `blog-numbers-from-pipeline-outputs`)
5. **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`, or `uv.lock`
- When onboarding a new dependency
- During code review of any PR that adds build tools, test frameworks, or local config
## Examples
**Dependency misclassification:**
```toml
# ❌ 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:**
```diff
# 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 reproducible
- `docs/solutions/workflow-issues/verify-session-artifacts-against-canonical-sources-2026-04-24.md` — same verification principle applied to session artifacts