Skip to main content
RuleContext is the supported interface between a custom rule and Fensu’s analysis engine. It is passed to every rule check as ctx. Import all public protocols, handles, locations, facts, and dependency models from fensu, never from internal package paths.
The context groups its surface into current-file facts, project/tree facts, an architecture graph, text and syntax analysis, fault construction, position helpers, and configuration. Legacy Python callbacks may ignore the context and walk their ast.Module directly when they need unrestricted Python AST access.

ctx.facts

Backend-neutral semantic facts for the current file, computed once and shared by all rules through a single traversal.
evaluate_rule_calls() supports policy analysis such as FFR707. Custom-rule tests should use the public testing harness rather than inspect these facts directly.

ctx.project

Cross-file and filesystem queries. Every call records its input for cache invalidation, so a result stays reusable only while its answers hold. Pass the file whose result depends on the query as requester, normally ctx.path.
Read project state only through ctx.project, never the filesystem directly. Untracked reads break cache correctness and fail cacheability verification.

ctx.project.tree

An immutable, deterministic view of the authoritative discovered project. It does not perform arbitrary filesystem discovery. Every query records its exact answer against the active file or project-rule requester. ProjectPath rejects absolute paths, parent traversal, Windows path spellings, and unsupported path objects. FilePosition includes path, analyzer, source_kind, scope, scope_root, module, package, domain_parts, role, role_depth, is_entry_module, and is_main_module.

ctx.graph

For Python, TypeScript, and Svelte targets, an immutable architecture graph resolved from static imports and authoritative discovered modules. Unresolved imports remain explicit and dynamic imports are not represented. Web graph resolution reuses the native analyzer’s static TypeScript, JavaScript, SvelteKit, and configured alias resolution. ImportEdge preserves the AuthoredImport, source location, resolved module name, ImportResolution, and optional target node. Graph identities include analyzer and source-kind ownership so they remain safe as additional analyzers gain graphs.

ctx.rust

For Rust targets, a versioned immutable workspace fact view transferred from the native analyzer. It contains no syn or Cargo metadata objects. RustItemFact uses RustItemKind and RustVisibility, preserves derives and implementation targets, and carries a stable SourceLocation. RustUseFact preserves authored parts, source and target modules, optional target file and crate, and a RustUseResolution of RESOLVED, CRATE, EXTERNAL, or UNRESOLVED. RustFileFacts.parse_error remains explicit when source cannot be parsed. Each broad or focused call records its answer for subject-aware cache replay. Use a focused file() or crate() lookup when unrelated source or Cargo changes should not invalidate a cached result.

ctx.web

For TypeScript and Svelte targets, a versioned immutable fact view transferred from the native analyzers. It contains no parser-library nodes and does not require Node. WebFileFacts exposes its analyzer, WebSourceKind, WebSourcePurpose, module, source root, scope, authored source, optional parse error, and immutable imports, functions, classes, models, bindings, calls, resources, re-exports, and export counts. WebImportFact.target is a File only when the native analyzer proves a project-local destination. Use ctx.graph for resolved dependency and dependent queries. syntax_handles contains stable Fensu-owned WebSyntaxHandle byte ranges for imports, declarations, calls, resources, re-exports, Svelte scripts, and runes. Pass one to WebFileFacts.text(handle) to retrieve its authored text without receiving an analyzer AST. On Svelte targets, WebFileFacts.svelte is a SvelteFileFacts extension when the file is a component or state module. It records script contexts, recognized top-level runes, component markup, route identity, and state-module identity. TypeScript-only targets do not require this extension. Broad files and focused file() calls record their exact answers for subject-aware cache replay. Tree and graph calls made during the same invocation record their own query-specific dependencies.

ctx.targets

Repository rules use ctx.targets instead of an unqualified target context. all() returns every configured RuleTargetFacts handle in target-name order; named(name) returns one exact handle or None. Each handle exposes its stable Target identity, target-local tree and graph, and the typed python, rust, or web workspace facts owned by its analyzer. repository_path(file_or_path) and repository_location(location) translate target-local evidence before constructing a repository-relative diagnostic. Wrong-analyzer access and access to an analyzer omitted from the active rule’s analyzers fail closed. Repository contexts have no current file, source, ctx.project, or unqualified analyzer zones. Their queries use a distinct repository requester for cache replay. See Repository rules for the complete execution and cache model.

ctx.text, ctx.syntax, ctx.relations

Source text, opaque syntax identities, and syntax relationships for the current file. The zone protocols are FactAnalysis, ProjectAnalysis, TextAnalysis, SyntaxAnalysis, and RelationAnalysis, all exported from fensu. The table above describes the Python analysis zones. Serialized Rust rules use ctx.rust, ctx.project.tree, ctx.source, and stable locations. Serialized web rules use ctx.web, ctx.graph, ctx.project.tree, ctx.source, and WebSyntaxHandle. The generic ctx.syntax and ctx.relations zones are unavailable because parser implementation objects are not part of native-analyzer authoring contracts.

Fault construction

Construct diagnostics through the context so the rule’s code, and optionally its declared message and remediation, are wired automatically. Omitted message and remediation use the rule’s envelope values. During a project-rule invocation there is no current file, source, text, syntax, or file-fact object. path_fault() therefore requires an explicit project-relative path, and fault_for() requires an absolute path confined to the analyzed project. fault_at() accepts a SourceLocation or SourceRange; a bare SyntaxHandle cannot be resolved without a current file analysis. During a repository-rule invocation, path_fault() accepts a repository-relative path and may omit it to report against the repository subject ".". fault_at() accepts a repository-relative SourceLocation; translate a target-local location with target.repository_location(...) first.

Position and roles

Location facts derived from configured roots and scopes, not hardcoded package names.

AST helpers

Convenience operations over the current file’s parsed tree. Prefer ctx.nodes() over ast.walk(module) for module-wide scans so cooperating rules share Fensu’s single traversal.

Configuration

Pass the exact RuleOption[T] declaration from the active rule to preserve the static return type. Requests for undeclared or cross-rule options are authoring errors. Values are immutable and already validated before evaluation.

Public models

All models below are exported from fensu and returned by the calls above. They are frozen dataclasses; inspect exact fields with your editor’s type information. Locations and handles Function and dataflow facts Annotation facts Declaration and reference facts Project and dependency facts Subjects, project tree, and architecture graph Rust facts Python repository facts Web facts Test facts

Custom rules

Authoring, loading, testing, and cacheability.

Configuration

Thresholds and contracts the context resolves.