dsh-model-everything 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.en-US.md +319 -0
- package/README.md +319 -3
- package/cordis.patch.yml +20 -0
- package/examples/README.md +48 -0
- package/examples/control-dispatcher.json +113 -0
- package/examples/control-dispatcher.overbudget.json +113 -0
- package/examples/ecommerce-data-model.json +185 -0
- package/examples/order-state-machine.after.json +116 -0
- package/examples/order-state-machine.before.json +101 -0
- package/examples/user-migration.after.json +29 -0
- package/examples/user-migration.before.json +29 -0
- package/gate/locale/en.json +6 -0
- package/gate/locale/zh.json +6 -0
- package/gate/package.json +5 -0
- package/lib/baseline.js +249 -0
- package/lib/compose-tool.js +76 -0
- package/lib/concurrency-tool.js +40 -0
- package/lib/concurrency.js +206 -0
- package/lib/data-engine.js +1141 -0
- package/lib/data-tool.js +79 -0
- package/lib/design-engine.js +1149 -0
- package/lib/design-tool.js +55 -0
- package/lib/engine.js +4343 -0
- package/lib/export-tool.js +78 -0
- package/lib/exporters.js +434 -0
- package/lib/gate.js +274 -0
- package/lib/granularity.js +237 -0
- package/lib/index.js +237 -0
- package/lib/json-value.js +18 -0
- package/lib/option-shape.js +62 -0
- package/lib/report-diff-tool.js +63 -0
- package/lib/report-view.js +408 -0
- package/lib/structure-tool.js +91 -0
- package/lib/structure.js +711 -0
- package/lib/tool.js +85 -0
- package/lib/types/baseline.d.ts +65 -0
- package/lib/types/compose-tool.d.ts +7 -0
- package/lib/types/concurrency-tool.d.ts +8 -0
- package/lib/types/concurrency.d.ts +37 -0
- package/lib/types/data-engine.d.ts +211 -0
- package/lib/types/data-tool.d.ts +10 -0
- package/lib/types/design-engine.d.ts +215 -0
- package/lib/types/design-tool.d.ts +11 -0
- package/lib/types/engine.d.ts +494 -0
- package/lib/types/export-tool.d.ts +7 -0
- package/lib/types/exporters.d.ts +37 -0
- package/lib/types/gate.d.ts +91 -0
- package/lib/types/granularity.d.ts +106 -0
- package/lib/types/index.d.ts +45 -0
- package/lib/types/json-value.d.ts +21 -0
- package/lib/types/option-shape.d.ts +22 -0
- package/lib/types/report-diff-tool.d.ts +8 -0
- package/lib/types/report-view.d.ts +43 -0
- package/lib/types/structure-tool.d.ts +10 -0
- package/lib/types/structure.d.ts +152 -0
- package/lib/types/tool.d.ts +11 -0
- package/lib/types/uml-communication.d.ts +109 -0
- package/lib/types/uml-composite.d.ts +107 -0
- package/lib/types/uml-interaction-overview.d.ts +91 -0
- package/lib/types/uml-profile.d.ts +112 -0
- package/lib/types/uml-structure.d.ts +53 -0
- package/lib/types/uml-syntax.d.ts +296 -0
- package/lib/types/uml-timing.d.ts +152 -0
- package/lib/types/uml-tool.d.ts +12 -0
- package/lib/types/uml-usecase.d.ts +58 -0
- package/lib/types/uml.d.ts +303 -0
- package/lib/uml-communication.js +353 -0
- package/lib/uml-composite.js +442 -0
- package/lib/uml-interaction-overview.js +277 -0
- package/lib/uml-profile.js +373 -0
- package/lib/uml-structure.js +306 -0
- package/lib/uml-syntax.js +545 -0
- package/lib/uml-timing.js +974 -0
- package/lib/uml-tool.js +213 -0
- package/lib/uml-usecase.js +532 -0
- package/lib/uml.js +2110 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +124 -6
- package/skills/model-everything/SKILL.md +482 -0
- package/skills/model-everything/references/dsh-model-schema.md +437 -0
- package/skills/model-everything/references/gap-routing-guide.md +42 -0
- package/skills/model-everything/references/hash-spec.md +152 -0
- package/skills/model-everything/references/logic-verification-guide.md +422 -0
- package/skills/model-everything-datamodel/SKILL.md +130 -0
- package/skills/model-everything-datamodel/references/data-model-guide.md +84 -0
- package/skills/model-everything-datamodel/references/data-model-schema.md +128 -0
- package/skills/model-everything-design/SKILL.md +115 -0
- package/skills/model-everything-interleaving/SKILL.md +63 -0
- package/skills/model-everything-interleaving/SKILL.zh-CN.md +60 -0
- package/skills/model-everything-interleaving/references/concurrency-risk-guide.md +63 -0
- package/skills/model-everything-report-reading/SKILL.md +137 -0
- package/skills/model-everything-report-reading/references/reading-a-report.md +166 -0
- package/skills/model-everything-report-reading/references/reading-a-report.mmd +59 -0
- package/skills/model-everything-structure/SKILL.md +96 -0
- package/skills/model-everything-structure/references/structure-review-guide.md +201 -0
- package/skills/model-everything-uml/SKILL.md +60 -0
- package/skills/model-everything-uml/references/uml-modeling-guide.md +551 -0
- package/src/baseline.ts +288 -0
- package/src/compose-tool.ts +78 -0
- package/src/concurrency-tool.ts +44 -0
- package/src/concurrency.ts +254 -0
- package/src/data-engine.ts +1208 -0
- package/src/data-tool.ts +83 -0
- package/src/design-engine.ts +1263 -0
- package/src/design-tool.ts +60 -0
- package/src/engine.ts +4564 -0
- package/src/export-tool.ts +81 -0
- package/src/exporters.ts +399 -0
- package/src/gate.ts +330 -0
- package/src/granularity.ts +312 -0
- package/src/index.ts +289 -0
- package/src/json-value.ts +20 -0
- package/src/option-shape.ts +64 -0
- package/src/report-diff-tool.ts +67 -0
- package/src/report-view.ts +451 -0
- package/src/structure-tool.ts +93 -0
- package/src/structure.ts +786 -0
- package/src/tool.ts +94 -0
- package/src/uml-communication.ts +428 -0
- package/src/uml-composite.ts +520 -0
- package/src/uml-interaction-overview.ts +321 -0
- package/src/uml-profile.ts +439 -0
- package/src/uml-structure.ts +317 -0
- package/src/uml-syntax.ts +764 -0
- package/src/uml-timing.ts +1106 -0
- package/src/uml-tool.ts +230 -0
- package/src/uml-usecase.ts +566 -0
- 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).
|