feat(conventions): modularity checks + scan-derived, language-aware rules

The standard was architectural LINTING (placement + hygiene) — things ruff/eslint already do — and it forced backend rules onto frontend projects. This makes it enforce MODULARIZATION, the separation-of-concerns a senior demands that linters are blind to:

- modular_cohesion: a file that mixes architectural concerns (a model defined in a router, a schema in a component) is a monolith — split it. One concern per file.
- thin_routes (Python): a route handler that runs its own DB access instead of delegating to a service.
- thin_components (TypeScript/React): a component that fetches data in its body instead of using a hook.
- god_class: a class past a method-count threshold (single responsibility).

The checks inspect a definition's BODY and a file's COMPOSITION via tree-sitter, precision-over-recall (fire only on a confident structural signal). Rules are now scan-derived and language-aware: hygiene seeds universally, placement only for modules that exist, and modularity per stack — so a frontend project carries no_models_in_components + thin_components, never a backend no_models_in_routers. BUILTIN_RULES is reduced to language-agnostic hygiene.
This commit is contained in:
Renn F
2026-06-22 14:26:25 +02:00
parent 63b6114364
commit 4a8c508f91
7 changed files with 533 additions and 14 deletions
@@ -81,11 +81,13 @@ def test_unknown_definition_kind_in_forbidden_raises() -> None:
)
def test_builtin_rules_cover_the_org_defaults() -> None:
assert BUILTIN_RULES["no_models_in_routers"] == "block"
assert BUILTIN_RULES["no_helpers_in_routers"] == "block"
def test_builtin_rules_are_language_agnostic_hygiene_only() -> None:
# BUILTIN_RULES are the universal hygiene defaults; placement / modularity
# rules are derived per project from the scan, never seeded universally.
assert BUILTIN_RULES["no_lint_suppressions"] == "block"
assert BUILTIN_RULES["no_inline_comments"] == "warn"
assert "no_models_in_routers" not in BUILTIN_RULES
assert "no_helpers_in_routers" not in BUILTIN_RULES
def test_models_construct_directly() -> None:
@@ -14,8 +14,10 @@ from roboco.foundation.policy.conventions.models import (
def test_effective_map_applies_builtin_rules_when_file_absent() -> None:
eff = effective_map(ConventionsStandard(), None)
assert eff.rules["no_models_in_routers"].level == "block"
assert eff.rules["no_lint_suppressions"].level == "block"
assert eff.rules["no_inline_comments"].level == "warn"
# Placement / modularity rules are derived per project, not universal.
assert "no_models_in_routers" not in eff.rules
def test_file_module_overrides_derived_by_path() -> None: