Skip to main content
Every Strata rule belongs to a family. A family groups rules by subject, gives you a single selector to turn the whole group on or off, and carries a letter in each rule’s code. Strata ships seven core families and reserves the X namespace for custom rules.

Rule codes

A core rule code is SF + a family letter + a number, for example SFL001 or SFS131.
  • SF marks a Strata fault and is collision-free with other linters’ prefixes.
  • The third letter is the family.
  • The number is a stable address within the family. The hundreds digit groups a sub-theme (for example SFS0xx are size caps, SFS1xx are dataflow), and the rule’s slug carries the precise meaning.
Codes are permanent. A removed or renamed rule’s code is retired forever and never reused, and existing rules are never renumbered. Custom rules must use the X namespace and are rejected at load if they start with SF.

The families

SelectorFamilySubject
SFLLayersImports, boundaries, dependency direction.
SFRRolesModule taxonomy: what a file may contain and where it lives.
SFSShapeFunction size caps and dataflow discipline.
SFNNamingName-to-behavior contracts.
SFXHygieneClarity and smell: docstrings, comments, raises, decision literals.
SFTTestsTest-suite layout, naming, and parametrization conventions.
SFAAnnotationsType-annotation completeness.
Which families apply depends on the file’s scope: product roots and tooling get the structural families (SFL, SFR, SFS, SFN, SFX, SFA), while the tests scope gets SFT and SFA.

SFL: Layers

The headline family, and the anti-spaghetti core. Layers rules govern which modules and packages may import which, so boundaries stay intact.
CodeSlugChecks
SFL001absolute-imports-onlyRelative imports hide package boundaries.
SFL002no-star-importsStar imports hide names from boundary analysis.
SFL101no-sibling-package-internalsDo not reach into a sibling’s internals; import its published surface.
SFL102no-cross-package-internalsImport only via a package’s public surface.
SFL103no-internal-public-surface-importsInternal code imports concrete owning modules, not the bare package surface.
SFL110no-cross-file-use-of-helper-private-classA _-prefixed class in helpers/ is file-only.
SFL301no-runtime-imports-from-toolingRuntime code must not import from the tooling scope.

SFR: Roles

The largest family, and the clearest separation from tools that only look at the import graph: SFR looks inside modules. It enforces what each role file may contain, where declarations may live, package layout, entry-module shape, and file size. Representative rules:
CodeSlugChecks
SFR001models-only-modelsmodels.py holds only model declarations.
SFR101model-declaration-outside-modelsA model declared outside models.py.
SFR204banned-generic-package-nameNo shared, common, util, misc, and similar in domain positions.
SFR301helpers-package-layouthelpers/ stays flat until a subfolder is warranted.
SFR401entry-module-shapeAn entry file defines one public function and at most two private ones (a limit on functions in the file, not on what they may call). See entry modules.
SFR402init-module-empty__init__.py is docstring-only.
SFR501classes-one-class-per-moduleOne class per module in classes/.
SFR601source-file-line-countA file must not exceed the line-count threshold.
SFR701tooling-entrypoint-shapeA direct tooling script is a thin command adapter.
SFR704rules-role-contentA rules/ module holds only imports and @rule functions.
SFR spans role-file content (SFR0xx), misplaced declarations (SFR1xx), naming (SFR2xx), package layout (SFR3xx), surface shape (SFR4xx), class and helper shape (SFR5xx), file size (SFR6xx), and tooling layout (SFR7xx).

SFS: Shape

Shape rules keep functions small and their dataflow explicit. This is where the LLM-sprawl countermeasures live.
CodeSlugChecks
SFS001too-many-statementsStatement cap on main/ orchestrator functions.
SFS002too-many-distinct-callsDistinct-callee cap on main/ functions.
SFS003too-many-localsAssigned-local cap on main/ functions.
SFS010max-argumentsGlobal argument-count cap on any function.
SFS011max-statements-globalLoose global statement floor on any function.
SFS101meaningful-project-result-discardedA meaningful returned value must be used, not silently dropped.
SFS110default-mutation-returnA function that mutates a parameter must return it.
SFS120keyword-only-argumentsParameters past the positional threshold must be keyword-only.
SFS130no-outer-state-mutationNo rebinding or mutation of module globals or closure captures.
SFS131no-complex-comprehensionsOne generator per comprehension, no nested comprehension.
SFS201mutable-result-modelmodels.py dataclasses must be frozen=True.
SFS102 (parameter-mutation-in-phase-helpers) is the one core rule that is opt-in: it ships disabled and runs only when named explicitly by code. It is the stricter input-immutability stance for teams adopting the full phase model, layered on top of the default SFS110.

SFN: Naming

Naming rules enforce that a name means what it claims. This is the “dishonest functions” family.
CodeSlugChecks
SFN001validator-must-not-returnA validate_* or enforce_* function must not return a meaningful value.
SFN001 checks exactly one thing: a validator raises or passes, so returning a value means it is really a query and should be named is_* or get_*. Extend the name-to-behavior contracts to more prefixes with the contracts table.

SFX: Hygiene

Hygiene rules target clarity and smells that hide meaning.
CodeSlugChecks
SFX001single-line-docstringsDocstrings are a single line.
SFX002no-standalone-commentsNo load-bearing standalone comments.
SFX003no-raw-builtin-raiseRaise structured errors, not bare built-ins.
SFX004no-assert-in-runtimeNo assert in runtime code.
SFX005no-swallowed-exception-probeNo except that silently swallows.
SFX006no-complex-comprehensions-in-toolingThe comprehension readability limit, in tooling.
SFX007no-unnamed-string-decisionsString literals must not directly control comparisons.
SFX008no-magic-numeric-comparisonsNumeric literals other than -1, 0, 1 must not control comparisons.
SFX007 and SFX008 push decisions to compare against named concepts (enum members or named constants) instead of unexplained literals. They report the literal expression and do not infer runtime values.

SFT: Tests

Test rules make a test suite readable and consistent. They apply only in the tests scope. The conventions include a mirrored layout, given-when-then naming, and dataclass-backed parametrization. Representative rules:
CodeSlugChecks
SFT006test-file-nameTest files start with test_.
SFT007test-function-nametest_given_<state>_when_<action>_then_<expectation>.
SFT008dataclass-parametrizeTests use @pytest.mark.parametrize with a dataclass-backed case.
SFT025description-lambda-idsParametrize ids come from lambda case: case.description.
SFT026local-test-types-fileEach test module has a sibling _test_types.py.
SFT028test-layoutTest directories mirror the source tree.
SFT036no-if-in-testsNo conditional control flow inside a test function.
SFT groups layout and mirroring (SFT0xx), module hygiene (SFT1xx), test-role files (SFT2xx), test file and function shape (SFT3xx), and dataclass-backed parametrization (SFT4xx).

SFA: Annotations

Annotation rules require types to be explicit, so they are in the text the reader (and a type checker, and an LLM) can see. SFA runs over both the product and test scopes.
CodeSlugChecks
SFA001parameter-annotationEvery function parameter is annotated.
SFA002return-annotationEvery function has a return annotation.
SFA101module-variable-annotationModule-level variables are annotated.
SFA102class-attribute-annotationClass attributes are annotated.
SFA103local-variable-annotationLocal variables are annotated on first binding.
All five are on by default, including annotate-every-local. See Philosophy for why this stricter stance is deliberate.

Inspecting rules

Look up any rule’s full metadata, including its message and remediation, with strata rule:
strata rule SFS131
Generate agent-facing guidance from your active rules with strata skills.