Diagrams¶
FORMAT · DIAGRAMS
Diagrams are type: Diagram elements. The diagramKind: field selects the rendering path.
Diagram kinds¶
diagramKind | Rendering | Description |
|---|---|---|
BDD | SVG (server / PlantUML) | Block Definition Diagram — part/item type hierarchy and compositions |
IBD | SVG (server / PlantUML) | Internal Block Diagram — part usages, ports, and connections within a block |
StateMachine | SVG (server / PlantUML) | State machine — states, transitions, and guards |
Action | SVG (server / Mermaid) | Action diagram — action steps, fork/join/decision/merge control nodes, successions and flows |
FeatureModel | SVG (server / Mermaid / PlantUML) | Feature diagram in FODA notation — the feature tree with mandatory/optional marks, XOR and OR groups and requires/excludes constraints; derived from FeatureDefs |
Requirement | SVG (server / PlantUML) | Requirement diagram — requirements, derivation, and verification links |
Sequence | SVG (PlantUML) | Sequence diagram — lifelines, messages, returns |
Mermaid | Mermaid.js (client) | Any diagram expressible in Mermaid graph syntax |
Structured diagrams (BDD, IBD, StateMachine, Action, Requirement, Sequence)¶
These diagrams carry their content in YAML frontmatter, from one of two sources. A BDD, IBD, StateMachine, Action, Sequence, Requirement or Allocation diagram with a subject: and no shapes: is derived from the model (next section) — the preferred form for those kinds. A diagram with a shapes: block is manifest-sourced: the hand-listed alternative, where the author enumerates the shapes, edges, and optional layout pins, and the only form for kinds that have no generator yet. The presence of shapes: selects the source; there is no mode field.
shapes: — mapping of shape-id to descriptor¶
shapes:
s-uavsystem: {ref: "UAV::UAVSystem", kind: PartDef}
s-avionics: {ref: "UAV::Avionics::AvionicsBay", kind: PartDef}
s-fc: {ref: "UAV::Avionics::FlightController", kind: Part, parent: s-avionics}
Each shape descriptor:
| Sub-field | Description |
|---|---|
ref | Qualified name of the model element this shape represents; validated by W402 |
kind | Rendering hint: PartDef, Part, Port, boundary, state, initial, Requirement, etc. |
parent | Shape-id of the enclosing boundary shape (for IBD nesting) |
Sub-feature refs (e.g. UAV::Avionics::FlightController::powerIn) do not resolve as top-level elements — W402 suppresses the warning for these.
edges: — mapping of edge-id to descriptor¶
edges:
e-comp: {source: s-uavsystem, target: s-avionics, kind: composition}
e-power: {source: s-fc-power, target: s-imu, kind: flowConnection}
Each edge descriptor:
| Sub-field | Description |
|---|---|
source | Shape-id of the source end; validated by W403 |
target | Shape-id of the target end; validated by W403 |
kind | composition, flowConnection, derivedFrom, verifies, allocatedTo, transition |
ref | Optional: model element this edge represents (e.g. a feature or connection usage) |
layout: — pixel coordinates¶
layout: is optional. It holds pinned positions: a manifest diagram without it is laid out automatically (ELK) on export, and a layout: block only pins the shapes it names, leaving the rest to the auto-layout.
Derived diagrams (BDD, IBD, StateMachine, Action, Sequence, Requirement, Allocation, FeatureModel)¶
A FeatureModel diagram takes a FeatureDef, a FeatureModel sheet or a package of features as its subject: and draws the feature tree in FODA notation (spec §8.16.8.9); the same graph is the browser's /features page. A BDD, IBD, StateMachine, Action or Sequence diagram needs no manifest at all. Declare the kind and the subject, and the generator reads the content from the model every time the diagram is built:
Adding a part, a port or a supertype: to the model changes the picture; nothing in the diagram file needs editing. The demo model ships one of each: Diagrams::UAVSystemDerivedBDD (subject: the UAV package) and Diagrams::PowerSystemDerivedIBD (subject: UAV::Power::PowerSystem; compare the hand-listed Diagrams::PowerSystemIBD, the same view as a manifest).
What the generator draws¶
BDD — subject: a Package, PartDef or ItemDef.
- One block per
PartDef,ItemDef,PortDef,InterfaceDeforConnectionDefthat is a direct member of the subject package (for a definition subject: the definition itself plus its direct sub-definitions). Actions, states and requirements never appear. - A compartment listing the block's attribute features (
mass : Real [kg]) and ports (port powerOut : PowerPort (out)). inheritanceedges fromsupertype:;compositionedges from part usages (inlinefeatures:typed by a definition, and childPart/Itemelements), labelled with the usage name and a non-1 multiplicity (motor [2]); anassociationedge for aConnectionDefwhose two end types are blocks on the diagram.- An edge is drawn only when both of its ends are on the diagram.
IBD — subject: a PartDef or Part.
- A boundary for the subject with its own ports (a
Partsubject also shows its definition's ports). - One block per owned part usage — inline
features:typed by aPartDef, or childPartelements — labelledname : Type [mult], with ports from the usage's own features and from its definition, each carrying itsdirection. - Edges from the subject's
connections:(connection),flowConnections:(flow),bindingConnections:(binding) andsuccessionConnections:(succession). Endpoints are the usual dotted chains:engine.powerOutis portpowerOutof usageengine; a single segment is a usage or a boundary port. A chain that reaches nothing draws no edge — the generator never invents a port.
StateMachine — subject: a StateDef (or a State/ExhibitState usage, which reads the StateDef it is typed by). Demo: Diagrams::FlightStatesDerivedSM.
- One rounded state per
subStates:entry, withentry / …,do / …andexit / …compartment lines fromentryAction/doAction/exitAction. A substate typed by aStateDefwith its ownsubStates:becomes a container holding that machine's states, one level deep. - An initial pseudostate per region with a transition to each
isInitial: truestate, and a final node everyisFinal: truestate transitions to. - One transition edge per transition in either placement (nested under its source substate, or top-level with
source:; the deprecatedfrom/to/triggerkeys are read like the canonical ones), labelledaccept [guard] / effectwith the absent parts omitted — the payload's last segment (orafter …/when …/at …for a time or change trigger), the guard in brackets, the effect's name or type. A transition whose endpoint is not on the diagram draws nothing (that is W929's job).
Action — subject: an ActionDef (or an Action usage, which reads its definition). A new diagramKind: Action (spec §8.16.8.8). Demo: Diagrams::MissionExecutionDerivedAction.
- One rounded step per
subActions:entry, stereotyped by its kind (perform,send,accept,assign,terminate, or plainaction), with a compartment for itstypedBy,payload, trigger andvia/tochains. - An
IfActionas a decision diamond labelled by its condition, itsthen/elsesteps joined to it by[then]/[else]successions and rejoining at a merge; aLoopActionas a container stereotypedloop, labelledname [for v in seq](or[while c]/[until c]), holding its body in order. - A fork/join bar or decision/merge diamond per
controlNodes:entry. - A succession edge per
successionConnections:entry (labelled[guard]when present; anIfActionendpoint enters at its decision and leaves from its merge) and a flow edge perflowConnections:entry. - An initial node feeding every step with no incoming succession and a final node reached from every step with no outgoing one — only when the action declares at least one succession; otherwise the steps are drawn unordered.
Requirement — subject: a Package, RequirementDef or Requirement (REQ-TRS-VIS-020; demo: Diagrams::RequirementsDerived).
- One requirement box per native
Requirement,RequirementDefor SysMLRequirementthat is the subject or lies under it at any depth, labelled byname(else id), with a compartment showingid = …andstatus = …when present. deriveedges from each requirement to itsderivedFrom:targets andrefineedges to itsrefines:targets (child below parent — the layout puts the target above the source).satisfyedges from every element whosesatisfies:names a requirement on the diagram, drawn as a block with its real type's stereotype, andverifyedges from everyTestCasewhoseverifies:names one. These context nodes appear only through such an edge.containmentedges from aRequirementDefto the requirements it owns.include:/exclude:apply to requirements and context nodes alike, by qualified name, stable id (REQ-UAV-FC-001,TC-UAV-FC-001) or short name — the way to keep a large requirements package readable.
Allocation — subject: a Package, AllocationDef or Allocation (REQ-TRS-VIS-022; demo: Diagrams::FunctionAllocationDerived).
- Every allocation pair under the subject: an
Allocationelement'sallocatedFrom:/allocatedTo:, itsfeatures:entries oftype: Allocation, anAllocationDef'sallocations:entries, andallocatedTo:on aPart/PartDef/Action/ActionDef(the element itself is the source). - Two swimlanes, Logical (sources) and Physical (targets), each holding one block per distinct element with its real type's stereotype; an end that does not resolve is a dashed block labelled by the reference text; an element on both sides appears once in each lane.
- One
«allocate»edge per pair, labelled by the usage name when it has one. include:/exclude:apply to the end elements; an edge is drawn only when both ends are kept.
Sequence — subject: an ActionDef or Action (or a UseCaseDef/UseCase with actors:), or a PartDef/Part that performs: an action — its sequence is the messages of the performed actions, in the order performs: declares them (REQ-TRS-SEQ-001; a part that performs nothing is W418) (REQ-TRS-VIS-021; demo: Diagrams::MissionExecutionDerivedSeq, compare the hand-listed Diagrams::MissionExecutionSeq).
- One lifeline per participant, in first-appearance order: the subject first, then every
- A
SendActionorPerformActionthat names an operation (operation: <owner>::<name>) is a message labelled with the operation's signature,read(channel: Integer): Real, to the participantto:/via:names or else to the element that declares the operation. A synchronous operation with a return type is followed by a dashed return back to the subject, labelledReal ! Faultwhen errors are declared (REQ-TRS-SEQ-002). element aSendAction'sto:chain or anAcceptAction's/SendAction'svia:chain resolves to — a port chain resolves to the part that owns the port (an inline feature of the subject, a port element's owner, or the part in the model that owns a port of that name, preferring one thatperforms:the subject); an unresolved chain is a dashed lifeline labelled by the chain text — then each entry of the subject'sactors:as an actor. - One message per
SendAction(subject → participant, labelledname(Payload)) and perAcceptAction(participant → subject, labelled by the payload, else the trigger), in execution order:successionConnections:topological order when declared, else declaration order, descending into anIfAction'sthen/elseand aLoopAction'sbody. A nestedPerformActionis a step in the order but is not expanded; no return, create or destroy messages are generated. - One
altfragment perIfActionand oneloopfragment perLoopAction, labelled by the condition, enclosing the messages it contains; an activation on the subject's lifeline spanning its messages. - The generator places everything itself — lifelines left to right at a fixed pitch, messages top to bottom in order, fragments around their span — as pins, with horizontal waypoints on every message, so every renderer (SVG export, the browser editor, Mermaid, PlantUML) draws it with the
fixedalgorithm and no layout-engine run. Alayout:block can still move a lifeline or fragment by its id (s-<subject>-<participant>,s-<subject>-<action>). include:/exclude:name participants (qualified or short name; never the subject); a dropped participant takes its messages with it.W080(manifest completeness) is never raised on a derived Sequence diagram.
UseCase and Custom have no generator; a derived diagram of such a kind is drawn empty, so keep using a manifest for them.
Composition across packages: depth:¶
A derived BDD pulls in the blocks that its blocks compose, even from other packages, as external blocks (marked external). depth: N sets how many composition levels are followed beyond the subject's own blocks (default 1; 0 = only the subject's members). A member that include:/exclude: left out is never pulled in by composition. On a package subject, include: also accepts a name qualified relative to the subject (Hardware::Box), a full qualified name or a stable id (never a bare display name of a definition elsewhere). The Mermaid and PlantUML exports tag such blocks <<external>>.
Narrowing the view: include: / exclude:¶
diagramKind: BDD
subject: UAV
include: [UAV::UAVSystem, Airframe] # qualified name, or name relative to the subject
exclude: [Airframe] # applied after include
include: keeps only the named members (BDD: definitions in the package; IBD: owned part usages) and the edges joining them; exclude: removes members, and every edge touching them, from an otherwise complete view. Either takes a string or a list. An entry that names no member of the subject is W417; include:/exclude: on a manifest diagram is one W417 and is ignored.
Pins¶
Derived shape ids are deterministic: s- plus the element's qualified name lower-cased with :: and other non-alphanumerics replaced by - — UAV::Power::PowerSystem::pdu becomes s-uav-power-powersystem-pdu. A layout: block therefore works on a derived diagram exactly as on a manifest one: pins survive regeneration, and a renamed element simply loses its pin (W416) and is laid out automatically again.
Nesting, deployment, marks and domain frames (IBD)¶
A derived IBD reads more of the model than its subject's direct parts:
depth: 2(or more, up to 16) draws each part's own parts inside it, inline or childPartfiles, with their ports and the connections their definition declares. A part that contains itself is opened once.- Software with
isDeploymentPackage: truethat is allocated to hardware (anAllocation, orallocatedTo:) is drawn inside that hardware. If the hardware is not drawn at this depth, the software goes inside the part that contains it. With two boards, each board's firmware goes into that board's MCU. - A part with
silLevel:,asilLevel:or asafetystereotype gets a heavy amber outline and aSIL 2/ASIL Bbadge. A part withappliesWhen:is drawn dashed with anoptionalbadge andwhen <feature>. The SVG adds a legend when any part is marked. groupBy: domainframes the subject's parts bydomain:(system, hardware, software). Deployed software stays inside its hardware and shows its domain as a banner.syscribe diagram export <qname> --config <CONF>draws only the parts active in that product, and badges none of them optional.
System context: actors (IBD, BDD)¶
A context is an ordinary PartDef holding the system beside the actors it deals with. An actor is a part usage marked role: external (spec §12.13), one field shared with the role: environment parts of a verification set-up:
---
type: PartDef
name: Inverter Context
features:
- {name: inverter, typedBy: System::GridTieInverter}
- {name: pvArray, typedBy: Context::PvArray, role: external}
- {name: grid, typedBy: Context::UtilityGrid, role: external}
connections:
- {from: pvArray.dcOut, to: inverter.pvIn}
- {from: inverter.gridOut, to: grid.acIn}
---
A derived IBD of the context draws each actor «actor» beside the system, an environment part «environment» with a dashed outline, and the connections between their ports; a derived BDD marks an actor's definition «actor». In a context, a port of a product part that no connection, flow or binding reaches is W996, so a port added to the system without saying who is on the other side is caught. A part with no actor is not a context and is not checked.
Flow kinds and port sides¶
An architecture picture reads faster when the lines say what they carry. Put flowKind: on a PortDef (a sub-PortDef inherits it through supertype:), on a Port, on a port feature, or on a connections:/flowConnections: entry or a manifest edges: entry. The kinds are power, signal, command, data, protection and mechanical. An edge takes the entry's own kind, else the source port's, else the target port's. Bindings and successions carry nothing and keep their look.
signal draws as every edge did before. Power is a heavy orange line, command dashed blue, data dotted green, protection red and mechanical heavy grey. SVG, sprotty, DOT, Mermaid (linkStyle plus a %% flow: comment) and PlantUML (a coloured arrow and a <<kind>> stereotype) all carry the kind. A model with no flowKind: draws exactly as before.
A [diagram] table in .syscribe.toml restyles a kind and can place ports by kind. A port's own side: (north, east, south, west) always wins.
[diagram.styles.power] # stroke (or color), width, dash ("" is solid)
width = 3
[diagram.portSides]
power = "direction" # in west, out east: the default
signal = "south"
stroke must be #rgb, #rrggbb or a colour name and dash a list of numbers. An unknown kind or side, a malformed [diagram] entry, or a flowKind:/side: where nothing reads it, is W419 and is ignored.
Views: what a diagram is about¶
On a derived IBD, include: may be a mapping that says what the diagram is about rather than which members to keep. The parts it selects, and the edges between them, are drawn as usual; the parts that contain them stay as frames; everything else is faded (spec §8.16.10).
type: Diagram
name: GtiPowerView
diagramKind: IBD
subject: System::GridTieInverter
include: {kinds: [power]} # also domains:, tags:, features:, depth:, members:
style: focus # a [diagram.style.focus] table of .syscribe.toml
layout: {flow: left-to-right, layers: [power, sensing]}
include: key | Selects |
|---|---|
kinds | parts joined by an edge of that flow kind, and those edges |
domains | parts whose usage or definition has that domain: |
tags | parts whose usage or definition has that tag |
features | parts whose appliesWhen: names that feature (qname or FEAT-* id) or one beneath it |
stereotypes | stereotype selectors (Safety, Safety where integrity >= 2; a specialisation counts): parts, or their definitions, that apply one. An entry that is malformed or names no MetadataDef is W421 and ignored |
A named style lives in .syscribe.toml:
[diagram.style.focus]
context = "fade" # or "hide": leave the context out
[diagram.style.safety]
extends = "focus"
[diagram.style.safety.flows.protection]
width = 4
Styles layer as project [diagram.styles], then [diagram.style.default], then the extends chain, then the named style; pins apply on top. layout.flow sets the layout direction and layout.layers orders each container's parts by band (a part name, a flow kind or a domain frame label).
Style by stereotype (REQ-TRS-META-040, -043, -044). [diagram.stereotypes.<Def>] gives the nodes of the elements that apply a stereotype — or a specialisation of it — their own presentation, in every writer (browser, SVG, DOT, PlantUML, Mermaid):
[diagram.stereotypes.Safety]
stroke = "#d00000" # fill, stroke: #rgb, #rrggbb or a colour name
fill = "#ffe5e5"
width = 3 # outline width
when = "integrity >= 2" # optional: only where the application's tagged values satisfy this
[diagram.stereotypes.Optional]
dash = "6,3" # a dashed outline ("" is solid)
banner = false # hide the «Optional» banner
priority = 5 # higher wins a property two styles set; ties go to the later definition name
Several stereotypes on one element compose property by property. The graph carries a legend of the stereotypes that actually styled a node (strongest first), which the SVG writer draws under the diagram. With no table a diagram draws exactly as before. A malformed entry is W419 and is ignored. frame = true makes a stereotype a swimlane for an IBD with groupBy: stereotype (the part's, or its definition's, stereotype; strongest first). A stereotype applied on a connections: entry (metadata:), or on its typedBy ConnectionDef, restyles that edge (stroke, width, dash) in every writer. Not yet: icon and badge.
syscribe diagram export <qname> --config A --config B draws two products in one diagram: what only A has is marked only A, what only B has only B. Parameter values that change no element are not shown; syscribe diff --config A --config B reports them.
Committed renders: diagram export --all¶
syscribe diagram export --all renders every diagram in the model into generated/ beside the model (SVG by default), one file per diagram named after its qualified name. Commit that directory and run syscribe diagram export --all --check in CI: it fails, naming the file, whenever an edit changed what a diagram draws and nobody regenerated it. The output does not depend on the machine. Box sizes come from Liberation Sans advance widths compiled into syscribe (a metric twin of Helvetica and Arial), plus a margin, so a browser drawing the SVG with its own sans font does not clip a label. Hand-drawn companion diagrams are skipped, and the export only ever removes files it recorded in generated/.syscribe-generated, so a hand-drawn picture kept there is safe. With --config CONF-X the renders go to generated/CONF-X/. See model_gti/generated/ for the grid-tie inverter's renders.
Codes¶
| Code | Condition |
|---|---|
| W417 | include:/exclude: on a manifest diagram (ignored), or an entry that names no member of the subject |
| W418 | A derived diagram's subject: type is not valid for its diagramKind: (BDD: Package/PartDef/ItemDef; IBD: PartDef/Part; StateMachine: StateDef/State/ExhibitState; Action: ActionDef/Action; Requirement: Package/RequirementDef/Requirement; Allocation: Package/AllocationDef/Allocation; Sequence: ActionDef/Action/UseCaseDef/UseCase, or a PartDef/Part with performs:); the diagram is drawn empty |
| W419 | A flowKind: or side: names no flow kind or side, or a [diagram] entry is malformed; the value is ignored |
| W420 | groupBy: is not domain, or is set on a diagram that is not an IBD; ignored |
| W421 | A view is malformed: an unknown include: key or a value of the wrong shape, a kind, domain or feature that is none, the mapping form on a diagram that is not a derived IBD, a style: that names no style or sits on an element that is not a Diagram, a bad layout: hint; ignored |
An unresolved subject: is still W401, and a derived diagram never raises W402/W403 for the shapes it generates.
Mermaid diagrams¶
Set diagramKind: Mermaid and include a fenced ```mermaid block in the document body. The validator fires E400 if the block is absent.
---
type: Diagram
name: RequirementTrace
diagramKind: Mermaid
subject: Requirements
---
Requirement derivation tree.
```mermaid
graph TD
%% ref: REQ-UAV-PERF-000
PERF["REQ-UAV-PERF-000<br/>Mission Performance"]
%% ref: REQ-UAV-COMM-001
COMM["REQ-UAV-COMM-001<br/>Data Link ≥ 5 km"]
PERF --> COMM
```
The Mermaid.js runtime is loaded from CDN and renders the diagram client-side when the tab is activated.
PlantUML companion diagrams¶
pumlMode: companion opts a diagram into the PlantUML workflow. Syscribe generates a .puml source file; your PlantUML toolchain (JAR, CI step, IDE plugin) renders it to SVG. Every shape emits a [[URL]] hyperlink back to the element's detail page in the web browser.
Frontmatter fields¶
| Field | Description |
|---|---|
pumlMode: companion | Opt in; the only supported value |
pumlFile: ./MyDiagram.puml | Override companion path (default: <stem>.puml) |
The body must reference the anticipated SVG so the diagram is visible on GitHub and other Markdown renderers. syscribe plantuml injects a Markdown image link automatically if the body has no image reference yet. Both  and <img src="path.svg"> satisfy W413.
---
type: Diagram
name: UAVSystemBDD
diagramKind: BDD
pumlMode: companion
pumlFile: ./UAVSystemBDD.puml
subject: UAV::UAVSystem
shapes:
s-uavsystem: {ref: "UAV::UAVSystem", kind: PartDef}
s-avionics: {ref: "UAV::Avionics::AvionicsBay", kind: PartDef, parent: s-uavsystem}
edges:
e-comp: {source: s-uavsystem, target: s-avionics, kind: composition}
---

CLI workflow¶
# 1. Generate .puml source files for all companion diagrams
syscribe -m model/ plantuml
# 2. Render .puml → .svg (needs plantuml on PATH or PLANTUML_JAR env var)
syscribe -m model/ plantuml render
syscribe -m model/ plantuml render --jar /opt/plantuml/plantuml.jar
# Generate a single diagram
syscribe -m model/ plantuml Diagrams::UAVSystemBDD --output -
Style configuration (.syscribe.toml)¶
[plantuml]
theme = "spacelab" # any PlantUML built-in theme
# style_file = "style/custom.puml" # !include — takes precedence over theme
# base_url = "https://my-server" # clickable link prefix (default: http://localhost:3000)
# jar = "/opt/plantuml.jar" # JAR path for `plantuml render`
Supported diagramKind values for PlantUML generation¶
diagramKind | PlantUML output |
|---|---|
BDD | Class diagram — class "Name" <<part def>>, *-- composition |
IBD | Component diagram — rectangle boundary, component blocks |
StateMachine | State diagram — [*] initial, state "Name" as id |
Sequence | Sequence diagram — actor/participant, -> messages |
Requirement | Class diagram — <<requirement>> stereotype, ..> edges |
Action | Activity-style diagram of the action's steps and flows |
Allocation | Allocation diagram — allocated elements and their targets |
UseCase | Use-case diagram — usecase nodes, actors (manifest-sourced; there is no derived generator) |
Mermaid and unknown kinds are skipped with a warning.
Validation rules for diagrams¶
| Code | Severity | Condition |
|---|---|---|
| E400 | Error | diagramKind: Mermaid but body has no ```mermaid block |
| E402 | Error | svgFile: path does not exist on disk |
| E403 | Error | pumlMode has an unrecognized value (only companion is supported) |
| E404 | Error | pumlMode: companion set but diagramKind is absent |
| W400 | Warning | Diagram element has no diagramKind — rendering mode ambiguous |
| W401 | Warning | subject: does not resolve to a known element |
| W402 | Warning | A shape ref: does not resolve (and is not a sub-feature of a known element) |
| W403 | Warning | An edge source or target is not a defined shape id in this diagram |
| W417 | Warning | include:/exclude: on a manifest diagram (ignored), or an entry that names no member of the subject |
| W418 | Warning | A derived diagram's subject: type is not valid for its diagramKind:; the diagram is drawn empty |
| E405 | Error | shapes:, edges: or layout: is present but malformed; the entry is skipped |
| W413 | Warning | pumlMode: companion but body has no <img tag |
| W414 | Warning | pumlMode: companion but the .puml companion file does not exist yet |
| W415 | Warning | [plantuml] style_file in .syscribe.toml does not exist on disk |
This table is not exhaustive: W404–W412 (operation types, SVG/Mermaid annotation consistency, shape links) and W416 (stale layout: pin) are also raised for diagrams. See the validation rules for the complete list.