tach

$npx mdskill add anam-org/metaxy/tach

Enforces module boundaries and dependencies in Python codebases

  • Solves issues with circular dependencies and unauthorized imports
  • Uses a configuration file and module dependency graph for analysis
  • Checks imports against declared dependencies and layer rules
  • Reports violations and enforces boundaries during testing and commits
SKILL.md
.github/skills/tachView on GitHub ↗
---
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/`).

```toml
[[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`.

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

```toml
[[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:

```toml
depends_on = [{ path = "core", deprecated = true }]
```

### tach-ignore

Suppress specific violations with inline comments:

```python
# tach-ignore
from core.main import private_function

from core.api import priv, pub  # tach-ignore priv
```

Add reasons: `# tach-ignore(reason here) member_name`

## Common Tasks

### Add a New Module

1. Add the module definition to `tach.toml` with `path`, `layer`, and `depends_on`
2. Run `tach sync --add` to discover any additional dependencies
3. Run `tach check` to verify

### Move a Module Between Layers

1. Update the `layer` field in `tach.toml`
2. Adjust `depends_on` based on new layer relationships
3. 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:
1. Add the dependency to `depends_on` in `tach.toml`
2. Mark dependency as `deprecated` to track without blocking
3. Use `# tach-ignore` for exceptions
4. Restructure imports to go through public interfaces

## Configuration Reference

Full configuration documentation: https://docs.gauge.sh/usage/configuration/
More from anam-org/metaxy