dsh-model-everything 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.en-US.md +332 -0
  3. package/README.md +332 -3
  4. package/cordis.patch.yml +20 -0
  5. package/examples/README.md +48 -0
  6. package/examples/control-dispatcher.json +113 -0
  7. package/examples/control-dispatcher.overbudget.json +113 -0
  8. package/examples/ecommerce-data-model.json +185 -0
  9. package/examples/order-state-machine.after.json +116 -0
  10. package/examples/order-state-machine.before.json +101 -0
  11. package/examples/user-migration.after.json +29 -0
  12. package/examples/user-migration.before.json +29 -0
  13. package/gate/locale/en.json +6 -0
  14. package/gate/locale/zh.json +6 -0
  15. package/gate/package.json +5 -0
  16. package/lib/baseline.js +249 -0
  17. package/lib/compose-tool.js +80 -0
  18. package/lib/concurrency-tool.js +40 -0
  19. package/lib/concurrency.js +206 -0
  20. package/lib/data-engine.js +1141 -0
  21. package/lib/data-tool.js +87 -0
  22. package/lib/design-engine.js +1149 -0
  23. package/lib/design-tool.js +63 -0
  24. package/lib/engine.js +4343 -0
  25. package/lib/export-tool.js +78 -0
  26. package/lib/exporters.js +434 -0
  27. package/lib/gate.js +274 -0
  28. package/lib/granularity.js +237 -0
  29. package/lib/index.js +237 -0
  30. package/lib/json-value.js +18 -0
  31. package/lib/model-file.js +48 -0
  32. package/lib/option-shape.js +62 -0
  33. package/lib/report-diff-tool.js +63 -0
  34. package/lib/report-view.js +433 -0
  35. package/lib/structure-tool.js +91 -0
  36. package/lib/structure.js +711 -0
  37. package/lib/tool.js +94 -0
  38. package/lib/types/baseline.d.ts +65 -0
  39. package/lib/types/compose-tool.d.ts +7 -0
  40. package/lib/types/concurrency-tool.d.ts +8 -0
  41. package/lib/types/concurrency.d.ts +37 -0
  42. package/lib/types/data-engine.d.ts +211 -0
  43. package/lib/types/data-tool.d.ts +10 -0
  44. package/lib/types/design-engine.d.ts +215 -0
  45. package/lib/types/design-tool.d.ts +11 -0
  46. package/lib/types/engine.d.ts +494 -0
  47. package/lib/types/export-tool.d.ts +7 -0
  48. package/lib/types/exporters.d.ts +37 -0
  49. package/lib/types/gate.d.ts +91 -0
  50. package/lib/types/granularity.d.ts +106 -0
  51. package/lib/types/index.d.ts +45 -0
  52. package/lib/types/json-value.d.ts +21 -0
  53. package/lib/types/model-file.d.ts +7 -0
  54. package/lib/types/option-shape.d.ts +22 -0
  55. package/lib/types/report-diff-tool.d.ts +8 -0
  56. package/lib/types/report-view.d.ts +50 -0
  57. package/lib/types/structure-tool.d.ts +10 -0
  58. package/lib/types/structure.d.ts +152 -0
  59. package/lib/types/tool.d.ts +11 -0
  60. package/lib/types/uml-communication.d.ts +109 -0
  61. package/lib/types/uml-composite.d.ts +107 -0
  62. package/lib/types/uml-interaction-overview.d.ts +91 -0
  63. package/lib/types/uml-profile.d.ts +112 -0
  64. package/lib/types/uml-structure.d.ts +53 -0
  65. package/lib/types/uml-syntax.d.ts +296 -0
  66. package/lib/types/uml-timing.d.ts +152 -0
  67. package/lib/types/uml-tool.d.ts +12 -0
  68. package/lib/types/uml-usecase.d.ts +58 -0
  69. package/lib/types/uml.d.ts +303 -0
  70. package/lib/uml-communication.js +353 -0
  71. package/lib/uml-composite.js +442 -0
  72. package/lib/uml-interaction-overview.js +277 -0
  73. package/lib/uml-profile.js +373 -0
  74. package/lib/uml-structure.js +306 -0
  75. package/lib/uml-syntax.js +545 -0
  76. package/lib/uml-timing.js +974 -0
  77. package/lib/uml-tool.js +213 -0
  78. package/lib/uml-usecase.js +532 -0
  79. package/lib/uml.js +2110 -0
  80. package/locale/en.json +6 -0
  81. package/locale/zh.json +6 -0
  82. package/package.json +128 -6
  83. package/skills/model-everything/SKILL.md +450 -0
  84. package/skills/model-everything/references/dsh-model-schema.md +437 -0
  85. package/skills/model-everything/references/gap-routing-guide.md +42 -0
  86. package/skills/model-everything/references/hash-spec.md +152 -0
  87. package/skills/model-everything/references/logic-verification-guide.md +422 -0
  88. package/skills/model-everything-datamodel/SKILL.md +130 -0
  89. package/skills/model-everything-datamodel/references/data-model-guide.md +84 -0
  90. package/skills/model-everything-datamodel/references/data-model-schema.md +128 -0
  91. package/skills/model-everything-design/SKILL.md +115 -0
  92. package/skills/model-everything-interleaving/SKILL.md +63 -0
  93. package/skills/model-everything-interleaving/SKILL.zh-CN.md +60 -0
  94. package/skills/model-everything-interleaving/references/concurrency-risk-guide.md +63 -0
  95. package/skills/model-everything-report-reading/SKILL.md +137 -0
  96. package/skills/model-everything-report-reading/references/reading-a-report.md +166 -0
  97. package/skills/model-everything-report-reading/references/reading-a-report.mmd +59 -0
  98. package/skills/model-everything-structure/SKILL.md +96 -0
  99. package/skills/model-everything-structure/references/structure-review-guide.md +201 -0
  100. package/skills/model-everything-uml/SKILL.md +60 -0
  101. package/skills/model-everything-uml/references/uml-modeling-guide.md +551 -0
  102. package/src/baseline.ts +288 -0
  103. package/src/compose-tool.ts +82 -0
  104. package/src/concurrency-tool.ts +44 -0
  105. package/src/concurrency.ts +254 -0
  106. package/src/data-engine.ts +1208 -0
  107. package/src/data-tool.ts +91 -0
  108. package/src/design-engine.ts +1263 -0
  109. package/src/design-tool.ts +68 -0
  110. package/src/engine.ts +4564 -0
  111. package/src/export-tool.ts +81 -0
  112. package/src/exporters.ts +399 -0
  113. package/src/gate.ts +330 -0
  114. package/src/granularity.ts +312 -0
  115. package/src/index.ts +289 -0
  116. package/src/json-value.ts +20 -0
  117. package/src/model-file.ts +49 -0
  118. package/src/option-shape.ts +64 -0
  119. package/src/report-diff-tool.ts +67 -0
  120. package/src/report-view.ts +483 -0
  121. package/src/structure-tool.ts +93 -0
  122. package/src/structure.ts +786 -0
  123. package/src/tool.ts +103 -0
  124. package/src/uml-communication.ts +428 -0
  125. package/src/uml-composite.ts +520 -0
  126. package/src/uml-interaction-overview.ts +321 -0
  127. package/src/uml-profile.ts +439 -0
  128. package/src/uml-structure.ts +317 -0
  129. package/src/uml-syntax.ts +764 -0
  130. package/src/uml-timing.ts +1106 -0
  131. package/src/uml-tool.ts +230 -0
  132. package/src/uml-usecase.ts +566 -0
  133. package/src/uml.ts +2139 -0
@@ -0,0 +1,166 @@
1
+ # How to read a report
2
+
3
+ Every tool returns a report. They share one skeleton, and they differ in where they keep their
4
+ findings. This guide is the reading order: what to look at first, what each field answers, and what
5
+ a report does not tell you.
6
+
7
+ The structure of this guide is the mind map beside it, which is generated from live reports rather
8
+ than drawn. A hand-drawn map drifts; this one cannot claim a shape the engines do not produce.
9
+
10
+ ## The three questions, in order
11
+
12
+ ### 1. Did it run?
13
+
14
+ `ran` separates a tool that refused from one that ran, but it answers that question for one kind of
15
+ refusal only. A call refused at the boundary — an argument in the wrong shape, a format that does not
16
+ exist, a notation that is not one — comes back with `ran: false`, an `errorCode`, and a
17
+ `verdictReason` beginning "the tool did not run". There is no verdict to route on, so fix the call.
18
+
19
+ An input the engine's own schema rejects is a different shape, and it matters more because it looks
20
+ like a review: `ran: true` (the engine did run), `ok: false`, `verdict: fail`, no `errorCode`, and one
21
+ finding under a check whose id is `MODEL`, `DATA_MODEL` or `DESIGN`. Its `summary.checksRun` is `0`.
22
+ Read it as "the input is the problem, not the design" — a model that was actually reviewed examined
23
+ something, so a non-zero `checksRun` is what separates a defective model from a malformed one.
24
+
25
+ So a refusal and a failing review need different responses, and `ran` alone does not tell them apart:
26
+ the test is `ran: false`, or `checksRun: 0` beside a check named after the validation.
27
+
28
+ ### 2. What did it decide?
29
+
30
+ `verdict` is the answer, and it has three values:
31
+
32
+ | `verdict` | Means |
33
+ |---|---|
34
+ | `pass` | The checks ran and found nothing to report |
35
+ | `pass_with_findings` | Warnings only; nothing decided against the model |
36
+ | `fail` | At least one error finding |
37
+
38
+ Read `verdict`, never `ok`. `ok` carries a different historical meaning in each family — in most it
39
+ is `true` for a failing review, and a composition sets it to `false` when any error-severity finding
40
+ exists — so it cannot be read as a verdict, and reading it as one is how a reader concludes the
41
+ opposite of what the report says. A report with `verdict: "fail"` is a successful run that found
42
+ something; that is the normal way this plugin tells you about a defect.
43
+
44
+ `verdictReason` is the one-line why, and it names the first error code. When the verdict is not what
45
+ you expected, that string is where to start.
46
+
47
+ ### 3. What should I do?
48
+
49
+ `nextSteps` is never empty, and it is derived from the findings rather than written beside them. It
50
+ names the route to whatever covers the ground the report does not: another check, a source-side
51
+ scan, or an external checker.
52
+
53
+ ## The skeleton every report shares
54
+
55
+ `ok`, `ran`, `verdict`, `verdictReason`, `schema`, `summary`, `nextSteps` — every family carries all
56
+ of them. `schema` is the versioned contract, so a reader can tell which shape it is holding.
57
+
58
+ `hashes` and `modelHash` are there so two reports can be compared. The same input and the same hash
59
+ spec give the same hash; a changed hash means the model changed, which is what makes a diff
60
+ meaningful.
61
+
62
+ `summary.boundNotes` records a budget that was replaced or clamped, and `metadataKeys` lists the
63
+ `_`-prefixed keys the schema ignored. Both are printed by the rendered body (`BOUND`, `METADATA`),
64
+ so a reader who only has the text still learns that the numbers came from a different bound than
65
+ the one they asked for, and that a field they wrote was treated as annotation.
66
+
67
+ A report can also carry notes beside its findings — `warnings` holds what a notation or a parser
68
+ could not carry. They count towards the verdict, so the body prints them under `WARNING`, and the
69
+ `FINDINGS` line says how many there are when the roster itself is empty. A body that shouted
70
+ "nothing was reported" over a note the verdict had already counted was the defect this line fixes.
71
+
72
+ ## Where the findings are
73
+
74
+ This is the part that varies, and it is worth knowing before you go looking:
75
+
76
+ | Where | Families |
77
+ |---|---|
78
+ | Inside each check, as `checks[].findings` | `model_verify`, `model_compose_verify`, `model_datamodel_verify`, `model_design_review` |
79
+ | At the top level, as `findings` | `uml_structure_verify`, the `uml` review path, `model_interleaving_scan` |
80
+ | Nowhere — compare `added` / `changed` / `removed` | `report_diff` |
81
+
82
+ A check-carried finding is attributed to a check, and the check also carries a status of its own:
83
+ `pass`, `fail`, or `skip`. A top-level finding has no check to attribute it to, which is why those
84
+ families report a `summary` instead.
85
+
86
+ The status is coarse on purpose: `fail` means the check has a finding, not that the finding is severe.
87
+ A check holding only warnings is `fail` just as a check holding errors is, so `status` alone cannot tell
88
+ you whether the verdict moved - the finding's own severity does that. The rendered body separates them,
89
+ because a human reads it: `fail` there means at least one error, and `warn` means warnings only. When
90
+ the body lists a check under `warn` and the JSON gives that same check `"status": "fail"`, both are
91
+ correct and they answer different questions.
92
+
93
+ `summary.checksRun` counts the checks that examined the model, so it is smaller than the number of check
94
+ objects whenever something was skipped. `summary.checksSkipped` names those, and it is absent when
95
+ nothing was skipped.
96
+
97
+ `report_diff` is the exception in a second way: it has no findings because its subject is the
98
+ difference between two other reports. `verdict` describes the delta, and `currentVerdict` describes
99
+ the current run on its own terms. The two differ exactly when the run is not clean but the delta is
100
+ empty — comparing a failing report against itself gives `verdict: "pass", currentVerdict: "fail"`,
101
+ and both are true: nothing changed, and the run fails.
102
+
103
+ A delta looks at three things, and only one of them is "new": `added`, `removed`, and `changed` — a
104
+ finding that kept its identity and moved. A warning that became an error is in `changed`, not in
105
+ `added`, and it still fails the delta: the slice added nothing and made the report worse. A change
106
+ the other way, error to warning, is named too, and does not fail it.
107
+
108
+ ## Read the severity before the code
109
+
110
+ Within a finding, the severity decides whether it changed the verdict:
111
+
112
+ - **error** — this is what makes the verdict `fail`;
113
+ - **warning** — reported, does not fail the delta;
114
+ - **info** — context, for example that one element makes an inseparable group of findings;
115
+ - **undecided** — the search stopped before deciding. Not a pass. `summary.truncated` says the same
116
+ thing for the report as a whole, and the rendered body leads with a `LIMIT` line, so a reader who
117
+ only has the text is told before reaching any finding.
118
+
119
+ The finding codes are prefixed by the check that owns them (`S1`, `A4`, `DS5`, `DA2`, `DES12`,
120
+ `UML023`), so a code tells you where to look even before you read its message.
121
+
122
+ ### A code is for you, not for the person you are answering
123
+
124
+ The codes exist so you can route a finding to the reference that explains it, and so a run is comparable
125
+ with the last one. A user who never read the report has no idea what `A4_NO_RELEASE_EVENT` means, and a
126
+ reply that recites codes makes the work look opaque while telling them nothing they can act on.
127
+
128
+ **Say what the finding says, in words.** Each finding's own `message` is already a sentence written for a
129
+ reader who does not know the codes. Use it. Name the code afterwards only when it helps the user find the
130
+ same line in the report, and give it a meaning at the same time: not "S7 failed", but "the invariant is
131
+ violated - the model reaches C after two events (`S7_INVARIANT_VIOLATION`)".
132
+
133
+ The same rule covers a check id: `Event Completeness` is the name, `S5` is the label. Lead with the name.
134
+
135
+ One code is worth relaying with its reason rather than its name. A model whose reachable state space is
136
+ infinite is reported as `S6_UNBOUNDED_VARIABLE`, an error, and the point to pass on is that **no** search
137
+ bound would have decided it: the question is undecidable for that model, not merely unanswered.
138
+
139
+ ## What each family adds
140
+
141
+ Beyond the skeleton, each family carries what its own domain needs: a graph, a round-trip record,
142
+ a notation, a rendered source, the three delta lists, or a design review's split of `violations`,
143
+ `signals` and `facts`. Read `schema` to know which one you have, then read the section that belongs
144
+ to it.
145
+
146
+ The design review's three lists are worth knowing, because only the first decides anything:
147
+
148
+ - `violations` — the check levels that decide the verdict. Ten codes carry it, from a dependency
149
+ cycle to a type relation that resolves to nothing;
150
+ - `signals` — four codes that are worth a look and fail nothing;
151
+ - `facts` — no findings at all: one measurement object with the graph's numbers (`density`, `ranks`,
152
+ `articulationPoints`, `feedbackArcs`, `largestCycle`). It also carries `checksSkipped`, which is
153
+ where a reader learns that nine of the checks never ran for want of a declaration.
154
+
155
+ No code appears in both `violations` and `signals`: the split is exclusive, so a reader who reads
156
+ only `violations` sees every finding that decided the verdict and none that did not.
157
+
158
+ ## What a report does not tell you
159
+
160
+ - **It is about the model, not the code.** A clean verdict is evidence about the drawing you
161
+ supplied. Reconciling it with the source is a separate step, and the difference between the two is
162
+ where the finding usually is.
163
+ - **A truncated search decided nothing.** When `truncated` is true, the checks that stopped early
164
+ report themselves as undecided rather than as passes.
165
+ - **A skipped check examined nothing.** `skip` and `pass` are different answers: a check that had no
166
+ vocabulary to work with did not look, and saying so is the point.
@@ -0,0 +1,59 @@
1
+ mindmap
2
+ root((how to read a report))
3
+ the three questions in order
4
+ did it run
5
+ ran tells a refusal apart from a review
6
+ a refusal carries no verdict to route on
7
+ what did it decide
8
+ verdict is the answer
9
+ ok is not a verdict, see the misreadings
10
+ verdictReason is the one-line why
11
+ what should I do
12
+ nextSteps is never empty
13
+ it is derived from the findings
14
+ the skeleton every report shares
15
+ ok
16
+ ran
17
+ verdict
18
+ verdictReason
19
+ schema
20
+ summary
21
+ nextSteps
22
+ hashes and modelHash
23
+ same input and same spec give the same hash
24
+ a changed hash means the model changed
25
+ where the findings are
26
+ inside each check: verify compose datamodel design
27
+ at the top level: structure uml concurrency
28
+ nowhere, compare the three lists: baseline
29
+ read severity before the code
30
+ error decides the verdict
31
+ warning is reported and does not
32
+ info is context
33
+ undecided is not a pass
34
+ truncated says the search stopped early
35
+ an infinite state space is a finding of its own
36
+ what each family adds
37
+ verify adds checks metadataKeys
38
+ compose adds checks
39
+ datamodel adds checks
40
+ design adds the graph numbers and three lists
41
+ structure adds findings graph notation warnings
42
+ uml adds findings metadataKeys primary roundTrip source warnings
43
+ concurrency adds findings
44
+ baseline adds added changed currentVerdict removed
45
+ only violations decide, signals do not, facts are numbers
46
+ no code is in both lists
47
+ what a report does not tell you
48
+ it is about the model not the code
49
+ a clean verdict is evidence about the drawing
50
+ a truncated search decided nothing
51
+ a skipped check examined nothing
52
+ skip and pass are different answers
53
+ the two easy misreadings
54
+ ok is not the verdict
55
+ it means no error finding in most families
56
+ a composition sets it false where verify sets it true
57
+ a delta and a run answer different questions
58
+ verdict is the delta, currentVerdict is the run
59
+ an unchanged failing report is pass over fail
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: model-everything-structure
3
+ description: "Use when auditing architecture or module structure from a drawing. It reads component, package, class, object or deployment diagrams as a dependency graph and checks isolated nodes, dangling endpoints, cycles, allowed-dependency violations, layer violations and missing required edges, across one level or several. Audits the drawing, not the code — reconcile it with a source-side scan. SRP/LSP/ISP questions are model-everything-design. Doctrine: the model-everything skill."
4
+ ---
5
+
6
+ # Model Everything — Structure and Dependency Review
7
+
8
+ A component diagram is a model of an architecture, and a model can be wrong. This skill audits the graph the diagram actually draws — nodes and dependency arrows — against a declared dependency matrix, and says plainly what that does and does not prove.
9
+
10
+ The claim-verification doctrine it inherits — cite evidence, read `verdict` and never `ok` — lives in the `model-everything` skill. Load that one for behavioural claims (state machines, protocols, timing); load this one when the question is *where the dependencies point*.
11
+
12
+ ## When This Skill Applies
13
+
14
+ | The task involves… | What it does |
15
+ |---|---|
16
+ | "Review this architecture", "are the module dependencies sound", "is the layering right" | Parse the structure diagram into a graph, then run the structural checks below |
17
+ | A component / package / class / deployment diagram (PlantUML) or a Mermaid class diagram | `uml_structure_verify` reads nodes, containers, labels and arrows; nothing is discarded |
18
+ | A rule table of allowed dependencies (allow/deny, layers, required edges) | Every edge is judged against it and reported with the rule ids it matched |
19
+ | One architecture drawn at several levels (repository / module / subdirectory) | `diagrams: [{name, diagram, parent}]`: each level is checked on its own, then against its declared parent, with the per-pair node/edge/expansion counts (`UML028`-`UML031`) |
20
+ | A diagram that must be reconciled with an include/graph check over the source | The per-edge rule ids are the meeting point: a difference between the two is the finding worth chasing |
21
+
22
+ A host without the tools runs the repository engine on the same diagram text, and on a `granularity` manifest for a multi-level set.
23
+
24
+ ## What It Checks
25
+
26
+ | Code | Severity | What it means |
27
+ |---|---|---|
28
+ | `UML020_ISOLATED_NODE` | warning | A declared, non-container node with no edge at all. A dead entry, or a dependency the diagram forgot to draw. |
29
+ | `UML021_DANGLING_REFERENCE` | error | An arrow endpoint that no declaration ever introduced. In a component diagram this is how a dependency on a component that does not exist stays invisible. |
30
+ | `UML022_CYCLE` | error | A directed cycle, reported as the **shortest** cycle path. A cycle means no build order and no layering can hold. |
31
+ | `UML023_DISALLOWED_EDGE` | error | An edge the matrix forbids — or, with `default: "deny"`, an edge no rule permits. The denying rule id is named. |
32
+ | `UML024_LAYER_VIOLATION` | error | An edge from a lower declared layer into a higher one (a child depending on its parent). Downward edges are allowed. |
33
+ | `UML025_MISSING_EXPECTED_EDGE` | warning | A rule with `require: true` whose edge the diagram does not draw — including the case where no node matches the source pattern at all. The matrix and the diagram disagree. |
34
+ | `UML026_NO_NODES` | error | The text declares no node, so there is no structure to review. |
35
+ | `UML028_UNKNOWN_PARENT` | error | A level names a parent that is not in the set. |
36
+ | `UML029_REFINEMENT_VIOLATION` | error | Multi-level only: a child invents a dependency at the parent level, or drops a parent edge instead of expanding it into a path. |
37
+ | `UML030_PARENT_CYCLE` | error | The declared parent relation is not a forest. |
38
+ | `UML031_NO_DIAGRAMS` | error | Nothing was supplied to compare. |
39
+
40
+ ## The Dependency Matrix
41
+
42
+ The matrix is the single source of truth the diagram is checked against. Keep it next to the rules it encodes, in JSON:
43
+
44
+ ```json
45
+ {
46
+ "rules": [
47
+ { "id": "R1-app-may-use-hal", "source": "app.*", "allow": ["hal.*"], "deny": ["hal.internal"] },
48
+ { "id": "R2-adapter-owns-hal", "source": "PAY", "allow": ["DRV"], "deny": ["SVC"] },
49
+ { "id": "R3-no-back-edges", "source": "*", "deny": ["SVC"] },
50
+ { "id": "R4-persistence-required", "source": "PAY", "allow": ["DB"], "require": true }
51
+ ],
52
+ "layers": [
53
+ { "name": "app", "members": ["SVC", "PAY"] },
54
+ { "name": "hal", "members": ["DRV", "DB"] }
55
+ ],
56
+ "default": "deny"
57
+ }
58
+ ```
59
+
60
+ - `source`, `allow`, `deny` and `layers[].members` accept **node ids or globs** (`*` any run, `?` one character).
61
+ - **`deny` wins globally.** A later rule's allow never resurrects an edge an earlier rule forbade; the result cannot depend on rule order.
62
+ - `default: "allow"` (the default) means only the rules judge: an edge no rule mentions is `default`. `default: "deny"` closes the matrix — anything unlisted is `UML023`.
63
+ - `require: true` turns a rule into an expectation about the diagram (`UML025`); it needs `allow` to say which edges must exist.
64
+ - Layers are ordered **top first**: an edge from an earlier layer to a later one is allowed, the reverse is `UML024`.
65
+ - Every edge is echoed in `edgeVerdicts` as `{from, to, line, matchedRules, allowed, basis}`, where `basis` is `allow` / `deny` / `default` / `unlisted`. That is the audit trail for the reconciliation below.
66
+
67
+ ## Reconciling With a Source-Side Check
68
+
69
+ This tool audits the **diagram**; an include or dependency scan audits the **code**. They answer the same question from two sides, and each side can be the one that is wrong:
70
+
71
+ 1. Run both. Keep the rule ids identical on both sides — that is what makes a difference explainable.
72
+ 2. For every rule id, compare: does the diagram draw the edge, and does the source contain the dependency?
73
+ 3. Classify each difference instead of averaging it away:
74
+ - **Diagram missing an edge the code has** — the diagram is stale (`UML025` fires when the rule is `require: true`).
75
+ - **Diagram drawing an edge the code does not have** — either the diagram is aspirational, or the source scan's scope is narrower (different file set, generated code, conditional compilation).
76
+ - **Code violating an allowed dependency the diagram never showed** — the architecture defect the diagram was hiding. This is the one worth acting on.
77
+ 4. Report the difference with both citations. Never present the diagram as evidence about the code without saying which side produced which half.
78
+
79
+ ## Limits — Say These Out Loud
80
+
81
+ - **The diagram may be wrong.** A clean `pass` means the drawn graph is internally consistent and satisfies the matrix — not that the code matches it. Only the reconciliation above touches the code.
82
+ - **No transitive closure by default.** `A → B → C` does not report `A` reaching `C`; write the rule you mean, or add the edge the code actually has.
83
+ - **No runtime behaviour.** Calls through function pointers, DI containers, registries, plugin loading and dynamic dispatch do not appear as edges. A missing edge is not proof of a missing dependency.
84
+ - **Containers are not dependencies.** Package/rectangle nodes carry members, not arrows; they are exempt from `UML020`.
85
+ - **The matrix is a claim too.** A matrix nobody maintains produces confident, wrong verdicts. Treat a rule change as a code change: review, version, cite.
86
+ - **State machines are a different question.** For guards, deadlocks, budget, probability or deadlines, that is `model_verify` and `model-everything-uml` (the diagram round trip).
87
+
88
+ ## Rules for This Mode
89
+
90
+ 1. **Never read a structure diagram as a state machine.** The state-machine front end refuses such a file (`UML_NOT_A_STATE_DIAGRAM`); this skill reads it as the dependency graph it is. The two must not be mixed in one report.
91
+ 2. **Quote the rule id for every judged edge.** "Not allowed" without the rule is an opinion; `matchedRules` makes it a finding.
92
+ 3. **Run the numbers, never the impression.** An isolated node and a back edge are exactly what a reader misses by eye; that is why the check exists.
93
+ 4. **Report the diagram-code difference, not just the diagram.** A structure review that never touches the source is a review of a drawing.
94
+ 5. **Report the rule id with its meaning, never the id alone.** Rule 2 keeps the id on every judged edge, so the finding is defensible; the sentence the reader gets says what the edge violated (「HAL 反向依赖了上层」), in their language. The full rule is under "Writing the Answer" in the `model_verify` skill.
95
+
96
+ The full matrix schema, a worked example and the reconciliation worksheet are in [`references/structure-review-guide.md`](references/structure-review-guide.md).
@@ -0,0 +1,201 @@
1
+ # Structure Review Guide
2
+
3
+ How to audit a component, package, class or deployment diagram as a dependency graph: the matrix schema, every finding code, a worked example, and the method for reconciling the diagram with a source-side check.
4
+
5
+ The tool is `uml_structure_verify`: it audits a component, package, class or deployment diagram as a dependency graph.
6
+
7
+ ## Inputs
8
+
9
+ ```text
10
+ uml_structure_verify { diagram: "<PlantUML or Mermaid class text>", notation?: auto|plantuml|mermaid, matrix?: {...} }
11
+ ```
12
+
13
+ **Accepted diagram families**: PlantUML `component`, `package`, `class`, `interface`, `enum`, `object`, `actor`, `usecase`, `database`, `node`, `artifact`, `deployment`, `rectangle`, `folder`, `frame`, `cloud`, `queue`, `stack`, `storage`, `collections`, `agent`, `boundary`, `control`, `entity`, `namespace`, `module`; Mermaid `classDiagram` (`class X`, `X --> Y : label`, `X ..|> Y`).
14
+
15
+ **Edge forms** (PlantUML): `-->`, `->`, `..>`, `--`, `==>`, `<--`, `<..`. Mermaid class: `-->`, `..>`, `--|>`, `..|>`, `--`, `..`.
16
+
17
+ **What is read**: declarations (with `as` aliases and quoted labels), container nesting (`package "app" { … }`), arrows with labels, and note blocks (skipped — a note is not a dependency). Anything else is reported as `STRUCTURE_IGNORED_LINE` in `warnings`, never silently dropped.
18
+
19
+ **Node identity**: the alias/id when one exists (`component [Order Service] as SVC` → `SVC`), otherwise the declared name. An endpoint that only ever appears in an arrow still becomes a node, marked `declared: false` — that is what `UML021` reports.
20
+
21
+ ## Matrix schema
22
+
23
+ ```jsonc
24
+ {
25
+ "rules": [
26
+ {
27
+ "id": "R1", // required, unique; this is what the report cites
28
+ "source": "app.*", // node id or glob; absent = the rule judges no source
29
+ "allow": ["hal.*"], // permitted targets (glob or id)
30
+ "deny": ["hal.secret"], // forbidden targets; deny wins over allow, globally
31
+ "require": false // true = an edge matching source→allow must exist (UML025)
32
+ }
33
+ ],
34
+ "layers": [
35
+ { "name": "app", "members": ["SVC", "PAY"] }, // top first
36
+ { "name": "hal", "members": ["DRV", "DB"] }
37
+ ],
38
+ "default": "allow" // or "deny": anything unlisted is a violation
39
+ }
40
+ ```
41
+
42
+ Rules:
43
+
44
+ - Glob: `*` matches any run of characters (including `.` and `:`), `?` exactly one. `app.*` matches `app.motion`, not `hal.app`.
45
+ - **Deny is global.** Evaluation collects every rule whose `source` matches the edge, then: any deny match ⇒ violation; else any allow match ⇒ permitted; else the matrix `default` decides (`default` basis vs `unlisted` basis). The verdict cannot depend on rule order.
46
+ - `default: "allow"` is the permissive default: rules are exceptions. `default: "deny"` is the closed-world form and is what most architecture tables actually mean.
47
+ - Layers are independent of rules and are evaluated on every edge whose endpoints both belong to a layer: index order is top→bottom, so `app → hal` is allowed and `hal → app` is `UML024`.
48
+ - An invalid matrix is a hard error (`MATRIX_INVALID`) and no rules apply — a typo must never silently weaken the check. Unknown keys, duplicate rule ids, non-string patterns and `require` without `allow` are all rejected.
49
+
50
+ ## Finding codes
51
+
52
+ | Code | Severity | Evidence | How to read it |
53
+ |---|---|---|---|
54
+ | `UML020_ISOLATED_NODE` | warning | `nodes[{id,line}]` | Declared, non-container, degree 0. Either a dead entry or a forgotten dependency. |
55
+ | `UML021_DANGLING_REFERENCE` | error | `nodes[{id,line}]` | An arrow endpoint no declaration introduced. The dependency targets something the diagram never defines. |
56
+ | `UML022_CYCLE` | error | `cycle[]`, `path[]` | Shortest directed cycle, start repeated (`A → B → C → A`). A self-loop is a cycle of two elements (`A → A`). |
57
+ | `UML023_DISALLOWED_EDGE` | error | `edge{from,to,line}`, `rule?`, `matchedRules[]`, `basis` | `basis: deny` means a rule forbade it; `basis: unlisted` means no rule allowed it under `default: deny`. |
58
+ | `UML024_LAYER_VIOLATION` | error | `violations[{from,to,fromLayer,toLayer,line}]` | An upward edge between declared layers. |
59
+ | `UML025_MISSING_EXPECTED_EDGE` | warning | `missing[{rule,source,expected,unsatisfiedFrom,noSourceMatch}]` | A `require: true` rule the diagram does not satisfy. `noSourceMatch` distinguishes "wrong target" from "no such source node". |
60
+ | `UML026_NO_NODES` | error | `notation` | Nothing to review; the text is not a structure diagram. |
61
+ | `MATRIX_INVALID` | error | — | The matrix was rejected; its messages name the exact field. |
62
+
63
+ Every report also carries `summary` (nodes, edges, containers, isolated, dangling, cycles, disallowedEdges, layerViolations, missingExpectedEdges, errors, warnings, rules), `edgeVerdicts` when a matrix was supplied, and `verdict` / `verdictReason`. A structure diagram with an error finding is `verdict: "fail"` even though `ok: true` — `ok` only means the tool ran.
64
+
65
+ ## Worked example
66
+
67
+ The diagram (a real shape: two packages, an orphan, a back edge, an undeclared endpoint):
68
+
69
+ ```plantuml
70
+ @startuml
71
+ package "app" {
72
+ component [Order Service] as SVC
73
+ component [Payment Adapter] as PAY
74
+ }
75
+ package "hal" {
76
+ component [Storage Driver] as DRV
77
+ }
78
+ component [Orphan Cache] as CACHE
79
+ SVC --> PAY : plan
80
+ PAY --> DRV : read
81
+ DRV --> SVC : fault
82
+ SVC --> GHOST : unknown
83
+ @enduml
84
+ ```
85
+
86
+ Without a matrix the structural checks already speak:
87
+
88
+ ```text
89
+ UML021_DANGLING_REFERENCE (error) GHOST is never declared
90
+ UML020_ISOLATED_NODE (warning) CACHE has no edge
91
+ UML022_CYCLE (error) SVC → PAY → DRV → SVC
92
+ verdict: fail
93
+ ```
94
+
95
+ With the matrix from the skill body (`default: "deny"`, layers `app` over `hal`):
96
+
97
+ ```text
98
+ UML023_DISALLOWED_EDGE (error) DRV → SVC basis=deny (R3-no-back-edges)
99
+ UML023_DISALLOWED_EDGE (error) SVC → GHOST basis=unlisted (no rule allows it)
100
+ UML024_LAYER_VIOLATION (error) DRV (hal) → SVC (app)
101
+ verdict: fail, disallowedEdges: 2, layerViolations: 1
102
+ ```
103
+
104
+ Both `UML022` and `UML023` fire on the same back edge: the cycle is a graph property, the violation is a matrix property. Report both — they are different findings with different fixes (a cycle needs restructuring; a disallowed edge needs either the edge removed or the matrix amended deliberately).
105
+
106
+ ## Reconciliation with a source-side check
107
+
108
+ The diagram and an include/dependency scan over the source are two representations of one fact, and they drift. Keep the rule ids identical on both sides, then classify each difference:
109
+
110
+ | Difference | Reading | Action |
111
+ |---|---|---|
112
+ | Code has the include, diagram has no edge | The diagram is stale | Redraw; `require: true` makes this a `UML025` warning every run |
113
+ | Diagram has the edge, code has no include | Either the diagram is aspirational, or the scan scope is narrower (generated files, `#ifdef`, build-time includes) | State which, with both citations |
114
+ | Code violates a rule the diagram never showed | The architecture defect the diagram hid | Fix the code, or amend the rule deliberately and record why |
115
+ | Both agree an edge is forbidden | A real violation, agreed by two methods | Act on it |
116
+
117
+ Never present the diagram's clean verdict as evidence about the code: it is evidence about the drawing. The reconciliation is the part that touches the source.
118
+
119
+ ## Multi-granularity: one architecture at several levels
120
+
121
+ A module-level diagram and the repository-level diagram it belongs to are two views of one
122
+ fact, and nothing keeps them in step. Declare the relation and both are checked:
123
+
124
+ ```text
125
+ uml_structure_verify { diagrams: [ { name, diagram, parent? }, … ] }
126
+ uml_structure_verify { diagrams: [ { name: "L1", diagram: "…", parent: "L0" }, … ] }
127
+ ```
128
+
129
+ Per-diagram structural checks run first (UML020-UML026), and every finding is tagged with
130
+ the `file` it came from, so a report over several levels never carries an anonymous
131
+ finding. Then each declared pair is checked:
132
+
133
+ | Code | Severity | What it means |
134
+ |---|---|---|
135
+ | `UML028_UNKNOWN_PARENT` | error | The `parent` names a diagram that is not in the set. |
136
+ | `UML029_REFINEMENT_VIOLATION` | error | **invented-edge**: the child draws a dependency between two nodes the parent also has, but the parent does not. **unexpanded-edge**: the parent draws an edge between two nodes the child also has, the child does not draw it, and no child path connects the same ends. |
137
+ | `UML030_PARENT_CYCLE` | error | The parent relation contains a cycle, so "which level owns this dependency" has no answer. |
138
+ | `UML031_NO_DIAGRAMS` | error | Nothing was supplied to compare. |
139
+
140
+ The rule is *refinement*, not equality: a child may add nodes and edges **below** the
141
+ parent level (an edge with an endpoint the parent does not have is the child's own
142
+ detail), and it may cover a **subtree** of the parent — a parent edge whose ends are not
143
+ both present in the child is out of scope for that level, not dropped. What it may never
144
+ do is invent a dependency at the parent level, or silently drop one it should have
145
+ expanded.
146
+
147
+ Each pair is summarised so the counts can be read without diffing two diagrams by eye:
148
+
149
+ ```json
150
+ { "parent": "L0", "child": "L1",
151
+ "parentNodes": 3, "childNodes": 4, "parentEdges": 2, "childEdges": 4,
152
+ "inheritedEdges": 1, "newEdges": 2,
153
+ "expandedEdges": [{ "from": "SVC", "to": "PAY", "line": 5 }],
154
+ "missingEdges": [], "inventedEdges": [] }
155
+ ```
156
+
157
+ A parent edge the child replaces with a path is a **legitimate expansion**, and it is
158
+ listed in `expandedEdges` rather than silently accepted: a reviewer confirms each one,
159
+ because that is where a refactoring quietly changes who calls whom. `hashes.diagrams`
160
+ records a sha256 per level, so a baseline diff can tell which level moved.
161
+
162
+ The same discipline as the single-diagram review applies: this checks the *diagrams*
163
+ against each other, not against the code. A level that is consistent with its parent but
164
+ not with the code is a drawing bug; a level that disagrees with its parent is a hierarchy
165
+ bug. Both need the source-side scan to settle.
166
+
167
+ ## Extracting the matrix from an existing rule table
168
+
169
+ A machine-checked rule table (an include/dependency checker, a lint config, a review checklist) can seed the matrix: one rule entry per row, `id` = the row's own number so citations line up, `source`/`allow`/`deny` = the row's patterns, `layers` = the layering the table describes. Review the result before trusting it — a translation that silently widens `deny` into `allow` is worse than no check. Keep both files under version control together, and treat a matrix change as a code change.
170
+
171
+ ## Where each class of check comes from
172
+
173
+ The distinction matters for how much weight a finding carries. A **published method** has a
174
+ definition and a body of work behind it. A **house convention** is this plugin's own rule: the
175
+ concern is standard, the decision procedure is ours.
176
+
177
+ | Codes | Method | Provenance |
178
+ |---|---|---|
179
+ | `UML020_ISOLATED_NODE` | Connectivity, and the degree-0 vertices of a graph | Published, as graph theory. The reading - "a declared node with no edge is a dead entry or a forgotten dependency" - is this guide's |
180
+ | `UML021_DANGLING_REFERENCE` | Referential closure: an edge endpoint must name a declared vertex | Published, as graph theory. In UML terms it is the notation's own well-formedness expectation that a relationship has two ends; see the UML review checklist, which separates normative constraints from tool rules |
181
+ | `UML022_CYCLE` | Cycle detection with the shortest cycle named | Published. The algorithm is standard; the **shortest** cycle as the reported witness follows the counterexample-minimality idea the verification engine uses for A7 |
182
+ | `UML023_DISALLOWED_EDGE` | Edge legality against a declared rule set: `deny` beats `allow`, and a `default` decides the rest | **Convention.** Conjunctive rule tables over a graph are common practice; this evaluation order is defined in `src/structure.ts` |
183
+ | `UML024_LAYER_VIOLATION` | Layer order over a declared partition: a later band may not depend on an earlier one | **Convention**, and it checks the *declaration* rather than deriving it. `layerOf` reads `matrix.layers` and nothing else, so a design with no declared layers has no layer order to violate - which is a `skip`, not a pass |
184
+ | `UML025_MISSING_EXPECTED_EDGE` | A declared rule with `require` whose edge the diagram does not draw | **Convention.** The idea of asserting a required dependency is standard; the `require` semantics and the `noSourceMatch` distinction are this plugin's |
185
+ | `UML026_NO_NODES` | Input completeness: no vertex means no graph to audit | **Convention**, and deliberately an error rather than an empty pass: auditing nothing must not read as "nothing wrong" |
186
+ | `UML028`-`UML031` | Refinement between levels: a child must not invent a parent-level dependency, and the parent relation must be a forest | **Convention.** Refinement checking has a literature (alloy/VDM refinement); the specific rules here are defined in `src/granularity.ts` |
187
+
188
+ Graph theory has more to say about a dependency graph than this audit asks it. Five questions
189
+ are computable today and deliberately not asked here: the minimum feedback arc set (NP-hard,
190
+ Garey & Johnson 1979 - `model_design_review` decides it for small cyclic parts), community
191
+ detection and spectral clustering (Newman & Girvan 2004; Fiedler 1973 - and unreliable at the
192
+ size of a typical design, see `model-everything-design`), betweenness centrality (Freeman 1977),
193
+ and deriving the layers from the graph instead of checking declared ones.
194
+
195
+ ## Limits
196
+
197
+ - The graph is what the diagram draws. Function pointers, DI, registries, plugins and dynamic dispatch are invisible.
198
+ - No transitive closure unless you write the rule for it.
199
+ - Containers (`package`, `rectangle`, `frame`) are exempt from the isolated-node check by design.
200
+ - `STRUCTURE_IGNORED_LINE` warnings mean the parser met a statement it does not model; treat a non-empty list as a signal that the diagram family may not be the one you think it is.
201
+ - Behaviour is out of scope: state machines, protocols, timing, budget, probability and deadlines belong to `model_verify` (verification) and `model-everything-uml` (the diagram round trip).
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: model-everything-uml
3
+ description: "Use when drawing a UML diagram or auditing a hand-drawn one: parse it as a state, activity, timing, profile or composite structure model, render a model in a notation that can draw it, and check round-trip fidelity. A component, class, object, package or deployment diagram is read as a dependency graph by the model-everything-structure skill; an entity-relationship diagram is not a UML family and is refused, with the reason. Doctrine: the model-everything skill."
4
+ ---
5
+
6
+ # Model Everything — UML Modelling
7
+
8
+ A diagram is a model, and a model can be wrong. A tidy flow chart is not evidence about the code, and a diagram that disagrees with its own model is worse than no diagram, because a reader will believe it.
9
+
10
+ This skill covers the diagram half of this toolbox. The claim-verification doctrine it inherits — enumerate verifiable claims, cite `file:line`, read `verdict` and never `ok` — lives in the `model-everything` skill; load that one when the task is to verify behaviour rather than to draw or audit a diagram.
11
+
12
+ ## When This Skill Applies
13
+
14
+ | The task involves… | What it does |
15
+ |---|---|
16
+ | Modelling a code flow as a state, activity or sequence diagram | `uml_render`: model to diagram source, with a citation per state, event, guard and action |
17
+ | Reading a hand-drawn diagram back into a model | `uml_parse`: Mermaid or PlantUML state/activity text into a LogicModelV1 that `model_verify` can check |
18
+ | Auditing a diagram somebody drew | `uml_review`: structural defects, documentation gaps, and render-versus-model round-trip fidelity |
19
+ | Printing what the parser accepts for labels and comments | `action=explain-labels`: the accepted label spellings, the form the renderer writes, and which lines are ignored |
20
+ | A diagram whose claims must be tied to the code | Every element needs a source citation; the review says what the diagram says, not whether the code is correct |
21
+
22
+ Pipeline (DSH):
23
+
24
+ ```text
25
+ code flow → model (citation per element) → uml_render → diagram source
26
+ → uml_review → modelling findings + round-trip fidelity
27
+ → model_verify → behaviour (S1-S8 structural, A1-A14 adversarial)
28
+ ```
29
+
30
+ - **render** — model to diagram. Mermaid covers the state, activity, sequence and `interaction-overview` families. PlantUML covers the state, sequence, timing, profile, composite structure, communication, class, object, package, component and deployment families. Any construct the notation cannot carry becomes a warning, never a silent drop. PlantUML activity is refused instead of approximated, and a family the notation only approximates is drawn with an `UML_RENDER_APPROXIMATE_VIEW` warning that names what the picture cannot say.
31
+ - **parse** — diagram to model. It reads Mermaid and PlantUML state or activity text, so a hand-drawn diagram can be verified like any other model. A PlantUML timing diagram is read as its own model (lifelines, states over time, messages, duration constraints), and a composite structure or communication diagram reads back exactly (nesting and port direction; lifelines, links and the numbered messages). Inputs are refused with the reason when they cannot reconstruct a model: a sequence diagram (a trace cannot reconstruct a machine), a profile (an extension and a generalization are the same arrow), an interaction overview (a flowchart node names an interaction rather than being one), an entity-relationship diagram (not a UML 2.5 family), a different Mermaid family (`classDiagram`, `erDiagram`, `gantt`, `mindmap`, …). Each comes back with its `errorCode` and, where the diagram carried constructs that were dropped, the construct named. The body carries the model it read (`MODEL`) and how it read the text (`PARSE`), and each note the parser made is a finding — a note that changed the verdict but appeared in no roster read as "nothing was reported".
32
+ - **review** — it answers one of three questions. Give it a model: is the machine well-modelled? Give it a diagram: what does the diagram say? Give it both: does the diagram match the model?
33
+ - **explain-labels** — the parser's own account of labels and comments: the accepted spellings, the form the renderer writes, and which lines it ignores. No diagram needed. It answers "why does my hand-drawn diagram report drift or ignored lines?" without reading the source.
34
+
35
+ The view follows the question, not the machine, and no tool picks it for you. Declare the question with `intent` (`present` / `verify` / `fidelity`) and a view that cannot serve it is refused before any work, with a code that names why. The ordered procedure, the intent-by-action table and those codes are under [Choosing the view](references/uml-modeling-guide.md#choosing-the-view).
36
+
37
+ A host without the tools runs the same four actions through the repository engine. The engine is deliberately single-source — do not copy it into this skill.
38
+
39
+ ## A Structure Diagram Is the Dangerous Case, Because It Parses
40
+
41
+ PlantUML component, package, class and deployment diagrams declare no diagram kind, so the parser meets their keywords line by line. It still returns *something* — the arrows look like transitions — so `parse` reports the by-product model **plus** an error finding `UML_NOT_A_STATE_DIAGRAM`, the declarations it could not represent in `discardedConstructs` (with line and text), the arrows that were misread in `discardedEdges`, and `verdict: "fail"`.
42
+
43
+ Read that as: **there is no model of this file; the diagram was not checked.** Do not feed that model to `model_verify` and call the result a review of the architecture, and do not present a component diagram as evidence about dependencies. A structure diagram has its own reviewer: the `model-everything-structure` skill and the `uml_structure_verify` tool parse the same text into a real dependency graph and check isolated nodes, dangling endpoints, cycles, allowed-dependency and layer violations (`UML020`-`UML026`). Use that one when the question is *where the dependencies point*.
44
+
45
+ Mermaid families with an explicit header (`classDiagram`, `erDiagram`, `gantt`, …) are refused outright, because an empty model would be worse than no model.
46
+
47
+ ## What the Review Reports
48
+
49
+ Structural defects: unreachable states, dead ends, ambiguous or non-exhaustive branches, self-loops with no exit, duplicate transitions. Documentation gaps: a missing narrative, unbounded variables, states the reader cannot map back to code, and label drift between diagram and narrative. It also runs the fidelity check — any structural difference between the diagram and its model is `UML017_ROUND_TRIP_MISMATCH`.
50
+
51
+ ## Rules for This Mode
52
+
53
+ 1. **A diagram is not evidence.** Every state, event, guard and action needs a citation. Use `file:line` for code and a section reference for a document. Present the citations with the diagram.
54
+ 2. **Review before you present.** Run `uml_review` and fix the error findings first. An ambiguous or dead-ended diagram misleads every later reader.
55
+ 3. **Check the verdict before the picture.** `ok: true` with `verdict: "fail"` means the review failed; a diagram whose review failed is not something to show as if it were checked.
56
+ 4. **The review never replaces verification.** It covers the modelling. S1-S8 and A1-A14 cover the behaviour, and each finding names the check that settles it — hand those over to the `model_verify` skill.
57
+ 5. **Keep the narrative with the model.** Write `narrative.states`, `narrative.events` and `narrative.scenarios`. Then the diagram stays readable against the code, and label drift shows up as a finding instead of as a stale picture.
58
+ 6. **Name a finding for the reader, not by its code.** `UML017_ROUND_TRIP_MISMATCH` is a report field; the sentence the reader gets says what disagreed (「图里少了 ERROR→COOLDOWN 这条迁移」), in their language. Keep the code for a report field quoted verbatim, a diff or a test name. The full rule is under "Writing the Answer" in the `model_verify` skill.
59
+
60
+ The full checklist (codes `UML001` to `UML019` plus `UML_NOT_A_STATE_DIAGRAM`), the directive format generated diagrams carry, a worked example, and the limits of each view are in [`references/uml-modeling-guide.md`](references/uml-modeling-guide.md).