Skip to main content

Architecture linting for Python repositories

Project description

Strata

Keeping Python repos from turning into spaghetti.

Most linters catch bad code inside files. Strata catches architectural drift: code crossing the wrong boundary, living in the wrong module, or growing into the wrong shape.

As a repository grows, code moves, lessons get forgotten, and the mental map decays. Tests preserve behavior and types preserve interfaces. Strata makes the repository's architectural expectations executable.

Strata enforces:

  • which layers may import which;
  • what each module or role file may contain;
  • whether orchestrator functions stay small;
  • whether dataflow and mutation are explicit;
  • whether names such as validate_* mean what they claim.

It ships a coherent default architecture rather than a blank rule framework, then lets projects disable, extend, or replace parts deliberately.

Strata is functional and self-hosting, but remains pre-release.

Installation

pip install stratalint

The distribution name is stratalint; the installed command is strata.

Quick Start

Add strata.toml at the repository root:

roots = ["src/my_package"]
tests = ["tests"]
tooling = ["scripts"]

Then run:

strata check

All rule families are enabled by default. Product roots and tooling receive structural rules; tests receive test-convention and annotation rules.

Default Structure

Product code uses domain, subdomain, then role. Tests mirror the code they cover; tooling uses one ownership level because scripts/ already establishes the outer boundary.

src/my_package/
└── domain/
    └── subdomain/
        ├── main/
        │   └── run.py
        ├── helpers/
        ├── classes/
        ├── models.py
        ├── types.py
        ├── constants.py
        └── exceptions.py
tests/unit/src/my_package/domain/subdomain/
├── _test_types.py
└── test_run.py
scripts/
├── run_tool.py
└── tool_name/
    ├── main/
    ├── helpers/
    └── classes/

Direct scripts/*.py files are thin command adapters. Supporting logic belongs under scripts/<tool>/<role>/.

Core Commands

strata check
strata rule SFS131
strata map run_plan --depth 3

strata check enforces the configured architecture, strata rule explains one rule and its remediation, and strata map renders a conservative downstream call tree. Mapping does not require Strata configuration or rule adoption.

Enforce It, Then See It

Because Strata enforces the structure, it can also render it. strata map produces a deterministic downstream call tree with clickable path:line locations, marking unresolved dynamic calls, depth limits, and cycles.

$ strata map run_map --depth 4

run_map(...)  src/strata/cli/main/map.py:21
├── _parser(...)  src/strata/cli/main/map.py:53
├── resolve_mapping_project(...)  src/strata/mapping/core/main/resolve_project.py:11
│   └── resolve_mapping_project(...)  src/strata/mapping/core/helpers/project.py:15
│       ├── _find_project_root(...)  src/strata/mapping/core/helpers/project.py:73
│       ├── _explicit_source(...)  src/strata/mapping/core/helpers/project.py:65
│       ├── _optional_config_source(...)  src/strata/mapping/core/helpers/project.py:38
│       │   └── find_config_source(...)  src/strata/config/core/main/find_config.py:12  (depth limit)
│       └── _configured_project(...)  src/strata/mapping/core/helpers/project.py:45
│           ├── load_config(...)  src/strata/config/core/main/load_config.py:15  (depth limit)
│           └── _configured_source(...)  src/strata/mapping/core/helpers/project.py:57
└── build_call_map(...)  src/strata/mapping/core/main/build.py:12
    ├── provider(...)  src/strata/mapping/core/main/build.py:24  (unresolved parameter call)
    └── render_tree(...)  src/strata/mapping/core/helpers/render.py:19
        ├── _child_lines(...)  src/strata/mapping/core/helpers/render.py:41
        │   └── _child_lines(...)  src/strata/mapping/core/helpers/render.py:41  (cycle)
        └── _label(...)  src/strata/mapping/core/helpers/render.py:88

The map is useful precisely because it is not guessing. strata check enforces layers, roles, and public surfaces first, and strata map then renders the structure the code is required to expose.

Philosophy

Strata is strict by default wherever it can make an honest deterministic claim. Following the rules should remove repeated architectural decisions from everyday work. Deliberate differences belong in selection, configuration, or custom rules, where they remain visible, rather than in scattered inline suppressions.

Unavoidable external calling conventions can use exact symbol-scoped exceptions:

[[rule_exceptions]]
rule = "SFS120"
path = "src/my_package/integrations/helpers/callbacks.py"
symbols = ["ProgressCollector.update"]
reason = "The external API invokes this callback positionally."

Exceptions accept one exact rule code, repository-relative Python file, and one or more qualified symbols. Globs, directories, line numbers, path-only entries, and inline suppression comments are not supported. strata check rejects stale exceptions that no longer suppress a fault.

Agent Skills

Generate repository-aware guidance from the active ruleset:

strata skills update
strata skills update --global

The generated skill includes Strata usage, rule-supported architecture examples, navigation and work-handoff guidance, and every enabled core and custom rule. Existing user-authored skill files are preserved unless --force is supplied.

Custom Rules

Custom checks use X... codes and the same RuleContext as core rules. Once configured, they participate in strata check, strata rule, and generated agent skills. See the custom-rule guide for the complete API and configuration.

Documentation

The quickstart, architecture model, configuration reference, adoption guide, and CLI reference live in the Strata documentation repository.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stratalint-0.2.0.tar.gz (587.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stratalint-0.2.0-py3-none-any.whl (163.4 kB view details)

Uploaded Python 3

File details

Details for the file stratalint-0.2.0.tar.gz.

File metadata

  • Download URL: stratalint-0.2.0.tar.gz
  • Upload date:
  • Size: 587.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stratalint-0.2.0.tar.gz
Algorithm Hash digest
SHA256 04417b47f61b7ee8caff661a0c1428c9079e7eb15dbfa5eeba11da231a3d7647
MD5 6d3f66998fa78c12b288a7f01e264c15
BLAKE2b-256 026893851106ef8b6ff4de801e73c8c26b8e29ed6001b8d5b56ad1c97a43b736

See more details on using hashes here.

Provenance

The following attestation bundles were made for stratalint-0.2.0.tar.gz:

Publisher: publish.yml on chio-labs/strata

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stratalint-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: stratalint-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 163.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for stratalint-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 42a25a6306e622a807f4060a2ece6521dc6ad75eed49af5ea92fa967daad53de
MD5 f2a2d145c612400b340bc8625d85c3c4
BLAKE2b-256 d0964bfd51ddefa40b777fa6079a9e0db14414d6d8e2f142f02e3d9b18fdae9e

See more details on using hashes here.

Provenance

The following attestation bundles were made for stratalint-0.2.0-py3-none-any.whl:

Publisher: publish.yml on chio-labs/strata

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page