| name | tach |
| description | This skill should be used when the user asks to "add a tach module", "configure tach layers", "define module boundaries", "set up interfaces", "run tach check", "check module boundaries", "tach sync", "tach show", "deprecate a dependency", "tach-ignore", "unchecked modules", "tach test", "skip tests with tach", "configure tach.toml", "source roots", "forbid circular dependencies", "enforce module boundaries", "set up architectural layers", or "tach init". |
Tach
Tach enforces module boundaries in Python codebases. It verifies that imports between modules respect declared dependencies and public interfaces, with no runtime impact.
For full documentation: https://docs.gauge.sh/
How This Project Uses Tach
The project defines three architectural layers in tach.toml: cli > ext > core. Higher layers may import from lower layers without declaring dependencies. Same-layer imports require explicit depends_on declarations. Unlayered facade and primitive modules (e.g., metaxy, metaxy._decorators) sit outside the layer hierarchy.
Run uv run tach check to verify boundaries. The pre-commit hook runs this automatically on src/ changes.
Run uv run pytest --tach to skip tests unaffected by changes (uses tach's module dependency graph for impact analysis).
Core Commands
| Command | Purpose |
|---|
tach init | Guided setup: walks through tach mod, tach sync, tach show |
tach mod | Interactive terminal UI to mark module boundaries |
tach sync | Sync tach.toml with actual imports (--add to only add) |
tach check | Report boundary/interface violations (--exact for unused deps) |
tach check-external | Validate 3rd-party imports match pyproject.toml |
tach show | Visualize dependency graph (--web, --mermaid, -o) |
tach map | JSON dependency map between files (--closure for transitives) |
tach report | Dependencies/usages of a module (--dependencies, --usages) |
tach test | Run only tests impacted by changes |
tach install | Install as pre-commit hook |
Full command reference: https://docs.gauge.sh/usage/commands/
Key Concepts
Modules
A module is a Python package or file with dependencies configured in tach.toml. Identified by import path from the nearest source root (e.g., metaxy.config for src/metaxy/config/).
[[modules]]
path = "metaxy.config"
layer = "core"
depends_on = ["metaxy._decorators", "metaxy.models"]
Special attributes:
utility: true — accessible to all modules without declaring dependency
unchecked: true — no dependency restrictions (for incremental adoption)
visibility: [] — isolate module from external imports
cannot_depend_on — forbidden dependencies (takes precedence over depends_on)
Layers
Ordered architectural tiers. Higher layers may freely import from lower layers; lower layers may never import from higher layers. Same-layer imports require explicit depends_on.
layers = ["cli", "ext", "core"]
Set layers_explicit_depends_on = true to require all cross-layer dependencies be declared explicitly. Mark a layer as closed with { name = "commands", closed = true } to force higher layers through the intermediary.
Full layers documentation: https://docs.gauge.sh/usage/layers/
Interfaces
Define public APIs to prevent deep coupling. Only imports matching expose patterns are allowed.
[[interfaces]]
expose = ["get_data"]
from = ["core"]
Interfaces support visibility to restrict consumers and exclusive: true to override other interfaces.
Full interfaces documentation: https://docs.gauge.sh/usage/interfaces/
Deprecation
Mark dependencies as deprecated to surface usage without failing checks:
depends_on = [{ path = "core", deprecated = true }]
tach-ignore
Suppress specific violations with inline comments:
from core.main import private_function
from core.api import priv, pub
Add reasons: # tach-ignore(reason here) member_name
Common Tasks
Add a New Module
- Add the module definition to
tach.toml with path, layer, and depends_on
- Run
tach sync --add to discover any additional dependencies
- Run
tach check to verify
Move a Module Between Layers
- Update the
layer field in tach.toml
- Adjust
depends_on based on new layer relationships
- Run
tach check to verify no violations
Debug Boundary Violations
Error format: file.py[L8]: Cannot import 'foo.bar'. Module 'baz' cannot depend on 'foo'.
Options:
- Add the dependency to
depends_on in tach.toml
- Mark dependency as
deprecated to track without blocking
- Use
# tach-ignore for exceptions
- Restructure imports to go through public interfaces
Configuration Reference
Full configuration documentation: https://docs.gauge.sh/usage/configuration/