Skip to main content
Repository rules enforce contracts that span more than one configured analyzer target. They are ordinary Python-authored custom rules, but receive one immutable Repository subject and explicit named target handles instead of a current file or one target-local project. Use them for policies such as a backend contract agreeing with a frontend client, shared schema ownership, or repository-wide dependency direction. A matching name in two targets is not evidence of a relationship: compare only facts and resolved edges that Fensu exposes.

Execution model

A repository rule runs exactly once during an aggregate all-target check:
It does not run during a target-scoped check:
Skipping is deliberate. A repository rule never receives a partial target universe. Target-local file and project rules continue to run normally in the same aggregate check, and all findings are merged into one deterministic report. Fensu collects target facts only when a repository rule is selected. Native Rust, TypeScript, and Svelte discovery, parsing, and built-in checks stay native; web analysis remains Node-free. Fensu starts the bounded Python host only when an active repository rule requires it.

Configure repository policy

Repository rules require at least two explicit named targets. Configure their policy once in the top-level [repository_rules] table, not inside any target:
The table supports the normal custom-rule rule_paths, rule_modules, select, warn, ignore, rule_options, rule_ignores, rule_exceptions, cache, and skills keys. Paths in repository exceptions and diagnostics are relative to the repository configuration, not to an individual target. Repository exceptions are file-level; they do not accept symbols. An active rule is applicable only when the configured registry contains every analyzer declared by that rule. Inspect its resolved subject, analyzer capabilities, options, and policy normally:
Generated project skills include the same repository-rule metadata and explain the aggregate-only execution contract.

Author a repository rule

Declare the Repository subject and all analyzer capabilities the rule reads:
The callback shape is:
The Repository value is the stable identity of the aggregate subject. Read target state only through ctx.targets; repository contexts do not expose a current file, source, ctx.project, ctx.graph, or unqualified analyzer facts.

Target handles

ctx.targets.all() returns every configured target in stable name order. ctx.targets.named(name) returns one exact target or None. Each RuleTargetFacts handle exposes: Target tree, graph, and analyzer facts use target-local paths. Always translate a path or location before reporting it. Accessing facts for the wrong analyzer, or an analyzer omitted from the rule’s analyzers, fails closed rather than returning an ambiguous value. Python targets expose PythonWorkspaceFacts.files and focused PythonWorkspaceFacts.file(...) lookups. Each PythonFileFacts contains the immutable file identity, source, and Fensu-owned fact, text, syntax, and relation zones. Rust and web targets use the same workspace models described in the RuleContext reference.

Diagnostics

Repository findings are repository-relative. Use one of the translated values:
ctx.path_fault() without a path reports against the repository subject ".". As with other custom rules, warnings do not affect the exit code, blocking findings produce exit code 1, and configuration or host errors produce exit code 2.

Cache behavior

Repository rules use a distinct cache namespace and requester. Cache identity always includes the complete deterministic target registry: target names, analyzers, and roots, together with rule implementation and repository policy. Adding, removing, renaming, relocating, or changing the analyzer of a target invalidates the result. Within a stable registry, invalidation follows the queries the rule actually made:
  • focused python.file(...), rust.file(...), web.file(...), tree, and graph lookups survive unrelated edits;
  • broad files, tree.paths, graph.nodes, and similar inventory queries invalidate when their complete answer changes.
Cached and uncached diagnostics and exit codes are identical. --cache-stats includes repository-rule hits, misses, invalidations, writes, and non-cacheable results in the aggregate counters.

Custom rules

Rule metadata, options, loading, testing, and cacheability.

RuleContext

Target-local tree, graph, and analyzer fact contracts.

fensu check

Aggregate checks, target selection, diagnostics, and exit codes.

Caching

Persistent cache correctness and invalidation.