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,551 @@
1
+ # UML Modelling Guide
2
+
3
+ This guide covers two jobs: drawing a code flow as UML, and reviewing that drawing. Load it when the task is "show me the flow" rather than "check this claim". Typical cases are reverse-engineering a handler, documenting a protocol, or auditing a diagram somebody else drew.
4
+
5
+ One rule runs through the whole guide. **A diagram is a model, and a model can be wrong.** A tidy flow chart is not evidence about the code. Worse, a diagram that disagrees with the model it came from will mislead every reader who trusts it.
6
+
7
+ ## Choosing the view
8
+
9
+ The view follows the question, not the machine. Decide in this order.
10
+
11
+ **1. Is the question about where dependencies point?** Architecture, layering, module
12
+ structure, "may this call that" — that question belongs to the `model-everything-structure`
13
+ skill, which reads the same component, package, class or deployment text as a dependency
14
+ graph. The state front end refuses that text by name
15
+ (`UML_NOT_A_STATE_DIAGRAM`) instead of reviewing it.
16
+
17
+ **2. Otherwise fix the boundary first, then pick the view by the reader's question.**
18
+
19
+ | Situation | View that answers it |
20
+ |---|---|
21
+ | "What are the states of this handler, and what moves between them?" | State machine |
22
+ | "What does this function actually do, including its error paths?" | Activity (flow) |
23
+ | "In what order do these messages arrive, and what is the state after each?" | Sequence |
24
+ | "Somebody gave me a diagram. Is it right?" | Any view. Feed it to the review and compare it against the code. |
25
+ | "This diagram and this model disagree." | Feed both to the review. The round-trip check names every difference. |
26
+
27
+ **3. Check the choice against the next step.** A view that cannot feed the check you are
28
+ about to run is the wrong view, however readable it looks.
29
+
30
+ | Next step | Views that can feed it |
31
+ |---|---|
32
+ | `model_verify` (S1-S8 and A1-A14) | State or activity: `parse` reads either into a LogicModelV1 |
33
+ | `uml_review` with a model, for the fidelity check | State or activity: only those round-trip |
34
+ | `uml_review` with a model alone | Any view. With `kind: "sequence"` the trace is rendered and the fidelity check is reported inapplicable (`UML019`) |
35
+ | A human reading a protocol exchange | Sequence, which is the honest view there |
36
+
37
+ **4. Say that out loud, and the tool refuses a pairing that cannot serve it.** `intent`
38
+ declares what the artifact is for. The check runs before any work, so an impossible
39
+ pairing is a refusal with a code rather than a report about the wrong artifact.
40
+
41
+ | Intent | What it means | Actions that serve it | View it needs |
42
+ |---|---|---|---|
43
+ | `present` | a human is going to read the diagram | render, review | any view the notation can draw |
44
+ | `verify` | it must be parseable into a LogicModelV1 for the engine | render, parse, review | state or activity: a trace cannot be parsed back |
45
+ | `fidelity` | the point is the render-and-reparse proof | review | state or activity: only those round-trip |
46
+
47
+ | `errorCode` | What happened |
48
+ |---|---|
49
+ | `UML_INTENT_UNSERVABLE` | The view exists but cannot serve the intent — a sequence view for `verify` or `fidelity`. |
50
+ | `UML_VIEW_UNREPRESENTABLE` | The `(notation, view)` cell does not exist — PlantUML activity. |
51
+ | `UML_INTENT_MISMATCH` | The intent names something this action does not do — `fidelity` on render (render proves nothing), `present` or `fidelity` on parse (parse returns a model). |
52
+
53
+ Omitting `intent` changes nothing: a sequence view still renders, and a review of one
54
+ still reports `UML019`. The intent is an opt-in statement of purpose, and nothing infers
55
+ it.
56
+ When the question is still unclear, model the state machine. It is the safe default, and
57
+ the other views render from the same model afterwards.
58
+
59
+ ### Nothing picks the view for you
60
+
61
+ `render` takes `kind` from the caller and defaults to `state`; it never guesses. A guess
62
+ between state and activity would be a presentation decision, and the machine cannot make
63
+ it: the same guarded branch is a state machine to one reader and an error path to another.
64
+ `parse` does detect the notation and the diagram family from the text, and it refuses the
65
+ families it cannot model, by name.
66
+
67
+ Mechanically decidable, and nothing else:
68
+
69
+ - the notation and the family the text declares — a Mermaid header (`stateDiagram`,
70
+ `flowchart`, `sequenceDiagram`, or a known other-family header) or a PlantUML body
71
+ keyword;
72
+ - whether the requested notation-and-kind combination exists at all (PlantUML has no
73
+ activity view here);
74
+ - whether a view can carry the fidelity check (a sequence cannot).
75
+
76
+ Everything else is a fact about the question, not about the artifact. A model can tell you
77
+ its shape — its state count, its guarded branches, its cycles — and its shape does not
78
+ choose the view.
79
+
80
+ Do not use this guide for a behavioural claim about a specific machine. That is the job of
81
+ `model_verify` (S1-S8 and A1-A14). The review here covers the modelling, not the
82
+ behaviour. Every finding it produces names the engine check that settles the behavioural
83
+ half.
84
+
85
+ ## The three views
86
+
87
+ One machine, three projections. They are not interchangeable. The review reports which view it saw, and the differences matter.
88
+
89
+ **State machine** (`stateDiagram-v2`, or a PlantUML state diagram). This view shows the whole topology: every state, every event-and-guard branch, and every terminal state as `--> [*]`. Model code flow into this view, and use it for review. It is the only view that round-trips without loss.
90
+
91
+ **Activity** (`flowchart TD` in Mermaid). This view draws the same machine as work rather than as states. Each edge carries `event [guard] / actions`. It suits a reader who thinks in steps. It holds the same information as the state view. PlantUML activity is **not** generated: its structured-flowchart syntax needs a while/if reconstruction for any graph with a merge or a cycle. A diagram that quietly reshapes the machine is exactly the failure this feature exists to prevent, so the tool refuses that combination.
92
+
93
+ **Sequence** (`sequenceDiagram`). This view shows one BFS trace: the messages in the order a walker meets them, with a note per state change. A trace is not a machine. Branches appear as separate guarded messages, paths the walk never took are absent, and the trace is capped. Use it to show a protocol exchange to a human. It cannot be the review's fidelity input, because `parse` refuses it and `review` reports the check as inapplicable (`UML019`).
94
+
95
+ ## Modelling a code flow from source
96
+
97
+ Extraction follows the same discipline as `logic-verification-guide.md`, applied to code instead of a plan. That guide ships with the entry-point skill: `../model-everything/references/logic-verification-guide.md`.
98
+
99
+ 1. **Fix the boundary.** Decide which function, task, or module is the machine. In scope: state the code holds across calls, such as statics, fields, enums, and task state variables. Out of scope: the call stack inside one invocation, hardware behaviour, and scheduler preemption.
100
+ 2. **Name the states from the code.** A state exists if something survives a call boundary and is tested later. An enum, a `state` field, or a task-local variable that gates the next entry all qualify. A local variable inside one function does not.
101
+ 3. **Name the events from the code.** Every edge must trace to a call site, a message, a timer expiry, or an ISR. An edge with no call site is a guess. Put it in the review findings instead of the model.
102
+ 4. **Record guards and actions verbatim.** A guard must be the real condition, with the real variable and the real constant. A retry limit of 3 modelled as "a few" is a model of your assumption, not of the code.
103
+ 5. **Mark the terminals.** An absorbing state is `terminal: true`. Examples are power-off, fatal, and done. If nothing is terminal, say so. The review will flag it either way.
104
+ 6. **Write the narrative as you go.** The `narrative` block holds natural-language meanings for states and events, plus a scenario for each state-and-event pair. It is what lets a reader check the diagram against the code without re-deriving every symbol. Once the block is present, the schema requires all three parts and full coverage, so it cannot rot half-way.
105
+
106
+ ### Evidence rule
107
+
108
+ Every state, event, guard and action needs a citation. Use `file:line` for code and a section reference for a document. A UML model without citations is a drawing. It cannot be reviewed, only admired. Present the citations together with the model.
109
+
110
+ ### Confirming the model
111
+
112
+ The usual gate applies: show the extracted transition table or the diagram, and get confirmation before treating the model as fact. When the user has already told you to proceed without confirmation in this session, skip the question; instead cite evidence for every element, round-trip the model (render, parse, compare), and mark the result `UNCONFIRMED`.
113
+
114
+ ## Rendering
115
+
116
+ In DSH:
117
+
118
+ ```json
119
+ { "action": "render", "model": { "...LogicModelV1..." }, "notation": "mermaid", "kind": "state" }
120
+ ```
121
+
122
+ `uml_render` returns the diagram source plus `warnings`. Read those warnings. They list every construct the notation could not carry verbatim: a state id that needed an alias, a label containing `[` or `/`, a trace that was capped.
123
+
124
+ Without the DSH tool, run the repository engine on the same model JSON.
125
+
126
+ You can also write the diagram by hand. The generator is a convenience, not a requirement. A hand-written diagram is parsed and reviewed exactly like a generated one.
127
+
128
+ ### Directives in generated diagrams
129
+
130
+ Generated text carries comment lines. Mermaid and PlantUML ignore them. The parser reads them:
131
+
132
+ ```text
133
+ %%dsh-model-everything:uml v1 notation=mermaid diagram=state
134
+ %%dsh-model-everything:init INIT
135
+ %%dsh-model-everything:terminal FATAL
136
+ %%dsh-model-everything:alias S_1 1st state
137
+ %%dsh-model-everything:variable retry integer
138
+ ```
139
+
140
+ These lines exist because the notation cannot express everything the model knows. `[*]` marks an initial state, but a flowchart has no such marker. A state id may contain characters the notation cannot spell. A boolean assignment (`armed := 1`) looks exactly like an integer one. Pinning those facts in comments is what makes the round-trip check exact instead of approximate. PlantUML uses `'` instead of `%%`.
141
+
142
+ A hand-written diagram needs none of these directives. Adding `%%dsh-model-everything:init` and `%%dsh-model-everything:terminal` to a flowchart is how you say which node starts and which one ends.
143
+
144
+ ## Reviewing the modelling
145
+
146
+ ```json
147
+ { "action": "review", "model": { "...LogicModelV1..." } }
148
+ ```
149
+
150
+ Four input shapes, four different questions:
151
+
152
+ | Input | Question answered |
153
+ |---|---|
154
+ | `model` only | Is this machine well-modelled? The review checks its structure, then renders and re-parses it to prove the diagram carries it. |
155
+ | `diagram` only | What does this diagram actually say? The diagram is parsed into a model, and that model is reviewed. Fidelity to code is **unchecked** (`UML018`). |
156
+ | `model` + `diagram` | Does the diagram match the model? Every structural difference is a modelling defect (`UML017`). |
157
+ | `model` + `kind: "sequence"` | The trace is rendered and the structure is reviewed, but the fidelity check **does not apply** (`UML019`). A trace cannot be parsed back into a machine, so the review says so instead of pretending it verified the diagram. `maxSteps` caps the trace. |
158
+
159
+ ### Findings
160
+
161
+ | Code | Severity | What it means |
162
+ |---|---|---|
163
+ | `UML001_DIAGRAM_UNREADABLE` | error | The text is not readable as a Mermaid or PlantUML state or activity diagram. |
164
+ | `UML_NOT_A_STATE_DIAGRAM` | error | The text belongs to another diagram family. Numbers are reserved for `UML0xx` modelling findings, so this one carries a name instead; see [Structure diagrams are not models](#structure-diagrams-are-not-models). |
165
+ | `UML002_UNREACHABLE_STATE` | error | No transition can enter this state from init. The diagram draws flow nobody can reach. The check is structural and ignores guards; S1 is the guard-aware check. |
166
+ | `UML003_DEAD_END_STATE` | error | A non-terminal state has no outgoing transition. Either it is terminal, or the outgoing flow was never modelled. |
167
+ | `UML004_AMBIGUOUS_BRANCH` | error | Two unconditional arrows share one state and event. No reader and no implementation can resolve that. S4 is the authoritative check. |
168
+ | `UML005_OVERLAPPING_GUARD` | warning | The same guard text appears twice in one branch group. |
169
+ | `UML006_INEXHAUSTIVE_BRANCH` | warning or info | The branch group has only guarded branches and no default. The severity is `warning` when the guards do not look complementary. It is `info` when a complementary pair such as `k < 3` and `k >= 3` is present, which is probably exhaustive. Only S6 can settle it. |
170
+ | `UML007_UNUSED_EVENT` | warning | The event fires only from unreachable states. The diagram shows messages that never arrive. |
171
+ | `UML008_SELF_LOOP_NO_EXIT` | warning | An unguarded self-loop has no other exit. The diagram presents it as progress, but the flow never leaves. S3 reports absorbing cycles. |
172
+ | `UML009_DUPLICATE_TRANSITION` | warning | The same from, event, guard, actions and target row appears twice. |
173
+ | `UML010_UNUSED_VARIABLE` | warning | No guard reads this variable and no action writes it. It is a symbol with no source. |
174
+ | `UML011_UNBOUNDED_VARIABLE` | info | An integer variable has no min or max, so no range invariant can be checked and A5 has no declared domain. |
175
+ | `UML012_NO_TERMINAL` | warning | No state is terminal, so completion, failure and a stuck flow all look the same. |
176
+ | `UML013_NO_NARRATIVE` | info | The model carries no meanings, so a reader must re-derive every symbol from the source. Adding a partial narrative is a valid first step. |
177
+ | `UML027_NARRATIVE_PARTIAL` | info | The narrative covers part of the model. `narrativeCoverage` (also in the finding's `evidence`) reports `covered/total` per dimension — states, events, scenarios. Incomplete is not wrong. |
178
+ | `UML014_UNDOCUMENTED_STATE` | info | These states render as their bare id, so the diagram cannot be read against the code. |
179
+ | `UML015_LABEL_DRIFT` | warning | A diagram label disagrees with the model narrative. One of the two is stale, and the review cannot tell which. |
180
+ | `UML016_DIAGRAM_PARSE_NOTES` | info | Notes collected while rendering or reading the diagram. They mark information the notation could not carry. |
181
+ | `UML017_ROUND_TRIP_MISMATCH` | error | The diagram does not carry the model. Transitions were lost or invented, or init, terminals or variables differ. The `roundTrip.diffs` array lists each one. |
182
+ | `UML018_FIDELITY_UNCHECKED` | info | Only a diagram was supplied, so nothing here proves it matches the code. |
183
+ | `UML019_ROUND_TRIP_SKIPPED` | warning | The fidelity check could not run, either because the view is not round-trippable or because it was switched off. |
184
+
185
+ `ok: true` means the review ran. It does not mean the model is good. Read `verdict`, not `ok`:
186
+
187
+ | `verdict` | What it means | What to do |
188
+ |---|---|---|
189
+ | `pass` | No error and no warning finding. | Safe to present, with the scope the model covers. |
190
+ | `pass_with_findings` | No error finding, but warnings exist (e.g. `UML012_NO_TERMINAL`, `UML006`). | Present the pass **and** the warnings; do not upgrade it to "no issues". |
191
+ | `fail` | An error finding exists (`UML002`, `UML003`, `UML004`, `UML017`, `UML_NOT_A_STATE_DIAGRAM`, …) or the diagram could not be read (`ok: false`). | The diagram is **not** reviewed. Fix the error findings, or stop calling the file a model. |
192
+
193
+ `verdictReason` states why in one line (`2 error finding(s) (first: UML002_UNREACHABLE_STATE)`).
194
+ A refused call returns `ok: false` with an `errorCode` that says why; a review that ran and failed carries `verdict: fail` with its error findings.
195
+
196
+ ### Structure diagrams are not models
197
+
198
+ This is the failure mode worth a section of its own, because it does not look like a failure.
199
+
200
+ A PlantUML component, package, class or deployment diagram declares no diagram kind —
201
+ it simply starts with `@startuml`. A state parser therefore meets its keywords line by
202
+ line, ignores the words it does not know, and reads the arrows as state transitions:
203
+
204
+ ```plantuml
205
+ @startuml
206
+ component [Order Service] as SVC
207
+ component [Payment Adapter] as PAY
208
+ package "HAL" {
209
+ component [Storage Driver] as DRV
210
+ }
211
+ SVC --> PAY : request
212
+ PAY --> DRV : read
213
+ @enduml
214
+ ```
215
+
216
+ Before this contract existed, that parsed to `ok: true` with three "states" and two
217
+ "transitions", and "the structure diagram was reviewed" was pure fabrication. Now the
218
+ same input returns the by-product **plus**:
219
+
220
+ ```json
221
+ {
222
+ "verdict": "fail",
223
+ "findings": [{ "code": "UML_NOT_A_STATE_DIAGRAM", "severity": "error",
224
+ "message": "the text is not a state or activity diagram: 4 declaration(s) of unsupported construct(s) (component ×3, package ×1) and 2 arrow(s) between them were read as states and transitions, so the parsed model is not this diagram." }],
225
+ "discardedConstructs": [{ "construct": "component", "line": 2, "text": "component [Order Service] as SVC" }, "…"],
226
+ "discardedEdges": 2
227
+ }
228
+ ```
229
+
230
+ Read that as: **there is no model of this file.** Do not feed the by-product to
231
+ `model_verify` and call the result an architecture review, and do not quote its
232
+ "states" as dependencies. `discardedConstructs` lists every declaration that could not
233
+ be represented (with its line and text), and `discardedEdges` counts the arrows between
234
+ them that were misread.
235
+
236
+ Mermaid families with an explicit header — `classDiagram`, `erDiagram`, `gantt`,
237
+ `journey`, `mindmap`, `gitGraph`, `C4*`, `requirementDiagram`, `timeline`,
238
+ `quadrantChart`, `sankey-beta`, `block-beta`, `packet-beta`, `architecture-beta`,
239
+ `radar-beta`, `treemap-beta`, `xychart-beta` — are refused instead, because an empty
240
+ model would be worse than no model: the tool returns `ok: false` with
241
+ `errorCode: "UML_NOT_A_STATE_DIAGRAM"` naming the family, and `review` reports it as
242
+ `UML001_DIAGRAM_UNREADABLE`. Sequence diagrams are in the same class of refusal, for a
243
+ different reason (a trace cannot reconstruct a machine).
244
+
245
+ Both routes are non-pass and both name what was discarded. What the tool will **not**
246
+ do is hand back a plausible model with no verdict — that is the false guarantee.
247
+
248
+ **A structure diagram has its own reviewer.** Dependency-graph checks — isolated
249
+ nodes (`UML020`), dangling endpoints (`UML021`), cycles (`UML022`), disallowed edges
250
+ and layer violations against an allowed-dependency matrix (`UML023`, `UML024`) and
251
+ required-but-absent edges (`UML025`) — are implemented by the `model-everything-structure`
252
+ skill and the `uml_structure_verify` tool, which parse the same component,
253
+ package, class and deployment text into a real node/edge graph instead of discarding
254
+ it. Use that for architecture questions; this guide is about the state-machine half.
255
+
256
+ ### Use case diagrams
257
+
258
+ A use case diagram is read as its own model (actors, use cases, associations, include,
259
+ extend, generalization), never as a machine. Its findings are these, and the severity
260
+ follows the source of the rule: an error is a UML 2.5 well-formedness constraint, while
261
+ a warning or an info is a completeness or style rule named by the tool that defines it.
262
+
263
+ | Code | Severity | What it means |
264
+ |---|---|---|
265
+ | `UML030_USE_CASE_NO_ACTORS` | error | No actor is declared, so the diagram does not say who uses the system. |
266
+ | `UML031_USE_CASE_NO_USE_CASES` | error | No use case is declared, so the diagram does not say what the system offers. |
267
+ | `UML034_DANGLING_RELATION_ENDPOINT` | error | A relation names an element the diagram never declares. The line points at nothing a reader can look up. |
268
+ | `UML035_INCLUDE_EXTEND_CYCLE` | error | Include and extend are cyclically dependent, so the use cases can never be performed in a finite order. The detail names the path. |
269
+ | `UML036_ACTOR_TO_ACTOR_ASSOCIATION` | error | An association joins two actors. Actors do not use each other: a shared role is a generalization, and shared behaviour belongs in a use case. |
270
+ | `UML037_ACTOR_INCLUDE_EXTEND` | error | An actor includes or extends a use case. Only use cases include or extend each other; an actor reaches a use case by an association. |
271
+ | `UML038_SELF_RELATION` | warning | An element relates to itself, which says nothing. |
272
+ | `UML032_ISOLATED_ACTOR` | info | An actor takes part in nothing. The tool rule that defines this check reports it at info, and an unused actor alone is not a modelling error: a diagram may be drawn before its use cases are. |
273
+ | `UML033_USE_CASE_WITHOUT_ACTOR` | warning | No actor reaches this use case, directly or through an include or extend, so nothing says who triggers it. |
274
+ | `UML039_USE_CASE_UNDOCUMENTED` | info | A use case carries neither a display label nor a narrative entry, so a reader cannot map the id back to the system. |
275
+
276
+ That table is the use case half of the review. It is not the review of the class,
277
+ object, package, component or deployment families: those reviews are the dependency
278
+ checks above, plus `UML040_STRUCTURE_NODE_UNDOCUMENTED` (info) when a node carries no
279
+ label. The per-family well-formedness constraints those diagrams have in the
280
+ specification are not implemented as findings — see the coverage table in the project
281
+ memory before claiming a structure diagram is clean.
282
+
283
+ ### Timing diagrams
284
+
285
+ A timing diagram is an interaction against a clock: which participants exist, what state
286
+ each is in over time, and what they send each other. PlantUML is the only notation here
287
+ (`robust`, `concise`, `binary`, `clock` with a period, `analog`, `rectangle`); Mermaid
288
+ has no timing diagram, and the tool refuses by name instead of drawing something else.
289
+
290
+ The model is `TimingModelV1`: `lifelines`, `states`, `segments`, `messages`,
291
+ `constraints`, and an optional `unit` for the axis. Two rules follow from what the
292
+ notation can actually say, and both are enforced rather than assumed:
293
+
294
+ - **A segment has no end time.** A lifeline is a list of changes; the next change on the
295
+ same lifeline *is* the end of the previous state. A model that also carried an end would
296
+ be carrying a field the diagram cannot express, and the round trip would drop it.
297
+ - **A state exists only where the diagram shows it.** "Which id is this drawn label?" is
298
+ answered by a `dsh-model-everything:timingState` directive, never by inventing a second
299
+ state. A state declared in the model but drawn nowhere is `UML048`.
300
+
301
+ | Code | Severity | What it means |
302
+ |---|---|---|
303
+ | `UML042_TIMING_NO_LIFELINES` | error | No lifeline is declared, so there is no participant whose state over time the diagram could show. |
304
+ | `UML043_TIMING_EMPTY_LIFELINE` | warning | A lifeline carries no state change and no message: it is drawn but says nothing. |
305
+ | `UML044_TIMING_TIME_ORDER` | error | The segments are written out of time order, so an earlier state would end before it begins. Negative times are legal — the run-up before the moment the reader cares about; what is not legal is an axis that goes backwards. |
306
+ | `UML045_TIMING_SIMULTANEOUS_STATES` | error | One lifeline is put into two states at the same moment. A renderer keeps only the last, so the model says something the diagram cannot. |
307
+ | `UML046_TIMING_UNDECLARED_REFERENCE` | error | A message or a segment names an element no declaration introduced. PlantUML draws a bare timeline for it, which is how a typo stays invisible. |
308
+ | `UML047_TIMING_CONSTRAINT_RANGE` | error | A duration constraint ends before it starts. |
309
+ | `UML048_TIMING_STATE_UNUSED` | warning | A state is declared but no lifeline is ever monitored in it. |
310
+ | `UML049_TIMING_UNDOCUMENTED` | info | A lifeline or state carries no name a reader can map back to the system. |
311
+
312
+ What the module does **not** carry, and names in `discardedConstructs` rather than
313
+ dropping: participant blocks (`@WB` with per-lifeline times), anchor points
314
+ (`@XX as :name`), a state's comment or colour (`is Waiting : note`, `is Waiting #LightCyan`),
315
+ `highlight`, `note top of`, `scale`, date axes, `mode compact`, and the `has` state-order
316
+ declaration. Each is a real construct of the notation and none of them is a state or a
317
+ message, so they are reported by name. A clock's `pulse` and `offset` are reported the
318
+ same way: the model carries the period.
319
+
320
+ ### Profile diagrams
321
+
322
+ A profile extends the metamodel: a package of stereotypes, the metaclasses they extend,
323
+ and the applications that put the profile to use. PlantUML draws one **approximately**,
324
+ and this family is the case where the honest answer is a one-way view:
325
+
326
+ - it has `metaclass` and `stereotype` elements and draws `class X <<stereotype>>`;
327
+ - it has **no** profile element and no ProfileApplication, so the profile package itself
328
+ is never drawn;
329
+ - an **extension** and a **stereotype generalization** are both a `--|>` arrow.
330
+
331
+ That last one is why `uml_parse` refuses this family by name: reading the text back would
332
+ have to decide which fact each arrow carries, and the notation does not say. So the model
333
+ is the record and the drawing is a view of it:
334
+
335
+ | Action | What happens |
336
+ |---|---|
337
+ | `uml_render` with `kind: "profile"` | Draws the approximation, with an `UML_RENDER_APPROXIMATE_VIEW` warning that says what it cannot carry. Every fact the drawing cannot show (which arrow is an extension, its multiplicity and role, the applications) is written as a `dsh-model-everything:profileExtension` / `:profileApply` / `:profile` directive, so the text is a faithful record even though the picture is not. |
338
+ | `uml_parse` | Refused with `UML_VIEW_UNREPRESENTABLE` and the reason. Use the model. |
339
+ | `uml_review` with `model` and `diagramKind: "profile"` | Reviews the model. This works, and is the intended path. |
340
+ | `uml_review` with `diagram` | Returns one `UML018_FIDELITY_UNCHECKED` info finding saying the text was not read — not a pass, and not an error either. |
341
+
342
+ The model is `ProfileModelV1`: `profile`, `stereotypes`, `metaclasses`, `extensions`
343
+ (with the extension end's `multiplicity` and role `label`), `applications`, and
344
+ `generalizations` (kept apart from extensions, because they are different facts).
345
+
346
+ | Code | Severity | What it means |
347
+ |---|---|---|
348
+ | `UML050_PROFILE_NO_STEREOTYPES` | error | The profile declares no stereotype, so it extends the metamodel with nothing. |
349
+ | `UML051_PROFILE_UNUSED_STEREOTYPE` | warning | A stereotype extends no metaclass and specialises no stereotype: nothing in the profile uses it. |
350
+ | `UML052_PROFILE_EXTENSION_MISSING` | warning | A metaclass is declared that no stereotype extends, so the profile does not change it. |
351
+ | `UML053_PROFILE_GENERALIZATION_CYCLE` | error | The stereotype hierarchy is cyclic, so which stereotype is the general one has no answer. The `detail` names the path. |
352
+ | `UML054_PROFILE_UNDECLARED_REFERENCE` | error | An extension, generalization or application names an element no declaration introduced. |
353
+ | `UML055_PROFILE_APPLICATION_MISSING` | info | The profile is applied to no package: nothing says where it is meant to be used. |
354
+ | `UML056_PROFILE_UNDOCUMENTED` | info | The profile or a stereotype carries no name a reader can map back to the specification it extends. |
355
+
356
+ The checks read the model alone. Whether a profile is *right* for the metamodel it
357
+ extends is a question about the specification being profiled, and that is not answered
358
+ here. The specification's own constraints on profiles — an extension end's aggregation,
359
+ `isRequired`, the ProfileApplication correspondence — need the drawing to carry the
360
+ extension endpoints and the application, which this notation does not.
361
+
362
+ ### Composite structure diagrams
363
+
364
+ A composite structure diagram is a classifier seen from the inside: the parts it is made of,
365
+ the ports on its boundary, and the connectors between them. PlantUML has no page for it, so
366
+ the drawing is an approximation built from the deployment diagram's vocabulary - and unlike
367
+ a profile, this one **reads back exactly**, because nesting, port direction and both
368
+ connector ends are all spelled.
369
+
370
+ | Action | What happens |
371
+ |---|---|
372
+ | `uml_render` with `kind: "composite-structure"` | Draws a nested element for the classifier, the nestable elements for its parts, `port`/`portin`/`portout` for its boundary, and arrows for the connectors. An `UML_RENDER_APPROXIMATE_VIEW` warning names what the notation cannot carry. |
373
+ | `uml_parse` | Reads it back. The round trip is measured, not assumed, and `uml_review` proves it every time it renders a model. |
374
+ | `uml_review` | Reports the family's own findings, and checks fidelity by rendering the model and reading it back. |
375
+
376
+ The model is `CompositeModelV1`: `self` (the classifier, with the keyword it is drawn with),
377
+ `parts`, `ports` (each with `in` / `out` / `inout`), and `connectors` (each end naming the
378
+ classifier, a port, or a part). There is no nesting: a part's inside is a second diagram.
379
+
380
+ | Code | Severity | What it means |
381
+ |---|---|---|
382
+ | `UML057_COMPOSITE_NO_PARTS` | error | The classifier is drawn as a composite structure but holds no part. |
383
+ | `UML058_COMPOSITE_PORT_UNCONNECTED` | warning | A port has no connector, so nothing reaches the classifier through it. |
384
+ | `UML059_COMPOSITE_CONNECTOR_ENDPOINT_MISSING` | error | A connector ends at a name the classifier never declares. |
385
+ | `UML060_COMPOSITE_DUPLICATE_PART` | error | A part is declared more than once, so a connector naming it is ambiguous. |
386
+ | `UML061_COMPOSITE_PORT_COLLISION` | error | A port shares its name with a part: a connector naming it cannot be read. |
387
+ | `UML062_COMPOSITE_UNDOCUMENTED` | info | The classifier or a part carries no name a reader can map back to the design. |
388
+
389
+ Two limits worth stating. Detection keys on a port declaration, so a composite structure
390
+ drawn without a single port is read as the component diagram it also is. And the
391
+ specification's own constraints on the decomposition - a part's type and multiplicity, a
392
+ port's required and provided interfaces, a connector's role names and end compatibility,
393
+ collaborations - need notation that does not exist here, so they are neither checked nor
394
+ guessed at.
395
+
396
+ ### Communication diagrams
397
+
398
+ A communication diagram is a set of lifelines joined by links, with numbered messages
399
+ travelling along the links. PlantUML has no such diagram type - the page is a 404 - but the
400
+ drawing is expressible with the object diagram's vocabulary, because the sequence number is
401
+ just a label: `C --> O : 1: create`. So this family is drawn approximately and read back
402
+ exactly, like the composite structure.
403
+
404
+ | Action | What happens |
405
+ |---|---|
406
+ | `uml_render` with `kind: "communication"` | Objects for the lifelines, `--` arrows for the links, `-->` arrows for the messages with the number in the label. An `UML_RENDER_APPROXIMATE_VIEW` warning names what the notation cannot carry. |
407
+ | `uml_parse` | Reads it back. |
408
+ | `uml_review` | Reports the family's own findings, and proves fidelity by rendering the model and reading it back. |
409
+
410
+ The model is `CommunicationModelV1`: `lifelines` (an id, and either a `classifier` for a
411
+ `:Name:` or a `label`), `links` (what the diagram draws between lifelines), and `messages`
412
+ (a `sequence` string plus its two ends). Links and messages are kept apart because a link
413
+ with no message is a real thing to notice, and so is a message with no link to carry it.
414
+
415
+ Those three lists are the family, so a model that declares other fields and none of them is
416
+ refused with `UML041_MODEL_FAMILY_MISMATCH`, naming what the family needs and what the model
417
+ declared instead. A model that declares nothing at all is not a mismatch — it is an empty
418
+ communication diagram, which the review reports as `UML063_COMMUNICATION_NO_LIFELINES`.
419
+
420
+ | Code | Severity | What it means |
421
+ |---|---|---|
422
+ | `UML063_COMMUNICATION_NO_LIFELINES` | error | The diagram declares no lifeline. |
423
+ | `UML064_COMMUNICATION_LIFELINE_UNLINKED` | warning | A lifeline takes part in no link and no message. |
424
+ | `UML065_COMMUNICATION_MESSAGE_WITHOUT_LINK` | warning | A message travels between two lifelines the diagram draws no link between. |
425
+ | `UML066_COMMUNICATION_UNDECLARED_ENDPOINT` | error | A link or message names a participant the diagram never declares. |
426
+ | `UML067_COMMUNICATION_SEQUENCE_DUPLICATE` | error | A sequence number is used twice, so the order has two answers. |
427
+ | `UML068_COMMUNICATION_UNDOCUMENTED` | info | A lifeline carries no name a reader can map back to the design. |
428
+
429
+ One limit is worth stating. `1.1` reads in UML as a nested call of `1`, but that is a claim
430
+ about the design; the diagram draws a label. The model keeps the number as the string the
431
+ diagram wrote, and the review does not check the drawing order against it - the numbers are
432
+ the order, and the order they were drawn in is not a claim.
433
+
434
+ ### Interaction overview diagrams
435
+
436
+ An interaction overview is a control-flow graph whose nodes are interactions: a start, a
437
+ decision, references to interactions, a stop. It is drawn here as a **Mermaid flowchart**
438
+ whose nodes name interactions and whose edges carry the branch conditions. Mermaid is the
439
+ only notation it is drawn in. PlantUML's activity syntax is the one place a `ref` step
440
+ exists, and this build refuses it, because a structured flowchart language cannot carry a
441
+ graph with merges or cycles.
442
+
443
+ | Action | What happens |
444
+ |---|---|
445
+ | `uml_render` with `kind: "interaction-overview"` | A `flowchart TD` with a start node, a reference drawn as `[/"Name"/]`, an inline interaction as `["Name"]`, a decision as a rhombus, and each branch condition as an edge label. |
446
+ | `uml_parse` | Refused. A flowchart node carries a *name* rather than the interaction it stands for, so reading one back would have to guess whether a node is a reference to another diagram, an inline interaction, or a plain step. |
447
+ | `uml_review` | Reviews the model, and renders it in Mermaid - the family's own notation - so the drawing comes back with the report. Fidelity is reported as unchecked, because the text cannot be read back. |
448
+
449
+ The model is `InteractionOverviewModelV1`: `nodes` (each with a `kind` of `initial`,
450
+ `final`, `decision`, `merge`, `interaction` or `reference`, and an `interaction` name for
451
+ the last two) and `edges` (each with an optional `condition`). Exactly one initial node is
452
+ required, and that is a validator refusal rather than a finding: every later check would be
453
+ reading a graph with no entry.
454
+
455
+ | Code | Severity | What it means |
456
+ |---|---|---|
457
+ | `UML069_OVERVIEW_NO_INITIAL` | error | No initial node. Refused by the validator before the review runs, so the review never reports it. |
458
+ | `UML070_OVERVIEW_NODE_UNREACHABLE` | error | A node is on no path from the initial node, so the overview does not run it. |
459
+ | `UML071_OVERVIEW_DECISION_ONE_BRANCH` | warning | A decision has one outgoing edge, so it does not decide anything. |
460
+ | `UML072_OVERVIEW_REFERENCE_UNDECLARED` | error | An edge names a node the overview never declares. |
461
+ | `UML073_OVERVIEW_NODE_WITHOUT_INTERACTION` | error | An interaction or reference node names no interaction. |
462
+ | `UML074_OVERVIEW_UNDOCUMENTED` | info | A node carries no name a reader can map back to the design. |
463
+
464
+ ### What the round trip proves
465
+
466
+ It proves the diagram is a faithful rendering of the model:
467
+
468
+ - the same initial state
469
+ - the same set of states and terminal states
470
+ - the same transitions, with the same guards and actions
471
+ - the same variables and kinds
472
+
473
+ It does **not** prove the model matches the code. Only a citation-per-element comparison does that, as described under "Evidence rule". It also does not prove the machine is correct. That is the job of `model_verify`.
474
+
475
+ A failing round trip means the notation lost something. In practice that is a real finding: an unlabelled arrow, a guard the parser could not read, or a diagram that was hand-edited away from its model.
476
+
477
+ ## Worked example
478
+
479
+ The code under review is a handshake that retries on timeout and gives up.
480
+
481
+ ```text
482
+ INIT --power_ready--> STARTING
483
+ STARTING --ack--> ACTIVE
484
+ STARTING --timeout [retry < 3] / retry := retry + 1--> ERROR
485
+ STARTING --timeout [retry >= 3]--> FATAL (terminal)
486
+ ERROR --cooldown--> STARTING
487
+ ```
488
+
489
+ Render it as a state machine, then review it. The review reports two modelling gaps:
490
+
491
+ ```text
492
+ UML006_INEXHAUSTIVE_BRANCH (info) guards look complementary on retry
493
+ UML013_NO_NARRATIVE (info) no state, event or scenario meanings
494
+ ```
495
+
496
+ Neither is a behaviour bug. Add the narrative so a reader can check the diagram against the code, then run `model_verify` for S1-S8 and A1-A14.
497
+
498
+ Now delete the `ERROR --cooldown--> STARTING` arrow and review again. `ACTIVE` and `ERROR` become dead ends (`UML003`), and `cooldown` becomes an event that only fires from an unreachable state (`UML007`). The diagram still looks plausible. That is the whole point.
499
+
500
+ ## Labels and comments: what the parser expects
501
+
502
+ Two things cause most of the friction with hand-written diagrams. Both are printed by the
503
+ tools themselves: `uml_explain_labels` returns the convention (pass
504
+ `notation: "plantuml"` for that flavour), Either one prints exactly the table below,
505
+ from the same rules the parser applies.
506
+
507
+ **1. State meanings, in the spellings the parser accepts.** A label carries a *meaning*
508
+ when it differs from the bare id. Accepted, in this order:
509
+
510
+ | Spelling | Example | Notes |
511
+ |---|---|---|
512
+ | bare id | `state "IDLE" as IDLE` | counts as **no** meaning: the state is reported undocumented (`UML014`) |
513
+ | `ID(meaning)` | `state "IDLE(waiting for power)" as IDLE` | what the renderer writes; full-width parentheses |
514
+ | `ID(meaning)` | `state "IDLE(waiting for power)" as IDLE` | accepted as well |
515
+ | a description line | `IDLE : waiting for power` | the same meaning in the other PlantUML spelling |
516
+ | a single-line note | `note right of IDLE : waiting for power` | read as the meaning, not as a comment |
517
+ | any other text | `state "waiting for power" as IDLE` | taken verbatim |
518
+
519
+ `UML015_LABEL_DRIFT` compares the *meaning* with `narrative.states[id]` character for
520
+ character: the wrapper (`ID(…)`) is stripped, the rest must match. A different wording is
521
+ still drift — the review cannot tell which side is stale. Writing no label is not drift;
522
+ it is `UML014_UNDOCUMENTED_STATE` (info).
523
+
524
+ **2. Comment lines.** The only comment lines the parser *consumes* are the `dsh-model-everything:`
525
+ directives the renderer writes (`%%dsh-model-everything:` in Mermaid, `'dsh-model-everything:` in PlantUML):
526
+ `uml v1 …`, `init ID`, `terminal ID`, `alias X id`, `variable NAME kind`. They are
527
+ ordinary comments to every renderer, which is why the diagram stays valid.
528
+
529
+ Everything else is notation chrome (skipped silently), an ignored line, or a reported
530
+ construct:
531
+
532
+ | Line | Behaviour |
533
+ |---|---|
534
+ | a `%%` / `'` comment that is not a directive | `UML_PARSE_IGNORED_LINE` warning — the line carried no statement |
535
+ | a multi-line `note … end note` block | its body is skipped (prose is not a state meaning) and the block is reported once in `discardedConstructs` |
536
+ | `@startuml`/`@enduml`, `stateDiagram-v2`, `direction`, `classDef`/`style`/`linkStyle`/`click`, `scale`, `skinparam`, `title`, `hide`, `autonumber` | skipped silently |
537
+ | anything else the parser cannot read | `UML_PARSE_IGNORED_LINE` warning, and the model is built from what it did read |
538
+
539
+ So the practical rule is: keep non-directive comments out of the diagram body (put render
540
+ commands and explanations around the diagram, not inside it), and give every state a
541
+ `ID(meaning)` label when the model carries a narrative — otherwise the diagram compares
542
+ as undocumented and a later drift check has nothing to compare against.
543
+
544
+ ## Limits
545
+
546
+ - The review is **structural**. It evaluates no guard over any valuation. "Probably exhaustive" is a shape test, not a proof. S6 is the check that decides.
547
+ - Reachability ignores guards (`UML002`). A state reachable only under an unsatisfiable guard is a behaviour finding (S1 and S6), not a modelling one.
548
+ - Sequence diagrams are traces. They are neither round-tripped nor parsed. Reviewing one renders the trace, reports the structure findings, and marks the fidelity check inapplicable (`UML019`).
549
+ - PlantUML activity is refused by design, as described above.
550
+ - Renaming a state in the diagram does not rename it in the code. The review can only tell you the two disagree. Resolving it needs the source.
551
+ - The generated diagram is a faithful view of the **model**, never of the **program**. If the model is wrong, the diagram is wrong in exactly the same way.