Skip to content

INW008 missing-member

What it does

Reports a package that lacks a member its [[tool.inwards.shape]] lists in require. The finding sits on the package's __init__.py, or on its first file when it has none. require = ["__init__", "router", "service"] means every package the shape selects has an __init__.py, a router module or subpackage and a service one; service.py means a module only, service/ a subpackage only. INW007 reports the other direction, members that shouldn't be there.

Why is this bad

A required member is usually the entry point others rely on: the composition root, a package's router, its public service. An agent can remove one without touching an import, by merging it into another module or deleting it through Bash, and nothing else fails until runtime. INW008 catches it at the end of the turn, and in the editor as soon as the file is gone.

Example

The layers from INW001, with a shape that requires a place module in the application package:

pyproject.toml
[tool.inwards]
layers = [
  { name = "domain",         modules = ["shop.domain"] },
  { name = "application",    modules = ["shop.application"] },
  { name = "infrastructure", modules = ["shop.infrastructure"] },
]

[[tool.inwards.shape]]
packages = ["shop.application"]
allow = ["cancel"]
require = ["place"]

An agent folds place.py into a new cancel.py and deletes it:

shop/application/cancel.py
import shop.domain.order
rm shop/application/place.py
inwards check
shop/application/cancel.py:1:1: INW008 Package "shop.application" has no "place" member, which its shape requires.
  fix: Create shop/application/place.py, or put back the one that was removed.
    1. If place.py was moved, renamed or deleted, restore it in shop/application/.
    2. Otherwise create shop/application/place.py for this package's place code.
    3. Don't edit [tool.inwards] yourself. If the package really doesn't need it, ask the user to change `require` in its [[tool.inwards.shape]].
  docs: https://sircypkowskyy.github.io/inwards/rules/INW008/

Fixed: the required module is back.

shop/application/place.py
import shop.domain.order
inwards check

How to fix

  1. If the member was moved, renamed or deleted, restore it.
  2. Otherwise create it, with the code the package's role says belongs there.
  3. If the package really doesn't need the member, the user changes require in its shape. An agent asks; it doesn't edit [tool.inwards].

Fix safety

No automatic fix (autofix: false). Restoring a file is easy to automate, but only a person or the agent that removed it knows which content is right.

Configuration

Severity error. Package shape documents require and the selectors. [tool.inwards.rules] can turn the rule off or make it a warning (Configure rules). INW008 can't be suppressed inline: it is about the package tree, and the shape config is the place to change it.

In the Claude Code hooks, INW008 for the edited file's package is always context, never a block: an agent creating router.py in a new package hears that service.py is still missing and can add it next. The Stop gate blocks on INW008 findings that are new since the session started, so deleting a required file through Bash is caught. A package that already lacked the member at session start doesn't block, unless stop-gate = "project" is set: then every INW008 finding the baseline doesn't accept blocks, old or new.

Known limitations

  • inwards server reports INW008 from its whole pass, which runs when the editor starts it, on every save and when files are created or deleted, not as you type.
  • Named templates, role layering and inwards init --style fastapi are open in #97.

References