Skip to main content

Fensu

Keeping Python repos from turning into spaghetti.

Fensu means "fence" in Japanese.

Most linters catch bad code inside files. Fensu 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. Fensu makes the repository's architectural expectations executable.

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

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

Installation

pip install fensu

The authoring/API distribution is fensu. It installs the lockstep fensu-cli binary package, which exclusively owns the fensu command. Core-only check, init, rule, map, skills, and --version execution is native. Configured Python custom rules launch one compatible Python host only for the policy metadata or callbacks they require.

Built-in commands are available only through the native fensu executable; python -m fensu is retired. Installing fensu-cli from its source distribution requires Rust 1.95 or newer.

Fensu requires Python 3.12+ and includes a compiled analysis core. Prebuilt wheels cover Linux (x86_64, aarch64), macOS (Intel, Apple silicon), and Windows (x86_64). On other platforms, pip builds from source and requires a Rust toolchain at version 1.95 or newer.

Quick Start

From the directory containing an existing repository's pyproject.toml, detect the layout, write a validated configuration with the full ruleset enabled, run an initial check, and install repository-local agent guidance:

fensu init --yes

For an empty repository, provide the package name to scaffold src/ and tests/:

fensu init --yes --name my_package

Pass --no-skills only when automation explicitly requires configuration without repository-local guidance. Do not call onboarding complete until this succeeds:

fensu skills --check

Then run:

fensu check

To configure manually instead, add a minimal fensu.toml at the repository root:

roots = ["src/my_package"]

tests defaults to ["tests"], tooling is optional, caching is enabled by default, and every rule family is on by default. Install and verify agent guidance after writing configuration manually:

fensu skills
fensu skills --check
fensu check

Product roots and tooling receive structural rules; tests receive test-convention and annotation rules.

Default Structure

Product code uses domain, optional subdomain, then role. Every leaf domain or subdomain owns meaningful behavior through a direct main/ containing at least one entry module. Branch-domain parents do not need their own main/; their leaf subdomains do. 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/

Do not create an empty or initializer-only main/ to satisfy the layout. If a package contains only passive models, types, constants, exceptions, or classes, move those declarations into the closest domain or subdomain whose main/ behavior owns and uses them. Fixed role files are siblings of _helpers/, never descendants such as _helpers/entry/models.py.

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

Core Commands

fensu init
fensu check
fensu rule FFS131
fensu map run_plan --depth 3

fensu init detects and validates an onboarding configuration, fensu check enforces the configured architecture, fensu rule explains one rule and its remediation, and fensu map renders a conservative project call tree. Downstream mapping is the default; --direction upstream shows proven callers. Mapping follows project functions and class methods when imports, annotations, constructors, or return types prove the receiver. Unique protocol dispatch can resolve to one concrete implementation; ambiguous protocols and untyped parameters remain visible as unresolved dispatch seams rather than guessed implementations. Mapping does not require Fensu configuration or rule adoption.

fensu check stores disposable evaluation results in a repository-local SQLite database under .fensu/cache/ and reuses them only after validating source, configuration, rule, implementation, and project-query inputs. Caching is enabled by default; set cache.enabled = false in configuration or pass --no-cache for an explicit uncached check. --cache overrides a disabled project preference for one invocation. Deleting .fensu/cache/ is always safe; ignore that directory rather than the complete .fensu/ namespace, which is reserved for other Fensu-owned state.

Enforce It, Then See It

Because Fensu enforces the structure, it can also render it. fensu map produces a deterministic call tree with clickable path:line locations, class-qualified method names, and explicit protocol seams while marking unresolved dynamic calls, depth limits, and cycles. Downstream walks follow proven callees; upstream walks invert those proven edges and omit callers Fensu cannot resolve.

$ fensu map run_map --depth 4

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

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

Philosophy

Fensu 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 = "FFS120"
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. fensu check rejects stale exceptions that no longer suppress a fault.

For a justified rule/path intersection that is broader than one exact finding, keep the rules and project context active with [[rule_ignores]]:

[[rule_ignores]]
rules = ["FFA", "XGENERATED"]
paths = ["src/my_package/generated/**"]
reason = "Generated interfaces are checked by their source schema."

One declaration must match both the finding's rule code and its reported path. Unlike exact exceptions, path-scoped ignores are not stale-checked.

Agent Skills

Generate repository-aware guidance from the active ruleset:

fensu skills
fensu skills --global

The generated skill includes Fensu 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 fensu check, fensu rule, and generated agent skills. Rules can declare typed RuleOption values, repositories can override them under [rule_options.<CODE>], and checks read the validated current value through ctx.option(). Rules can also use ctx.facts, ctx.project, ctx.text, ctx.syntax, and ctx.relations; these are the same backend-neutral analysis zones used by Fensu's built-in rules. Project and filesystem reads made through ctx.project are tracked for cache invalidation. Raw ast.Module access remains available for checks that need unrestricted Python syntax traversal. Semantic fact contracts and author-facing models are public Python APIs, while their production extraction has one native Rust owner; raw AST, syntax, and relation artifacts remain lazy CPython capabilities. Keep project-owned checks in the canonical scripts/fensu_policy/rules/ tooling role and load them explicitly:

tooling = ["scripts"]
rule_paths = ["scripts/fensu_policy/rules"]

See the custom-rule guide for the complete API and configuration.

Documentation

The quickstart, architecture model, configuration reference, adoption guide, and CLI reference live at docs.fensu.dev.

Download files

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

Source Distribution

fensu-0.10.0.tar.gz (413.4 kB view details)

Uploaded Source

Built Distributions

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

fensu-0.10.0-cp312-abi3-win_amd64.whl (3.7 MB view details)

Uploaded CPython 3.12+Windows x86-64

fensu-0.10.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ x86-64

fensu-0.10.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.9 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.17+ ARM64

fensu-0.10.0-cp312-abi3-macosx_11_0_arm64.whl (3.7 MB view details)

Uploaded CPython 3.12+macOS 11.0+ ARM64

fensu-0.10.0-cp312-abi3-macosx_10_12_x86_64.whl (3.8 MB view details)

Uploaded CPython 3.12+macOS 10.12+ x86-64

File details

Details for the file fensu-0.10.0.tar.gz.

File metadata

  • Download URL: fensu-0.10.0.tar.gz
  • Upload date:
  • Size: 413.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fensu-0.10.0.tar.gz
Algorithm Hash digest
SHA256 a3adcf525b6f8b863e8bb20e5dd78c27211b6d7405208db116357cfc66e7f67f
MD5 b0967c13408c494ef2d005929da57de1
BLAKE2b-256 e6c45da98f51e471244edcfe2a4f6b6f692d792e3337665883d2884c3a93f05c

See more details on using hashes here.

Provenance

The following attestation bundles were made for fensu-0.10.0.tar.gz:

Publisher: publish.yml on chio-labs/fensu

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

File details

Details for the file fensu-0.10.0-cp312-abi3-win_amd64.whl.

File metadata

  • Download URL: fensu-0.10.0-cp312-abi3-win_amd64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: CPython 3.12+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fensu-0.10.0-cp312-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 187112ffa85498f6a7caa36996f59a63db8b41d35b9d252c09056922f0acca9b
MD5 506cc6decb47d59615b6c6caf22267cf
BLAKE2b-256 2a830eda17b259dbfb93e9be59b6477e5d21825da130cbb2e14a45848481d2cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for fensu-0.10.0-cp312-abi3-win_amd64.whl:

Publisher: publish.yml on chio-labs/fensu

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

File details

Details for the file fensu-0.10.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for fensu-0.10.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b76cc79ae5d459549c3bbd3bd7c0a7ce4f1743aa1c241d01f98401591f3e2515
MD5 534bf21ad7a9e5923c5f18201b19e1e1
BLAKE2b-256 a4826842234039b8d6c20c8c2ed49cbeac4b524d3a26abc2ffc7e02220d6dfc7

See more details on using hashes here.

Provenance

The following attestation bundles were made for fensu-0.10.0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on chio-labs/fensu

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

File details

Details for the file fensu-0.10.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for fensu-0.10.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 77dd0624619bf2a90ec5710748d4d1c32592fe167ced2144bf42c8ffd30fb30f
MD5 0daa325440931898cb1af310145d5eaa
BLAKE2b-256 eea18b671dd214f162b3a1f8fe5c46d5c01725d79c59fbb73f524fd6b32d8b30

See more details on using hashes here.

Provenance

The following attestation bundles were made for fensu-0.10.0-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on chio-labs/fensu

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

File details

Details for the file fensu-0.10.0-cp312-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for fensu-0.10.0-cp312-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a13d7a1f7fd6485c37d2f8e9c4351e81ab58e5f7e2f28fd9a28191aa05fcf955
MD5 e123f77a20c31032f1507694b0d362db
BLAKE2b-256 dd1ffbeca6042e5c8b7014542af53938ca720bf26e673182bbdd5e75927ff01d

See more details on using hashes here.

Provenance

The following attestation bundles were made for fensu-0.10.0-cp312-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on chio-labs/fensu

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

File details

Details for the file fensu-0.10.0-cp312-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for fensu-0.10.0-cp312-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 3caf52eb57f5d1f41b3d93fdaff63fcc4e9298edc8a9a9b9d0e4cace82d9b85c
MD5 6cec30cf4bf1c425ea845e0a02c3f02c
BLAKE2b-256 f63ca90f1f9155cd0ce4b3728f1a4668258c5d2f480d778d92d3944082a5d737

See more details on using hashes here.

Provenance

The following attestation bundles were made for fensu-0.10.0-cp312-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on chio-labs/fensu

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

Release history Release notifications | RSS feed

0.14.0

6 files

0.13.1

6 files

0.13.0

6 files

0.12.0

6 files

0.11.0

6 files

This release

0.10.0 This release

6 files

0.9.4

6 files

0.9.3

6 files

0.9.2

6 files

0.9.1

6 files

0.9.0

6 files

0.8.1

6 files

0.8.0

6 files

0.7.0

6 files

0.6.0

6 files

0.5.2

6 files

0.5.0

6 files

0.4.1

6 files

0.4.0

6 files

0.3.0

6 files

0.2.1

6 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page