Przejdź do treści

Reguły

Każda diagnostyka Inwards linkuje do strony swojej reguły w tej sekcji: linia docs: w wyjściu tekstowym, pole docs w wyjściu JSON, helpUri w SARIF i link przy kodzie w edytorze. Każda strona mówi, co reguła zgłasza, dlaczego ma to znaczenie, gdy kod pisze agent AI, pokazuje przykład zgłoszony i poprawiony, opisuje, jak naprawić diagnostykę i jak skonfigurować regułę, oraz czego reguła jeszcze nie wyłapuje.

inwards rule INW001 wypisuje stronę reguły w terminalu, bez sieci, z pliku binarnego, którego używasz, a inwards rules pokazuje, które reguły włącza twoja konfiguracja, z jakim poziomem i dlaczego (konfiguracja).

Przeszukaj reguły, przefiltruj je według kategorii, statusu albo poprawki automatycznej, posortuj według kodu, nazwy, kategorii albo statusu i przeglądaj je stronami. Pasek adresu zapamiętuje widok, więc link otwiera tę samą listę. Kategoria obejmuje swoje podkategorie: imports pokazuje też reguły z imports › layers.

Reguły dzielą się na sześć kategorii, opisanych niżej w tej kolejności: importy, układ, role, integralność, diagramy i FastAPI. Każda kategoria ma też własną stronę, a nawigacja pokazuje pod nią jej reguły: importy, układ, role, integralność, diagramy i FastAPI. Reguła jest albo domyślnie włączona, albo opt-in. Presety inwards init --style zostawiają każdą domyślnie włączoną regułę, tylko django i fastapi wyłączają INW002, i włączają niektóre reguły opt-in jako ostrzeżenia: INW014, INW015 i INW020 w clean i hexagonal, INW012, INW013, INW016 i reguły FAPI w fastapi.

Importy

Co moduł może importować: warstwy wskazują do środka, konteksty importują się nawzajem tylko tak, jak to zadeklarowano, i tylko przez swoje moduły publiczne, bez cykli, bez biblioteki, której zabrania konfiguracja warstwy, bez własnego modułu, który nie istnieje, bez importu dynamicznego, który ukrywa któreś z tych naruszeń, i bez sięgania do modułu prywatnego spoza jego pakietu. Każda reguła z tej kategorii jest domyślnie włączona poza INW021, która jest opt-in.

Kod Nazwa Co zgłasza Włączona Poziom Włącza ją preset Wyciszenie w linii
INW001 layer-dependency Import z warstwy wewnętrznej do zewnętrznej domyślnie błąd wszystkie tak
INW002 context-independence Import z jednego kontekstu ograniczonego do innego, którego nie deklaruje jego depends-on domyślnie błąd wszystkie oprócz django, fastapi tak
INW003 public-api-only Import niepublicznego modułu kontekstu spoza tego kontekstu domyślnie błąd wszystkie tak
INW004 import-cycles Moduły albo konteksty ograniczone, które importują się nawzajem w cyklu (przy sprawdzaniu całego projektu) domyślnie błąd wszystkie nie
INW005 pure-domain Import biblioteki, na który konfiguracja warstwy nie pozwala, np. SQLAlchemy w domenie domyślnie błąd wszystkie tak
INW010 unknown-first-party Import własnego modułu, który nie istnieje domyślnie błąd wszystkie tak
INW011 dynamic-import Import dynamiczny, który sięga do warstwy zewnętrznej albo którego celu Inwards nie umie odczytać domyślnie błąd wszystkie tak
INW021 private-module Import modułu, którego nazwa zaczyna się od _, spoza pakietu, który go zawiera opt-in błąd vertical-slices tak

Układ

Gdzie leży kod: każdy własny moduł w jakiejś warstwie i każdy pakiet w kształcie, który nadaje mu konfiguracja. Domyślnie włączone.

Kod Nazwa Co zgłasza Włączona Poziom Włącza ją preset Wyciszenie w linii
INW006 unassigned-module Własny kod poza wszystkimi warstwami i prefiksy warstw, do których nie pasuje żaden moduł domyślnie błąd, część diagnostyk to ostrzeżenia wszystkie tak
INW007 package-shape Element pakietu, na który jego kształt nie pozwala, albo nazwa poza pakietami, do których należy domyślnie błąd, część diagnostyk to ostrzeżenia wszystkie nie
INW008 missing-member Brakuje elementu, którego wymaga kształt pakietu domyślnie błąd wszystkie nie

Role

Co robi kod w modułach danej roli: endpointy przekazują pracę dalej, w async def nie ma blokujących wywołań, porty są abstrakcyjne, adaptery deklarują swój port, jest jeden korzeń kompozycji, nazwy ORM trzymają konwencję. Te reguły oceniają kod według wyborów zespołu, więc są opt-in; szablon może włączyć regułę dla roli, a niektóre presety też je włączają.

Kod Nazwa Co zgłasza Włączona Poziom Włącza ją preset Wyciszenie w linii
INW012 thin-endpoint Endpoint HTTP, który sam wykonuje pracę: za dużo instrukcji, gałęzi albo pętli, własne wywołania bazy danych albo HTTP, albo brak wywołania warstwy, którą wskazuje delegate-to opt-in ostrzeżenie fastapi tak
INW013 async-blocking Synchroniczne wywołanie bazy danych, cache'u albo chmury wewnątrz async def: synchroniczny Session SQLAlchemy, redis.Redis, boto3 albo blokujący sterownik opt-in błąd fastapi tak
INW014 ports-abstract Klasa w module portu, która nie jest ABC ani Protocol, albo metoda portu, której ciało wykonuje pracę opt-in błąd clean, hexagonal tak
INW015 construct-only-in Moduł spoza korzenia kompozycji, który w czasie działania importuje adapter wyjściowy albo inną chronioną rolę lub buduje jedną z jej klas opt-in błąd clean, hexagonal tak
INW016 orm-naming Nazwa tabeli, która nie jest w lower_case_snake i w liczbie pojedynczej, albo kolumna daty i czasu bez _at lub _date; uruchomienie dla całego projektu wymaga też konwencji nazw w MetaData opt-in błąd fastapi tak
INW020 adapter-conformance Publiczna klasa w module adaptera bez linii _: Port = Adapter(...) pod if TYPE_CHECKING:, taka linia wykonywana przy imporcie albo linia, której adnotacja nie nazywa żadnego portu opt-in błąd clean, hexagonal tak

Integralność

Reguły, które pilnują uczciwości samego sprawdzenia: plik, którego zadeklarowane kodowanie może ukryć import, i wyciszenie w linii, które niczego nie ukrywa. Domyślnie włączone; INW000 nie da się wyłączyć.

Kod Nazwa Co zgłasza Włączona Poziom Włącza ją preset Wyciszenie w linii
INW000 unsupported-encoding Zadeklarowane kodowanie źródła, w którym komentarz może być prawdziwym importem domyślnie błąd wszystkie nie
INW009 suppression-comment Wyciszenie w linii, które niczego nie ukrywa albo nie pasuje do żadnej diagnostyki domyślnie błąd, nieużywane to ostrzeżenia wszystkie nie

Diagramy

Diagramy architektury w dokumentacji, sprawdzane względem [tool.inwards] (ADR-045). Opt-in i jako ostrzeżenia, żeby rozjazd dokumentacji nie blokował agenta.

Kod Nazwa Co zgłasza Włączona Poziom Włącza ją preset Wyciszenie w linii
INW017 diagram-unknown-name Węzeł w oznaczonym diagramie architektury w Mermaid albo PlantUML, który nie nazywa żadnej zadeklarowanej warstwy ani kontekstu, albo którego etykieta w cudzysłowie (moduł aliasu w PlantUML) nie pasuje do żadnego modułu opt-in ostrzeżenie żaden nie
INW018 diagram-forbidden-edge Ciągła strzałka w oznaczonym diagramie architektury w Mermaid albo PlantUML, która rysuje import zabroniony przez konfigurację: na zewnątrz albo między warstwami sąsiednimi, między kontekstami bez depends-on albo do modułu, który nie jest public opt-in ostrzeżenie żaden nie
INW019 diagram-missing-dependency Import w kodzie między dwiema warstwami albo kontekstami, które rysuje oznaczony diagram architektury w Mermaid albo PlantUML, bez strzałki (dla warstw: bez ścieżki strzałek), która by go rysowała (sprawdzenie całego projektu) opt-in ostrzeżenie żaden nie

Reguły FastAPI

Rodzina FAPI sprawdza aplikacje FastAPI między plikami: który router aplikacja dołącza, jakie kody błędów deklaruje schemat OpenAPI (ADR-037). Każda reguła FAPI jest opt-in i żadna nie zgłasza tego, co już zgłaszają reguły FAST Ruffa. W nowym projekcie inwards init --style fastapi włącza je wszystkie jako ostrzeżenia.

Kod Nazwa Co zgłasza Włączona Poziom Włącza ją preset Wyciszenie w linii
FAPI001 endpoint-metadata Operacja ścieżki bez metadanych OpenAPI, których wymaga projekt (podsumowanie, model odpowiedzi, kod statusu) opt-in błąd fastapi tak
FAPI002 undocumented-error-response Operacja ścieżki, która może zwrócić kod błędu (rzucony bezpośrednio, w funkcji pomocniczej albo zależności, albo przez handler wyjątków aplikacji) niezadeklarowany w responses= opt-in błąd fastapi tak
FAPI003 router-wiring APIRouter z trasami, którego nie dołącza żadna aplikacja, routery dołączające się nawzajem w cyklu albo include_router nad trasami dołączanego routera opt-in błąd fastapi tak
FAPI005 route-shadowing Operacja ścieżki, na którą wcześniejsza z tą samą metodą już odpowiada: /users/{id} nad /users/me albo ta sama metoda i ścieżka dwa razy opt-in błąd fastapi tak
FAPI006 lifespan-events Przestarzały handler on_event albo on_startup= (ostrzeżenie) oraz taki obok lifespan=, przez który FastAPI go pomija (błąd) opt-in błąd fastapi tak
FAPI007 yield-dependency-swallows Zależność z yield, której klauzula except wokół niego nie rzuca wyjątku ponownie, więc błąd endpointu jest ukryty opt-in błąd fastapi tak
FAPI008 duplicate-operation-id Dwie operacje ścieżki jednej aplikacji z tym samym literalnym operation_id opt-in błąd fastapi tak
FAPI009 depends-called Depends(get_db()): zależność jest wywoływana przy imporcie zamiast zostać przekazana opt-in błąd fastapi tak

Jeden kod jest zarezerwowany i niezarejestrowany (nieznany kod nadal jest błędem konfiguracji):

Kod Nazwa Zgłoszenie Status
FAPI004 unhandled-exception #185 nieużywany: przebieg na korpusie w spike'u wypadł na nie (precyzja 3% dla zadeklarowanych klas wyjątków, 4 prawdziwe trafienia w 25 aplikacjach dla zgłaszanych)

Katalog reguł w rozdziale 3 wymienia każdą regułę razem z resztą projektu.

Konfiguracja reguł

Tabela [tool.inwards.rules] w pyproject.toml określa, które reguły zgłaszają i jak głośno (ADR-027):

pyproject.toml
[tool.inwards.rules]
ignore = ["INW007", "INW008"]      # these rules never report
severity = { INW005 = "warning" }  # reported, but doesn't fail a check or block the agent
# extend-select = [...]           # turn opt-in rules on, next to the defaults
# select = ["INW001", "INW011"]    # or: only these rules report (default: every rule that is on by default)

Kody są dokładne, nie są prefiksami, a nieznany kod to błąd konfiguracji (kod wyjścia 2). ignore wygrywa z select i extend-select. Reguła ustawiona na "warning" pojawia się w każdym formacie, ale nie zmienia kodu wyjścia, nie blokuje hooków i nie trafia do baseline'u. INW000 nie da się wyłączyć ani obniżyć. Tabela jest częścią [tool.inwards], więc config guard nie pozwala agentowi jej edytować.

Reguły opt-in i opcje reguł

Reguła, która w kolumnie Włączona ma „opt-in”, nie zgłasza niczego, dopóki jej nie włączysz: wpisz jej kod do extend-select, co zostawia pozostałe reguły bez zmian, albo do select. Reguły INW są domyślnie włączone, poza INW012, INW013, INW014, INW015, INW016, INW017, INW018, INW019, INW020 i INW021; opt-in są reguły, które oceniają kod według progów wybranych przez zespół, takie jak INW012, reguły, które oceniają zawartość modułów danej roli, takie jak INW013, INW015, INW016 i INW020, reguły, które sprawdzają dokumentację względem konfiguracji i kodu, INW017, INW018 i INW019, konwencja, której trzymają się tylko niektóre projekty, INW021, oraz rodziny reguł dla frameworków, takie jak FastAPI. SARIF wymienia regułę opt-in z defaultConfiguration.enabled ustawionym na false.

Opcje reguły trafiają do tabeli nazwanej jak reguła, [tool.inwards.rules.<rule-name>]. Niektóre reguły mają też własne opcje, opisane na ich stronach (INW012, INW013, INW014, INW015, INW016, INW020, INW021, FAPI001, FAPI002). Każda reguła przyjmuje modules, listę prefiksów modułów albo selektorów zapisanych jak w modules warstwy (shop.domain obejmuje ten pakiet i wszystko pod nim, shop.*.api używa symboli wieloznacznych): reguła zgłasza wtedy tylko w modułach, które pasują. Nieznany klucz albo zły typ to błąd konfiguracji, który podaje nazwę klucza.

pyproject.toml
[tool.inwards.rules.pure-domain]
modules = ["shop.domain"]  # INW005 reports only in shop.domain and below

Reguły treści opt-in, INW012 do INW016, INW020 i reguły FAPI, oraz INW021 pomijają moduły testowe: moduł, którego ostatni segment nazwy to test_* albo *_test, albo który ma segment tests, test lub conftest, na przykład app/tests/test_orders.py, tests/conftest.py czy app/orders_test.py. Test celowo buduje jednorazową aplikację albo importuje adapter, więc zgłoszenie w nim rzadko warto poprawiać (ADR-037). check-tests = true w tabeli reguły przywraca je dla tej reguły. Pozostałe reguły sprawdzają moduły testowe jak każde inne, a ignore najwyższego poziomu nie zawęża reguł treści.

pyproject.toml
[tool.inwards.rules.async-blocking]
check-tests = true  # INW013 also reports in tests/ and test_*.py

Szablon może włączyć regułę opt-in dla jednej ze swoich ról, na przykład router = { async-blocking = true }; rozwija się to do extend-select i tabeli opcji reguły, z modułami roli w modules.

Tabela opcji nie włącza reguły. Tabela dla reguły wyłączonej (opt-in i niewybranej albo wymienionej w ignore) nic nie robi, więc inwards check zgłasza ostrzeżenie przy tej tabeli w pyproject.toml, z kodem reguły. Dzięki temu zespół może przygotować opcje reguły, zanim ją włączy.

Żeby na stałe zaakceptować jedną diagnostykę, dodaj wyciszenie w linii, na którą wskazuje, z kodem reguły i powodem (ADR-028):

shop/domain/order.py
from shop.infrastructure.legacy import LegacyClient  # inwards: ignore[INW001] reason="old billing adapter, removed in #210"

W ten sposób można wyciszać reguły, które wskazują na linię kodu Pythona: INW001, INW002, INW003, INW005, INW006, INW010, INW011, INW012, INW013, INW014, INW015, INW016, INW020, INW021 oraz reguły FAPI. Wyciszenie bez powodu, z nieznanym kodem albo z kodem, którego nie da się wyciszyć, niczego nie ukrywa i jest zgłaszane jako INW009. Hooki Claude Code pomijają wyciszenie, które agent dodał w trakcie sesji, chyba że ustawiono agent-suppressions = "allow".

Format strony

Każda strona reguły zaczyna się od front matter w formacie OKF: type: rule, title, description, resource (plik źródłowy reguły), tags (kategoria, najpierw nadrzędna), timestamp (kiedy reguła trafiła do kodu), status oraz pola Inwards code, name, severity, suppressible, autofix i related_issues. Każda strona ma też sekcje Co robi, Dlaczego to źle, Przykład (zgłoszony i poprawiony blok kodu) i Bezpieczeństwo poprawki oraz wiersz w tabeli na tej stronie i na stronie swojej kategorii (rules/<category>/), a także wpis w grupie swojej kategorii w nawigacji, w docs/zensical.toml i docs/zensical.pl.toml. uv run scripts/check-rule-pages.py sprawdza to wszystko w obu językach, także w CI, i zapisuje plik rules.json, z którego czyta lista powyżej; po zmianie strony reguły albo wiersza tutaj uruchom go z --write. Test sprawdza, że każda zarejestrowana reguła ma stronę, a każda strona wskazuje zarejestrowaną regułę. Przykłady zgłoszone i poprawione też działają jako testy, sprawdzane względem wyjścia pokazanego na stronie.