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...

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:

  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:

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:

# ❌ 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",
]
  • 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