Skip to content

INW017 diagram-unknown-name

What it does

Opt-in, and a warning when on. Reads the Mermaid and PlantUML architecture diagrams in the files the diagrams key lists and reports a name in them that [tool.inwards] or the code doesn't know, on the diagram's own line. A whole-project run (inwards check without paths, or the Stop gate with stop-gate = "project") checks them all; the per-edit hook, inwards server, MCP check_files and inwards check FILE check a listed diagram when it is the file edited or named (where it runs).

A diagram counts only when it is a flowchart or graph that opens with a marker comment, before or right after the header:

  • %% inwards: layers: every node id is a layer name.
  • %% inwards: contexts: every subgraph at the top level, and every node outside a subgraph, is a context name. The nodes inside a subgraph can have any id.

A layer or context whose name can't be a Mermaid id, such as a template's slices.domain or a name with a space, is named by a node with any id whose quoted label is one of its modules entries: slices_domain["shop.domain"]. A name that can be an id must be the id, so a typo in it is still reported.

In both kinds, a label in double quotes is a module prefix or a layer selector (shop.domain, shop.*.domain), and must match a module that exists. A label without quotes, or one with spaces, is a caption and isn't checked. A node with the class external (db:::external, or class db external) stands for something outside [tool.inwards], such as a database or a third-party API, and is skipped.

What is reported, once per name and diagram, at its first mention:

  • a node or subgraph id that names no declared layer or context, with the closest declared name when it looks like a typo;
  • a quoted label that matches no module, with the closest module prefix;
  • a diagrams entry that matches no file, on the entry in pyproject.toml.

Mermaid blocks without the marker are ordinary docs and are never read. The arrows are INW018's: it compares them with the layer order, depends-on and public.

Why is this bad

Teams draw their architecture in the README or a docs page, and GitHub, Zensical and most editors render Mermaid in Markdown. Nothing ties the drawing to the code: a layer gets renamed, a package moves, and the diagram keeps showing the old picture. New contributors, and agents that read the docs before they write code, then work from a map that is wrong. An agent asked to "follow the architecture in docs/architecture.md" will put code in a services layer that the config calls application.

[tool.inwards] stays the only source of truth (ADR-045): the diagram is checked against it and the code, never read as config. A finding means the picture drifted; the fix is to redraw it, or to ask the user whether the config should change.

Example

pyproject.toml
[tool.inwards]
diagrams = ["docs/*.mmd"]
layers = [
  { name = "domain", modules = ["shop.domain"] },
  { name = "application", modules = ["shop.application"] },
  { name = "infrastructure", modules = ["shop.infrastructure"] },
]

[tool.inwards.rules]
extend-select = ["INW017"]
shop/domain/order.py
class Order:
    pass
shop/application/place_order.py
from shop.domain.order import Order
shop/infrastructure/db.py
from shop.domain.order import Order

A diagram that calls the infrastructure layer infra and misspells the domain package:

docs/layers.mmd
%% inwards: layers
flowchart LR
  infra --> application --> domain["shop.domian"]
  infra --> postgres[("Postgres")]:::external
inwards check
docs/layers.mmd:3:3: INW017 (warning) Node "infra" in this layers diagram names no layer: the layers are "domain", "application" and "infrastructure".
  fix: Rename "infra" or mark it :::external.
    1. Rename the node to the layer it shows.
    2. If it stands for something outside [tool.inwards] (a database, a third-party service), mark it infra:::external.
    3. If the diagram is right and [tool.inwards] is missing it, ask the user; don't edit [tool.inwards] yourself.
  docs: https://sircypkowskyy.github.io/inwards/rules/INW017/

docs/layers.mmd:3:29: INW017 (warning) "domain" is labelled "shop.domian" in this layers diagram, and no module matches "shop.domian".
  fix: Change the label to "shop.domain".

Fixed: the node has the layer's name and the label the package's. The whole project passes:

docs/layers.mmd
%% inwards: layers
flowchart LR
  infrastructure --> application --> domain["shop.domain"]
  infrastructure --> postgres[("Postgres")]:::external
inwards check

How it works

The reader is a hand-written line parser in src/core/src/diagram/; Mermaid's own parser needs a browser DOM. It reads the flowchart syntax the Mermaid docs describe:

  • Where diagrams are. In a .mmd or .mermaid file, the whole file. In any other listed file, each fenced block whose info string is mermaid (backticks or tildes, as CommonMark reads them). YAML front matter and %%{init: ...}%% directives before the header are skipped. PlantUML component diagrams (.puml files and fenced plantuml blocks, marked ' inwards: layers or ' inwards: contexts) go through their own reader into the same model; the diagrams guide lists what it reads, and the fix hints use PlantUML syntax for them ([db] <<external>>).
  • Nodes. An id of letters, digits, _ and inner hyphens (order-api), with any shape: [ ], ( ), ([ ]), [[ ]], [( )], (( )), ((( ))), > ], { }, {{ }}, the parallelograms and trapezoids, and @{ shape: ..., label: "..." }. A label written as a Markdown string ("`text`") is a caption.
  • Links. Every kind, with text in them (-- text -->) or after them (-->|text|), an edge id (e1@-->), longer links (--->), chains (a --> b --> c) and & (a & b --> c). The reader records which links are solid or thick arrows that point one way, which INW018 reads as "may import".
  • The rest. subgraph id [title] and subgraph id["title"] up to end, nested; class a,b name; :::name after a node; %% comments, on their own line or after a statement; ; between statements. classDef, style, linkStyle, click, direction, accTitle and accDescr are skipped.

A statement the reader can't follow stops being read where it stops making sense; what came before still counts. Nothing is reported for a line it can't read.

Whether a quoted label exists is decided like INW006's check of a layer prefix: a module equals it or lies under it, or the selector matches one. The modules are the first-party modules of the whole-project run, or, when one diagram is checked on its own (the hook, the editor, a named path), the Python files under the config root, listed but not read.

How to fix

  1. Rename the node or subgraph to the layer or context it shows, as [tool.inwards] spells it.
  2. Fix the label to the module prefix the node stands for, or drop the quotes to make it a caption.
  3. Mark a node that isn't part of the project :::external.
  4. If the diagram is right and the config is behind, ask the user to change [tool.inwards]; an agent doesn't edit it.

Fix safety

No automatic fix (autofix: false). The finding says the diagram and the config disagree; only a person knows which one is right. Renaming a node changes docs only and is safe; changing [tool.inwards] changes what every rule checks.

Configuration

Severity warning, so docs drift doesn't block the Stop gate by default. Opt-in: turn it on with extend-select, and list the files in diagrams:

pyproject.toml
[tool.inwards]
diagrams = ["README.md", "docs/**/*.md", "docs/**/*.mmd"]

[tool.inwards.rules]
extend-select = ["INW017"]
severity = { INW017 = "error" }      # optional: block on drift

The rule has no options of its own; [tool.inwards.rules.diagram-unknown-name] takes only modules, which has no effect, since a diagram isn't a module. A finding can't be suppressed inline: a diagram holds no Python comment, and a %% comment would be one more thing for the reader to ignore. Mark the node :::external or fix the name instead.

Known limitations

  • Layer and context names that aren't Mermaid ids, such as slices.domain from a template, can't be a node id; such a layer is found only through a quoted label that is exactly one of its modules entries, and only when no other such layer lists the same entry.
  • Mermaid flowcharts and PlantUML component diagrams only. C4Context, architecture-beta, class diagrams and other PlantUML diagram types aren't read; a marker on one is ignored. The PlantUML syntax Inwards reads is in the diagrams guide.
  • One line at a time. A statement split across lines, or a label with a " inside it (Mermaid's #quot;), may be read in part.
  • A code edit doesn't check the diagrams. Editing a diagram runs the rule in the hook and the editor; a package renamed in the code reaches the drawing on the next whole-project run.
  • Hidden directories (.github/) and the directories every walk skips (virtualenvs, node_modules) aren't searched by a glob; name such a file without a wildcard.

References