INW007 package-shape¶
Co robi¶
Sprawdza, gdzie leży kod, względem kształtów z [[tool.inwards.shape]] i reguł nazw z [[tool.inwards.names]]. Zgłasza:
- element pakietu z kształtem, którego nie obejmuje
allow(błąd albo ostrzeżenie przyextra = "warning") albo który wymieniaforbid(błąd); - element, którego nazwa pasuje do wzorca z
[[tool.inwards.names]], w dowolnym miejscu poza pakietamionly-intej reguły (błąd); - selektor kształtu, do którego nie pasuje żaden pakiet (ostrzeżenie, w
pyproject.toml).
Diagnostyka wskazuje linię 1 pliku elementu. Kształt obejmuje bezpośrednie elementy pakietu: moduły, zaślepki i podpakiety. Komunikat podaje element i pakiet, nigdy listę dozwolonych, więc wpis w baseline przetrwa zmianę allow; poprawka wymienia dozwolone elementy i wskazuje prawdopodobny cel. Drugi kierunek, brakujący wymagany element, zgłasza INW008.
Dlaczego to źle¶
Reguły importów nie widzą, gdzie leży kod. Agent, któremu potrzebna jest mała funkcja, dodaje helpers.py obok service.py, pakiet services/ obok service.py albo test_orders.py w aplikacji. Każda z tych zmian przechodzi reguły warstw i każda odsuwa kod od układu, na który umówił się zespół, edycja po edycji. INW007 blokuje edycję, która tworzy plik, i mówi, gdzie kod powinien trafić.
Przykład¶
Warstwy z INW001, z kształtem pakietu aplikacji i regułą, która trzyma testy w tests/:
[tool.inwards]
ignore = ["tests"]
layers = [
{ name = "domain", modules = ["shop.domain"] },
{ name = "application", modules = ["shop.application"] },
{ name = "infrastructure", modules = ["shop.infrastructure"] },
]
[[tool.inwards.shape]]
packages = ["shop.application"]
allow = ["place", "cancel", "utils"]
[[tool.inwards.names]]
pattern = "test_*"
only-in = ["tests", "tests.**"]
Agent dodaje moduł z funkcjami pomocniczymi:
shop/application/helpers.py:1:1: INW007 "helpers.py" is not an allowed member of package "shop.application".
fix: Move the code in "helpers.py" into utils.py.
1. Move the code into shop/application/utils.py and delete helpers.py.
2. Package "shop.application" may hold: place, cancel, utils.
3. Don't edit [tool.inwards] yourself. If the package really needs this member, ask the user to change its [[tool.inwards.shape]].
docs: https://sircypkowskyy.github.io/inwards/rules/INW007/
Po poprawce kod trafia tam, gdzie wskazuje kształt.
Plik testów w aplikacji łamie regułę nazw:
shop/application/test_place.py:1:1: INW007 "test_place.py" matches the name pattern "test_*", which doesn't belong in package "shop.application".
fix: Move "test_place.py" under tests/.
1. Move it into a package matching tests, tests.**, for example tests/.
2. Don't edit [tool.inwards] yourself. If the package really needs this member, ask the user to change [[tool.inwards.names]].
docs: https://sircypkowskyy.github.io/inwards/rules/INW007/
Jak naprawić¶
- Przenieś kod do elementu, który wskazuje poprawka. Jest on wybierany przez wbudowane synonimy (
helpers→utils,services→service,test_*→tests), dopasowanie końcówki (order_service→service) albo małą odległość edycyjną (rooter→router). - Usuń niedozwolony plik.
- Jeśli pakiet naprawdę potrzebuje tego elementu, użytkownik zmienia jego
[[tool.inwards.shape]]albo[[tool.inwards.names]]. Agent pyta; config guard nie pozwala mu edytować tabeli.
Bezpieczeństwo poprawki¶
Brak automatycznej poprawki (autofix: false). Przeniesienie kodu między modułami zmienia jego ścieżkę importu, więc każdy moduł importujący musi za nią pójść, a wynik powinien sprawdzić człowiek.
Konfiguracja¶
Poziom: błąd; extra = "warning" w kształcie zmienia elementy nieobjęte przez allow w ostrzeżenia. Selektory, wzorce elementów i reguły nazw opisuje Kształt pakietu. [tool.inwards.rules] może wyłączyć regułę albo zmienić ją w ostrzeżenie (Konfiguracja reguł). INW007 nie da się wyciszyć w linii: dotyczy drzewa pakietów, a miejscem na zmianę jest konfiguracja kształtu (ADR-028).
W hookach Claude Code INW007 blokuje edycję, gdy plik jest nowy w tej sesji. Dla pliku, który istniał na starcie sesji, jest tylko kontekstem, więc stary układ nigdy nie blokuje niezwiązanej edycji; Stop gate też na nim nie blokuje.
Write, który utworzyłby niedozwolony plik .py albo .pyi, jest odrzucany, zanim plik powstanie (shape guard, #96), z tym komunikatem i poprawką jako powodem. Agent zapisuje więc kod od razu we właściwym elemencie pakietu, zamiast tworzyć plik, a potem przenosić go i usuwać przez Bash. Tak samo działa to we wtyczce OpenCode, której write przechodzi przez ten sam hook. Każda odmowa liczy się do escalate-after tak jak INW007, któremu zapobiega, a ta, która osiąga limit, każe agentowi zapytać użytkownika. Edycje istniejących plików i pliki utworzone przez Bash są sprawdzane po fakcie, przez hook edycji i Stop gate. Jeśli sam guard zawiedzie, Write przechodzi, a plik wyłapią te sprawdzenia.
Znane ograniczenia¶
- Nazwane szablony, warstwy ról i
inwards init --style fastapisą otwarte w #97. - Tylko
Write, który tworzy plik, jest odrzucany, zanim plik powstanie. Plik utworzony przez Bash albo niedozwolony element powstały przez edycję jest zgłaszany po fakcie, przez hook edycji i Stop gate. - Liczą się tylko pliki i pakiety Pythona, i tylko to, gdzie leżą, a nie ich zawartość.