dsh-model-everything 0.1.0 → 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.
- package/README.en-US.md +21 -8
- package/README.md +21 -8
- package/lib/compose-tool.js +5 -1
- package/lib/data-engine.js +1 -1
- package/lib/data-tool.js +12 -4
- package/lib/design-tool.js +14 -6
- package/lib/engine.js +1 -1
- package/lib/model-file.js +48 -0
- package/lib/report-view.js +30 -5
- package/lib/tool.js +13 -4
- package/lib/types/model-file.d.ts +7 -0
- package/lib/types/report-view.d.ts +8 -1
- package/package.json +5 -1
- package/skills/model-everything/SKILL.md +47 -79
- package/skills/model-everything-interleaving/SKILL.zh-CN.md +60 -60
- package/skills/model-everything-report-reading/SKILL.md +137 -137
- package/src/compose-tool.ts +5 -1
- package/src/data-engine.ts +1 -1
- package/src/data-tool.ts +12 -4
- package/src/design-tool.ts +14 -6
- package/src/engine.ts +1 -1
- package/src/model-file.ts +49 -0
- package/src/report-view.ts +483 -451
- package/src/tool.ts +13 -4
|
@@ -9,22 +9,20 @@ For an unverified design, failure is only a matter of time. Complex code becomes
|
|
|
9
9
|
|
|
10
10
|
## When This Skill Applies
|
|
11
11
|
|
|
12
|
-
**This is the entry point.**
|
|
12
|
+
**This is the entry point.** Load this skill whenever you need to check whether something about the code is true, however small the claim looks. It owns the doctrine: enumerate the claims, verify each against the code, cite `file:line`, read `verdict`. It also owns the executable verification for behaviour. A neighbouring domain goes to its own skill through the routing table below.
|
|
13
13
|
|
|
14
14
|
| The task involves… | What it does |
|
|
15
15
|
|---|---|
|
|
16
|
-
| Any claim about code
|
|
17
|
-
| A state machine or protocol
|
|
18
|
-
| A quantitative or temporal guarantee
|
|
19
|
-
| A handshake or power-up
|
|
20
|
-
| A refactoring that changes state topology or
|
|
21
|
-
|
|
|
22
|
-
| An architecture or dependency question about a component
|
|
23
|
-
| Whether a design keeps its own declarations
|
|
16
|
+
| Any claim about code: design doc, spec, plan, review comment, README, commit message, refactor proposal, "this is safe", "this always holds", "this can't deadlock" | Enumerate the verifiable claims (Phase 1), verify each with `file:line` evidence (Phase 2), classify the gaps (Phase 3) |
|
|
17
|
+
| A state machine or protocol with three or more states, guards, ACK/NACK/retry, lock/unlock, or start/stop ordering | Build an executable model. Run the S1-S8 structural checks and the A1-A14 adversarial probes |
|
|
18
|
+
| A quantitative or temporal guarantee: worst-case cost within a budget, "90% of runs reach SAFE", "must leave within 2 ticks" | A12 budget, A13 probabilistic reachability, A14 deadlines. Each returns a counterexample path |
|
|
19
|
+
| A handshake or power-up sequence across two or more components | `model_compose_verify`: C1 composition deadlock, C2 rendezvous that never fires |
|
|
20
|
+
| A refactoring that changes state topology or guards | Compare BEFORE and AFTER models for behaviour preservation, invariant continuity and deadlock regression (D1-D4) |
|
|
21
|
+
| An explicit request to prove code correct, verify a module, or check a migration for breakage | Route by domain through [Routing to the Sibling Skills](#routing-to-the-sibling-skills). Keep the doctrine |
|
|
22
|
+
| An architecture or dependency question about a component, package or class diagram | `model-everything-structure`: parse it into a dependency graph, judge every edge against the dependency matrix |
|
|
23
|
+
| Whether a design keeps its own declarations: layering, a responsibility boundary, an interface wider than its callers, a subtype that cannot replace its base | `model-everything-design`: `model_design_review` decides the declared contracts (`DES1`-`DES11`) and reports the graph's shape |
|
|
24
24
|
|
|
25
|
-
**Catalog budget**: DSH renders only the frontmatter `description
|
|
26
|
-
|
|
27
|
-
<HARD-GATE>
|
|
25
|
+
**Catalog budget**: DSH renders only the frontmatter `description` and truncates it at 500 characters. Every description here fits. Triggers that do not fit live in this table.
|
|
28
26
|
|
|
29
27
|
## Verification Depth (Plan-Mode Gate)
|
|
30
28
|
|
|
@@ -182,53 +180,23 @@ not a bound on the search.
|
|
|
182
180
|
|
|
183
181
|
### Native dsh Tools
|
|
184
182
|
|
|
185
|
-
|
|
186
|
-
|------|--------|
|
|
187
|
-
| `model_verify` | S1-S8 structural + A1-A14 adversarial, including A12 budget / worst-case path cost, A13 probabilistic reachability over a DTMC, and A14 discrete-tick deadlines. `beforeModel` + `stateMapping` add the D1-D4 before/after regression. |
|
|
188
|
-
| `model_datamodel_verify` | Data models and schema migrations: DS/DA checks plus the DD1-DD4 data regression. See the `model-everything-datamodel` skill. |
|
|
189
|
-
| `model_compose_verify` | Two or more machines checked together (rendezvous handshake semantics): C1 composition deadlock, C2 rendezvous never fires. When `shared` and `accesses` declare a location both machines touch, it also decides the sharing: C3 a declaration that names nothing, C4 an unordered write, C5 a read-modify-write pair the model does not make atomic (the lost update). |
|
|
190
|
-
| `model_interleaving_scan` | Mines claims about how work is executed: threads, synchronization, async, interrupts, shared state. Every finding carries a `family`; async terms (async, await, promise, event loop, callback, and their Chinese counterparts) are in the vocabulary, and an absolute async claim such as "non-blocking" is an error like "thread-safe" is. It routes each claim to the tool that decides it - and for a shared location two machines both update, that tool is `model_compose_verify` (C5), not an external analyzer. It proves nothing itself, and it refuses text it can mine nothing from (`CONCURRENCY_NO_CLAIM`) rather than calling it clean. |
|
|
191
|
-
| `model_export` | Emits external-checker input from a verified model: UPPAAL, TLA+, PRISM, SPIN. |
|
|
192
|
-
| `uml_render` | model → Mermaid/PlantUML source, for any family the notation can draw; a view it cannot draw is refused with the reason. |
|
|
193
|
-
| `uml_parse` | diagram text → the model of the family it declares, so a hand-drawn diagram can be checked. |
|
|
194
|
-
| `uml_review` | audit the modelling: structural defects, documentation gaps and the render-and-read-back fidelity check. |
|
|
195
|
-
| `uml_explain_labels` | print the label and comment contract the parser accepts, for one notation. |
|
|
196
|
-
| `uml_structure_verify` | Reviews a structure diagram as a dependency graph. It reports isolated nodes, dangling endpoints, cycles, disallowed edges, layer violations and missing required edges, and it checks refinement across declared levels. The diagram domain belongs to the `model-everything-structure` skill. |
|
|
197
|
-
| `model_design_review` | Reviews a design: the shape of its dependency graph, and the contracts the design declares about itself. Violations of a declaration decide the verdict; structural facts are reported as signals and never do. A check with nothing to hold the design to is `skip`, not `pass`. The design domain belongs to the `model-everything-design` skill. |
|
|
198
|
-
| `report_diff` | Compares two reports of the same family. A finding's identity is its check, its code and its machine-readable locator, so reworded prose reads as changed rather than new. This is slice acceptance computed instead of eyeballed. |
|
|
199
|
-
|
|
200
|
-
These tools are the engine: every capability this plugin claims is one of the tools above. What is not on that list is out of scope, and the skills name where it is answered instead.
|
|
201
|
-
|
|
202
|
-
### Reading a Report: `ok` Is Not a Verdict
|
|
183
|
+
Twelve tools: `model_verify`, `model_compose_verify`, `model_datamodel_verify`, `model_design_review`, `model_interleaving_scan`, `model_export`, `report_diff`, `uml_render`, `uml_parse`, `uml_review`, `uml_explain_labels`, `uml_structure_verify`. Each tool's description states what it decides and what it does not. Read the description before calling it, and read the report's `verdict` afterwards.
|
|
203
184
|
|
|
204
|
-
|
|
185
|
+
A model is the largest argument these tools take. When it is long, or you have edited it more than once, write it to a file and pass `modelPath` instead of inlining it: a streamed argument that long is where invalid JSON comes from, and the file accepts any formatting.
|
|
205
186
|
|
|
206
|
-
|
|
207
|
-
|-------|---------|
|
|
208
|
-
| `schema` | The versioned report contract (`model-everything/verify/v1`, `model-everything/uml/review/v1`, …). Branch on this instead of sniffing fields. |
|
|
209
|
-
| `ran` | The tool executed and produced this report. `false` only when the tool refused the request before running (see `errorCode`/`error`). |
|
|
210
|
-
| `verdict` | `pass` \| `pass_with_findings` \| `fail`. **This is the outcome you act on.** |
|
|
211
|
-
| `verdictReason` | Why, e.g. `2 error finding(s) (first: S2_NO_TRANSITIONS)` or `no error findings; 3 warning finding(s)`. |
|
|
212
|
-
| `findings[]` | `{code, severity, message, detail?, evidence?, path?}`. Any `severity: "error"` forces `verdict: "fail"`. |
|
|
213
|
-
| `hashSpec` | Which published model-hash specification `modelHash` follows — see `references/hash-spec.md`. |
|
|
214
|
-
| `hashes` | Every hash the report carries, in one place (a model hash, a diagram hash, before/after hashes). |
|
|
215
|
-
| `nextSteps[]` | What to do next, derived from the findings; never empty, and identical for two runs over the same input. |
|
|
216
|
-
| `metadataKeys[]` | Paths of the `_`-prefixed annotation keys the schema ignored, e.g. `["_source", "_verified", "states[0]._note"]` (present only when the input carried any). |
|
|
217
|
-
| `narrativeCoverage` | `{states: "5/5", events: "3/9", scenarios: "0/12"}` when the model carries a narrative — a partial narrative is valid, and this is how much is still undocumented. The gap is also a `NARRATIVE_PARTIAL` (info) finding, so a gate can read it without parsing `nextSteps` text. |
|
|
187
|
+
These tools carry every capability this plugin claims. Anything outside them is out of scope, and the skills name where that claim is answered instead.
|
|
218
188
|
|
|
219
|
-
|
|
189
|
+
### Reading a Report: `ok` Is Not a Verdict
|
|
220
190
|
|
|
221
|
-
|
|
191
|
+
**`ok` records that a report was produced. `verdict` records how the review went.** A report can carry `ok: true` and still be a failed review. Gate on `verdict`.
|
|
222
192
|
|
|
223
|
-
- `
|
|
224
|
-
- `
|
|
225
|
-
- `
|
|
193
|
+
- `pass`: no error and no warning. Report the result as verified, together with the scope the model covered.
|
|
194
|
+
- `pass_with_findings`: verification ran clean and warnings exist. Report the pass and the warnings. Do not restate it as "no issues".
|
|
195
|
+
- `fail`: an error finding exists, or the engine rejected the input (`MODEL_INVALID`). State that the claim is not verified, quote the counterexample path, then propose a correction. Do not describe a `fail` as a successful run.
|
|
226
196
|
|
|
227
|
-
|
|
197
|
+
`summary.truncated: true` and any code ending in `_UNDECIDED` both mean the search stopped before deciding. Name the invariant that went undecided and report the property as undecided.
|
|
228
198
|
|
|
229
|
-
`
|
|
230
|
-
stopped before deciding. That is **not** a pass: name the invariant that went undecided,
|
|
231
|
-
and say the property is undecided rather than reading the silence as success.
|
|
199
|
+
A tool prints a readable body. Passing checks stay in the `CHECKS` inventory line, and a fact that several checks report appears once. Only error findings inline their evidence. When the body truncates a check, pass `check: "<id>"` to print that check in full. `reportView: "full"` returns the whole report as compact JSON. Use it only to hand the report to `report_diff`; it costs the most tokens of any request you can make.
|
|
232
200
|
|
|
233
201
|
### Writing the Answer: Name the Finding, Not Its Code
|
|
234
202
|
|
|
@@ -315,24 +283,24 @@ Run these FIRST. They establish basic well-formedness before adversarial probing
|
|
|
315
283
|
|
|
316
284
|
### Phase 2b: Adversarial Probes
|
|
317
285
|
|
|
318
|
-
Run these
|
|
286
|
+
Run these second. Each probe tries to break the model, and a probe that finds a violation proves a behaviour gap.
|
|
319
287
|
|
|
320
|
-
| # | Attack | Method |
|
|
288
|
+
| # | Attack | Method | Claim it refutes |
|
|
321
289
|
|:--:|--------|--------|-------------|
|
|
322
|
-
| A1 | **Unexpected event** |
|
|
323
|
-
| A2 | **Race interleaving** |
|
|
324
|
-
| A3 | **Order permutation** |
|
|
325
|
-
| A4 | **Pair symmetry** | Match every `start/stop`, `lock/unlock`, `alloc/free` pair.
|
|
326
|
-
| A5 | **Boundary blast** | Probe counters at 0, 1, max-1, max
|
|
327
|
-
| A6 | **Resource injection** | Simulate `malloc
|
|
328
|
-
| A7 | **Counter-example witness** | For
|
|
329
|
-
| A8 | **Idempotent replay** |
|
|
330
|
-
| A9 | **Leads-to** | From a declared source state, every path must
|
|
331
|
-
| A10 | **Sequence order** |
|
|
332
|
-
| A11 | **Atomicity** |
|
|
333
|
-
| A12 | **Budget
|
|
334
|
-
| A13 | **Probability reachability
|
|
335
|
-
| A14 | **Deadline
|
|
290
|
+
| A1 | **Unexpected event** | Inject every event E into every state S that has no transition for (S, E). Record whether the model ignores it or crashes. | "All events are handled in all states" |
|
|
291
|
+
| A2 | **Race interleaving** | Deliver every pair of concurrent events in both orders. Report a differing terminal state. | "Behaviour is independent of ordering" |
|
|
292
|
+
| A3 | **Order permutation** | Permute N independent events. Report differing final states or broken invariants. | "Outcome is order-independent" |
|
|
293
|
+
| A4 | **Pair symmetry** | Match every `start/stop`, `lock/unlock`, `alloc/free` pair. Report a path where a pair is unbalanced. `onEntry` and `onExit` count as acquire and release. | "Resources are always released" |
|
|
294
|
+
| A5 | **Boundary blast** | Probe counters at 0, 1, max-1, max and max+1, and timestamps at 0 and wraparound. Report overflow, underflow or undefined behaviour. | "Handles all counter and timer values" |
|
|
295
|
+
| A6 | **Resource injection** | Simulate `malloc` returning NULL, a full queue and a semaphore timeout at each state that calls them. Report a state with no recovery path. | "Degrades gracefully under resource pressure" |
|
|
296
|
+
| A7 | **Counter-example witness** | For a failing invariant, emit the exact violating sequence. The breadth-first searches (state, sequence, atomicity, budget) return the shortest witness. Leads-to and probability return the witness they found without claiming minimality. | "This invariant holds" |
|
|
297
|
+
| A8 | **Idempotent replay** | Apply each `idempotentEvents` entry twice from every reachable state. Report a second application that changes state. | "This event is safe to replay" |
|
|
298
|
+
| A9 | **Leads-to** | From a declared source state, every path must reach the target state or a member of the declared target set. | "Always progresses to completion" |
|
|
299
|
+
| A10 | **Sequence order** | Declared events must occur in the declared order. | "backup before modify before commit" |
|
|
300
|
+
| A11 | **Atomicity** | An atomic event must commit or roll back before the scope is left or the machine terminates. | "all-or-nothing" |
|
|
301
|
+
| A12 | **Budget** | With `cost` per transition (absent means 1) and a `budget` invariant, every reachable path must stay within budget. The probe returns the shortest over-budget path and reports reachable positive-cost cycles as unbounded. | "Worst case fits the budget" |
|
|
302
|
+
| A13 | **Probability reachability** | With `weight` per transition (absent means 1) and a `probability` invariant, P(ever hitting the target) must meet the bound, by value iteration over the absorbing chain. | "90% of runs reach SAFE" |
|
|
303
|
+
| A14 | **Deadline** | State `maxTicks` plus top-level `tickEvents`. A tick step must not keep a state resident past its deadline. The probe returns the over-residency path. | "Must leave within 2 ticks" |
|
|
336
304
|
|
|
337
305
|
### Integration Back to Phase 3
|
|
338
306
|
|
|
@@ -469,14 +437,14 @@ If you catch yourself thinking any of these, stop:
|
|
|
469
437
|
|
|
470
438
|
## Rules
|
|
471
439
|
|
|
472
|
-
1.
|
|
473
|
-
2.
|
|
474
|
-
3. Cite evidence
|
|
475
|
-
4.
|
|
476
|
-
5.
|
|
477
|
-
6.
|
|
478
|
-
7.
|
|
479
|
-
8.
|
|
480
|
-
9.
|
|
481
|
-
10.
|
|
482
|
-
11.
|
|
440
|
+
1. Verify a document's claim against the code before you trust it.
|
|
441
|
+
2. State mechanism boundaries plainly. When the language standard cannot do the thing, say so.
|
|
442
|
+
3. Cite evidence as `file:line`.
|
|
443
|
+
4. Point the way during review. Implementation happens after approval.
|
|
444
|
+
5. Verify behavioural claims by running code. A plan that says "always", "never" or "guaranteed" gets a model, and one counterexample refutes a universal claim.
|
|
445
|
+
6. Confirm the model before you run it, unless this session already told you to proceed. Extraction errors are the dominant failure mode of formal verification. In auto mode, extract with cited evidence, validate the round trip, and mark the report `UNCONFIRMED`.
|
|
446
|
+
7. Cite the existing check instead of re-verifying it. Compile-time assertions, static analysis and runtime checks already settle that property.
|
|
447
|
+
8. Hand a neighbouring domain to its own skill. A diagram is a model, and auditing one belongs to `model-everything-uml`. A concurrency claim is mined and routed by `model-everything-interleaving`. Schema, data invariants and migrations belong to `model-everything-datamodel`. This skill keeps the doctrine and the behavioural checks.
|
|
448
|
+
9. Say what this does not prove. Hard real time, preemptive concurrency, hybrid-control stability, probabilistic reliability and execution-cost budgets go to the tool named in `references/gap-routing-guide.md`. `model_verify` reports carry the same routing as `coverageNotes`.
|
|
449
|
+
10. A declared range is a property, not a bound. `inc` and `dec` are never clamped; only a guard pins the value. A `truncated` report or an `_UNDECIDED` check means the property is undecided. Report it that way.
|
|
450
|
+
11. Write-site analysis is out of scope. "Only the ISR writes this flag" is a question about source code. `model_interleaving_scan` routes it. Neither the model nor the diagram settles it.
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
# Model Everything —— 并发类断言
|
|
2
|
-
|
|
3
|
-
这项技能**不**证明并发安全,它产出的任何报告都不该被读成证明。它在文档、计划或代码评审里挖出关于**工作怎样被执行**的断言——线程、同步原语、异步任务、中断、共享状态——把每一条标成 `UNVERIFIED`,再转给真正能判定它的工具。
|
|
4
|
-
|
|
5
|
-
它继承的断言核验教义——枚举可核验的断言、给出 `file:line`、读 `verdict` 而不读 `ok`——在 `model-everything` 技能里。当任务是状态机的行为核验、而不是对并发断言下判断时,加载那一份。
|
|
6
|
-
|
|
7
|
-
English: [SKILL.md](SKILL.md)
|
|
8
|
-
|
|
9
|
-
## 什么时候适用
|
|
10
|
-
|
|
11
|
-
**先确认目标真的有并发需求或并发行为**——多线程、异步任务、中断、共享状态、并行执行。目标如果是纯顺序的,就不要启用并发挖掘:那里没有东西可挖。
|
|
12
|
-
|
|
13
|
-
异步也算并发,而且是最常被漏掉的那一种:单线程事件循环照样在交错执行,下面每一条失败模式都适用于它。
|
|
14
|
-
|
|
15
|
-
| 断言 | 怎么处理 |
|
|
16
|
-
|---|---|
|
|
17
|
-
| 绝对式:「thread-safe」「lock-free」「no data race」「interrupt-safe」「ISR-safe」 | error / `UNVERIFIED`,除非给出专门的证据 |
|
|
18
|
-
| 绝对式(异步):「non-blocking」「never blocks」「no blocking」 | error / `UNVERIFIED` —— 事件循环是共享资源,而这句断言是无条件的 |
|
|
19
|
-
| 风险关键词:「race condition」「shared variable」「mutex」「atomic」「shared memory」「critical section」、`ISR`、`IRQ`、`NMI`、`disable_irq` / `enable_irq` | warning —— 检查计划是否真的处理了它 |
|
|
20
|
-
| 异步关键词:「async」「await」「promise」「event loop」「callback」「microtask」「coroutine」「fire-and-forget」「backpressure」 | warning —— 要审的是交错,不是词汇 |
|
|
21
|
-
| lock/unlock、alloc/free、start/stop 这类顺序敏感的配对 | 转给 `model_verify`(A4 配对对称性),它能在被建模的机器上判定是否平衡 |
|
|
22
|
-
|
|
23
|
-
在 DSH 里跑 `model_interleaving_scan` 工具。人工审查请照 [`references/concurrency-risk-guide.md`](references/concurrency-risk-guide.md)。
|
|
24
|
-
|
|
25
|
-
## 路由:每条断言由什么来判定
|
|
26
|
-
|
|
27
|
-
挖出一条断言不等于核验了它。把每一条交给能判定的工具:
|
|
28
|
-
|
|
29
|
-
| 维度 | 判定它的工具 |
|
|
30
|
-
|---|---|
|
|
31
|
-
| 数据竞争、加锁顺序、无锁算法 | ThreadSanitizer、Helgrind,或在真实代码上的模型检查器(CBMC) |
|
|
32
|
-
| 中断/ISR 竞争、临界区 | 具备 RTOS 感知的中断分析,或 ISR 与任务交错顺序的 TLA+ 模型 |
|
|
33
|
-
| 离散事件之间的协议级交错 | `model_verify`(S1-S8 / A1-A14)与 `model_compose_verify`(C1/C2) |
|
|
34
|
-
| 异步任务交错、丢失唤醒、永不发生的握手 | TLA+(由 `model_export` 导出),或两任务情形用 `model_compose_verify` |
|
|
35
|
-
| 两个任务都更新的同一个共享位置(丢更新、无序写) | 用 `shared` + `accesses` 调 `model_compose_verify`:C5 报出模型没有做成原子的读-改-写对,C4 报无序写,C3 报指向不存在对象的声明。两侧点名同一把锁的会被排除,报告会数出来 |
|
|
36
|
-
| 超时、截止期、可能永远不来的回调 | 带 `maxTicks` + `tickEvents` 的 `model_verify`,或 `model_export` → UPPAAL(时钟) |
|
|
37
|
-
| 异步处理器里的阻塞调用、漏掉的 `await`、没人接的 promise | **这里判不了。** 需要运行时或静态检查(事件循环阻塞检测、调用路径的 lint/评审)——说出那个检查名字,绝不要说设计是干净的 |
|
|
38
|
-
| 硬实时:截止期、周期、抢占 | 调度分析(RMA/EDF)与计时工具 —— 不是本工具箱 |
|
|
39
|
-
| 概率或可靠性断言 | PRISM/Storm、故障树分析 |
|
|
40
|
-
|
|
41
|
-
完整路由表(含硬实时、混合控制与执行成本三个维度)在 [`../model-everything/references/gap-routing-guide.md`](../model-everything/references/gap-routing-guide.md)。`model_verify` 的报告会在匹配的模型上,以 informational 的 `coverageNotes` 带上同样的路由。
|
|
42
|
-
|
|
43
|
-
## 异步审查看不到什么
|
|
44
|
-
|
|
45
|
-
异步的缺陷藏在模型表达不了的地方,所以报告必须说出来,而不是暗示自己覆盖了:
|
|
46
|
-
|
|
47
|
-
1. **异步处理器里的阻塞调用。** 模型没有时钟也没有运行时,它分不出同步读取和 `await` 过的读取。「这个处理器是非阻塞的」需要运行时检查(事件循环延迟、阻塞检测器),不是一次扫描。
|
|
48
|
-
2. **漏掉的 `await`。** 那是一个不存在的转移,照着计划建出来的模型会忠实地漏掉它。静态审查调用路径才能抓到;扫描只告诉你该去哪里看。
|
|
49
|
-
3. **跨 `await` 持有的锁。** 扫描器能在同一页上看到 `mutex` 和 `await`,看不出它们重叠。如果这个位置真要较真,就在 `shared` 里声明它、在 `accesses` 里声明两侧:模型没做成原子时 `model_compose_verify` 会报 C5,并数出被命名锁排除掉的对数。锁是否真的在整个操作期间持有,仍是 `model_verify` 的单机问题。
|
|
50
|
-
|
|
51
|
-
这三处都是「没有发现」和「没有问题」不同的地方。说清楚你查了哪一个、用什么查的。
|
|
52
|
-
|
|
53
|
-
## 本模式的规则
|
|
54
|
-
|
|
55
|
-
1. **挖,不是证——除了被声明的共享位置。** 并发断言报成 `UNVERIFIED` 并给出路由目标;永远不要报成通过。唯一的例外是两个模型都碰的那个位置:把 `shared` 与 `accesses` 交给 `model_compose_verify`,它判 C3/C4/C5,你要引用它的 verdict 与见证,而不是把断言说成未核验。
|
|
56
|
-
2. **答案里不许出现绝对语气。**「这是线程安全的」正是被挖的那条断言,不是这项技能能产出的结果。
|
|
57
|
-
3. **一个反例就足以推翻。** 若怀疑存在竞争,交付物是能打破它的那个交错序列,以及把它演出来的 harness(TSan/Helgrind 的输出),而不是一段论证。
|
|
58
|
-
4. **纯顺序的目标不在范围内。** 如果没有东西并发运行,说出来并停下。单线程的异步服务不是纯顺序的:每一个 `await` 处都存在交错。
|
|
59
|
-
5. **扫描器是 `model_interleaving_scan`。** 如果本部署没有注册它,就照上面的关键词表人工挖文本,并按同样的方式路由每一条断言。
|
|
60
|
-
6. **用文字说出断言和它的路由。**「QoS 1 只投递一次」加上判定它的工具,绝不是一个光秃秃的检查项名字。没背下本技能表格的读者,从一串枚举里学不到任何东西。完整规则在 `model_verify` 技能的「Writing the Answer」一节。
|
|
1
|
+
# Model Everything —— 并发类断言
|
|
2
|
+
|
|
3
|
+
这项技能**不**证明并发安全,它产出的任何报告都不该被读成证明。它在文档、计划或代码评审里挖出关于**工作怎样被执行**的断言——线程、同步原语、异步任务、中断、共享状态——把每一条标成 `UNVERIFIED`,再转给真正能判定它的工具。
|
|
4
|
+
|
|
5
|
+
它继承的断言核验教义——枚举可核验的断言、给出 `file:line`、读 `verdict` 而不读 `ok`——在 `model-everything` 技能里。当任务是状态机的行为核验、而不是对并发断言下判断时,加载那一份。
|
|
6
|
+
|
|
7
|
+
English: [SKILL.md](SKILL.md)
|
|
8
|
+
|
|
9
|
+
## 什么时候适用
|
|
10
|
+
|
|
11
|
+
**先确认目标真的有并发需求或并发行为**——多线程、异步任务、中断、共享状态、并行执行。目标如果是纯顺序的,就不要启用并发挖掘:那里没有东西可挖。
|
|
12
|
+
|
|
13
|
+
异步也算并发,而且是最常被漏掉的那一种:单线程事件循环照样在交错执行,下面每一条失败模式都适用于它。
|
|
14
|
+
|
|
15
|
+
| 断言 | 怎么处理 |
|
|
16
|
+
|---|---|
|
|
17
|
+
| 绝对式:「thread-safe」「lock-free」「no data race」「interrupt-safe」「ISR-safe」 | error / `UNVERIFIED`,除非给出专门的证据 |
|
|
18
|
+
| 绝对式(异步):「non-blocking」「never blocks」「no blocking」 | error / `UNVERIFIED` —— 事件循环是共享资源,而这句断言是无条件的 |
|
|
19
|
+
| 风险关键词:「race condition」「shared variable」「mutex」「atomic」「shared memory」「critical section」、`ISR`、`IRQ`、`NMI`、`disable_irq` / `enable_irq` | warning —— 检查计划是否真的处理了它 |
|
|
20
|
+
| 异步关键词:「async」「await」「promise」「event loop」「callback」「microtask」「coroutine」「fire-and-forget」「backpressure」 | warning —— 要审的是交错,不是词汇 |
|
|
21
|
+
| lock/unlock、alloc/free、start/stop 这类顺序敏感的配对 | 转给 `model_verify`(A4 配对对称性),它能在被建模的机器上判定是否平衡 |
|
|
22
|
+
|
|
23
|
+
在 DSH 里跑 `model_interleaving_scan` 工具。人工审查请照 [`references/concurrency-risk-guide.md`](references/concurrency-risk-guide.md)。
|
|
24
|
+
|
|
25
|
+
## 路由:每条断言由什么来判定
|
|
26
|
+
|
|
27
|
+
挖出一条断言不等于核验了它。把每一条交给能判定的工具:
|
|
28
|
+
|
|
29
|
+
| 维度 | 判定它的工具 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| 数据竞争、加锁顺序、无锁算法 | ThreadSanitizer、Helgrind,或在真实代码上的模型检查器(CBMC) |
|
|
32
|
+
| 中断/ISR 竞争、临界区 | 具备 RTOS 感知的中断分析,或 ISR 与任务交错顺序的 TLA+ 模型 |
|
|
33
|
+
| 离散事件之间的协议级交错 | `model_verify`(S1-S8 / A1-A14)与 `model_compose_verify`(C1/C2) |
|
|
34
|
+
| 异步任务交错、丢失唤醒、永不发生的握手 | TLA+(由 `model_export` 导出),或两任务情形用 `model_compose_verify` |
|
|
35
|
+
| 两个任务都更新的同一个共享位置(丢更新、无序写) | 用 `shared` + `accesses` 调 `model_compose_verify`:C5 报出模型没有做成原子的读-改-写对,C4 报无序写,C3 报指向不存在对象的声明。两侧点名同一把锁的会被排除,报告会数出来 |
|
|
36
|
+
| 超时、截止期、可能永远不来的回调 | 带 `maxTicks` + `tickEvents` 的 `model_verify`,或 `model_export` → UPPAAL(时钟) |
|
|
37
|
+
| 异步处理器里的阻塞调用、漏掉的 `await`、没人接的 promise | **这里判不了。** 需要运行时或静态检查(事件循环阻塞检测、调用路径的 lint/评审)——说出那个检查名字,绝不要说设计是干净的 |
|
|
38
|
+
| 硬实时:截止期、周期、抢占 | 调度分析(RMA/EDF)与计时工具 —— 不是本工具箱 |
|
|
39
|
+
| 概率或可靠性断言 | PRISM/Storm、故障树分析 |
|
|
40
|
+
|
|
41
|
+
完整路由表(含硬实时、混合控制与执行成本三个维度)在 [`../model-everything/references/gap-routing-guide.md`](../model-everything/references/gap-routing-guide.md)。`model_verify` 的报告会在匹配的模型上,以 informational 的 `coverageNotes` 带上同样的路由。
|
|
42
|
+
|
|
43
|
+
## 异步审查看不到什么
|
|
44
|
+
|
|
45
|
+
异步的缺陷藏在模型表达不了的地方,所以报告必须说出来,而不是暗示自己覆盖了:
|
|
46
|
+
|
|
47
|
+
1. **异步处理器里的阻塞调用。** 模型没有时钟也没有运行时,它分不出同步读取和 `await` 过的读取。「这个处理器是非阻塞的」需要运行时检查(事件循环延迟、阻塞检测器),不是一次扫描。
|
|
48
|
+
2. **漏掉的 `await`。** 那是一个不存在的转移,照着计划建出来的模型会忠实地漏掉它。静态审查调用路径才能抓到;扫描只告诉你该去哪里看。
|
|
49
|
+
3. **跨 `await` 持有的锁。** 扫描器能在同一页上看到 `mutex` 和 `await`,看不出它们重叠。如果这个位置真要较真,就在 `shared` 里声明它、在 `accesses` 里声明两侧:模型没做成原子时 `model_compose_verify` 会报 C5,并数出被命名锁排除掉的对数。锁是否真的在整个操作期间持有,仍是 `model_verify` 的单机问题。
|
|
50
|
+
|
|
51
|
+
这三处都是「没有发现」和「没有问题」不同的地方。说清楚你查了哪一个、用什么查的。
|
|
52
|
+
|
|
53
|
+
## 本模式的规则
|
|
54
|
+
|
|
55
|
+
1. **挖,不是证——除了被声明的共享位置。** 并发断言报成 `UNVERIFIED` 并给出路由目标;永远不要报成通过。唯一的例外是两个模型都碰的那个位置:把 `shared` 与 `accesses` 交给 `model_compose_verify`,它判 C3/C4/C5,你要引用它的 verdict 与见证,而不是把断言说成未核验。
|
|
56
|
+
2. **答案里不许出现绝对语气。**「这是线程安全的」正是被挖的那条断言,不是这项技能能产出的结果。
|
|
57
|
+
3. **一个反例就足以推翻。** 若怀疑存在竞争,交付物是能打破它的那个交错序列,以及把它演出来的 harness(TSan/Helgrind 的输出),而不是一段论证。
|
|
58
|
+
4. **纯顺序的目标不在范围内。** 如果没有东西并发运行,说出来并停下。单线程的异步服务不是纯顺序的:每一个 `await` 处都存在交错。
|
|
59
|
+
5. **扫描器是 `model_interleaving_scan`。** 如果本部署没有注册它,就照上面的关键词表人工挖文本,并按同样的方式路由每一条断言。
|
|
60
|
+
6. **用文字说出断言和它的路由。**「QoS 1 只投递一次」加上判定它的工具,绝不是一个光秃秃的检查项名字。没背下本技能表格的读者,从一串枚举里学不到任何东西。完整规则在 `model_verify` 技能的「Writing the Answer」一节。
|
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: model-everything-report-reading
|
|
3
|
-
description: "Use when reading a report this plugin returned: what does the verdict mean, why did it come out this way, which field do I act on, and what does the report not prove. It gives the reading order — did it run, what did it decide, what next — where each family keeps its findings, and the two readings that are easy to get backwards. It reads reports; it does not produce or check models. Producing one is model-everything."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Model Everything — Reading a Report
|
|
7
|
-
|
|
8
|
-
Every tool in this plugin returns a report, and a report is a claim about a model. This skill is the
|
|
9
|
-
reading order: which field answers which question, where the findings are in each family, and what
|
|
10
|
-
the report does not prove.
|
|
11
|
-
|
|
12
|
-
Load the `model-everything` skill to *produce* a model or to check a claim. Load this one when a
|
|
13
|
-
report is already in hand and the question is what it says.
|
|
14
|
-
|
|
15
|
-
## The Reading Order
|
|
16
|
-
|
|
17
|
-
Three questions, in this order. Each one can make the next unnecessary.
|
|
18
|
-
|
|
19
|
-
### Did it run?
|
|
20
|
-
|
|
21
|
-
`ran` separates a tool that refused from one that ran, but it answers that question for one kind of
|
|
22
|
-
refusal only. A call refused at the boundary — an argument in the wrong shape, a format that does not
|
|
23
|
-
exist, a notation that is not one — comes back with `ran: false`, an `errorCode`, and a
|
|
24
|
-
`verdictReason` beginning "the tool did not run". Fix the call; there is no verdict to route on.
|
|
25
|
-
|
|
26
|
-
An input the engine's own schema rejects is a different shape and looks like a review: `ran: true`,
|
|
27
|
-
`ok: false`, `verdict: fail`, no `errorCode`, one finding under a check whose id is `MODEL`,
|
|
28
|
-
`DATA_MODEL` or `DESIGN`, and `summary.checksRun: 0`. Read it as "the input is the problem, not the
|
|
29
|
-
design". A model that was actually reviewed examined something, so a non-zero `checksRun` is what
|
|
30
|
-
separates a defective model from a malformed one.
|
|
31
|
-
|
|
32
|
-
A refusal and a failing review need different responses, and `ran` alone does not tell them apart:
|
|
33
|
-
the test is `ran: false`, or `checksRun: 0` beside a check named after the validation.
|
|
34
|
-
|
|
35
|
-
### What did it decide?
|
|
36
|
-
|
|
37
|
-
`verdict` is the answer:
|
|
38
|
-
|
|
39
|
-
| `verdict` | Means |
|
|
40
|
-
|---|---|
|
|
41
|
-
| `pass` | The checks ran and found nothing to report |
|
|
42
|
-
| `pass_with_findings` | Warnings only; nothing decided against the model |
|
|
43
|
-
| `fail` | At least one error finding |
|
|
44
|
-
|
|
45
|
-
Read `verdict`, never `ok`. `ok` carries a different historical meaning in each family — in most it
|
|
46
|
-
is `true` for a failing review, and a composition sets it to `false` when any error-severity finding
|
|
47
|
-
exists — so it cannot be read as a verdict, and reading it as one is how a reader concludes the
|
|
48
|
-
opposite of what the report says. A `verdict: "fail"` is a successful run that found something;
|
|
49
|
-
that is the normal way this plugin tells you about a defect.
|
|
50
|
-
|
|
51
|
-
`verdictReason` is the one-line why and names the first error code. When the verdict is not what you
|
|
52
|
-
expected, start there.
|
|
53
|
-
|
|
54
|
-
### What should I do?
|
|
55
|
-
|
|
56
|
-
`nextSteps` is never empty and is derived from the findings rather than written beside them. It
|
|
57
|
-
names the route to whatever covers the ground the report does not: another check, a
|
|
58
|
-
source-side scan, or an external checker.
|
|
59
|
-
|
|
60
|
-
## Where the Findings Are
|
|
61
|
-
|
|
62
|
-
This is the part that varies, and it is worth knowing before you go looking:
|
|
63
|
-
|
|
64
|
-
| Where | Families |
|
|
65
|
-
|---|---|
|
|
66
|
-
| Inside each check, as `checks[].findings` | `model_verify`, `model_compose_verify`, `model_datamodel_verify`, `model_design_review` |
|
|
67
|
-
| At the top level, as `findings` | `uml_structure_verify`, the `uml` review path, `model_interleaving_scan` |
|
|
68
|
-
| Nowhere — compare `added` / `changed` / `removed` | `report_diff` |
|
|
69
|
-
|
|
70
|
-
A check-carried finding is attributed to a check, and the check carries a status of its own: `pass`,
|
|
71
|
-
`fail`, or `skip`. A top-level finding has no check to attribute it to, which is why those families
|
|
72
|
-
report a `summary` instead.
|
|
73
|
-
|
|
74
|
-
`report_diff` has no findings because its subject is the difference between two other reports.
|
|
75
|
-
`verdict` describes the delta and `currentVerdict` describes the current run on its own terms. They
|
|
76
|
-
differ exactly when the run is not clean but the delta is empty: comparing a failing report against
|
|
77
|
-
itself gives `verdict: "pass", currentVerdict: "fail"`, and both are true — nothing changed, and the
|
|
78
|
-
run fails.
|
|
79
|
-
|
|
80
|
-
## Severity Before Code
|
|
81
|
-
|
|
82
|
-
Within a finding, the severity decides whether it changed the verdict:
|
|
83
|
-
|
|
84
|
-
- **error** — what makes the verdict `fail`;
|
|
85
|
-
- **warning** — reported, does not fail the delta;
|
|
86
|
-
- **info** — context, for example that one element makes an inseparable group of findings;
|
|
87
|
-
- **undecided** — the search stopped before deciding. Not a pass. `summary.truncated` says the same
|
|
88
|
-
for the report as a whole.
|
|
89
|
-
|
|
90
|
-
The code prefix names the check that owns the finding (`S1`, `A4`, `DS5`, `DA2`, `DES12`, `UML023`),
|
|
91
|
-
so a code tells you where to look before you read its message.
|
|
92
|
-
|
|
93
|
-
### A code is for you, not for the person you are answering
|
|
94
|
-
|
|
95
|
-
The codes exist so you can route the finding to the right reference and re-check it. A user who never
|
|
96
|
-
read the report has no idea what `A4_NO_RELEASE_EVENT` means, and a reply that recites codes makes the
|
|
97
|
-
work look opaque while telling them nothing they can act on.
|
|
98
|
-
|
|
99
|
-
**Say what the finding says, in words.** The finding's own `message` is already a sentence written for a
|
|
100
|
-
reader who does not know the codes - use it. Name the code afterwards only if it helps the user find the
|
|
101
|
-
same line in the report, and then give it a meaning: not "S7 failed" but "the invariant is violated - the
|
|
102
|
-
model reaches C after two events (`S7_INVARIANT_VIOLATION`)".
|
|
103
|
-
|
|
104
|
-
The same goes for a check id: `Event Completeness` is the name, `S5` is the label. Lead with the name.
|
|
105
|
-
|
|
106
|
-
A model whose reachable state space is infinite is reported as `S6_UNBOUNDED_VARIABLE`, an error. It
|
|
107
|
-
is a property of the model, not a defect of the search: no search bound would have decided the
|
|
108
|
-
question. When you relay this one, relay the reason: *any* search bound would leave it undecided.
|
|
109
|
-
|
|
110
|
-
## What a Report Does Not Tell You
|
|
111
|
-
|
|
112
|
-
- **It is about the model, not the code.** A clean verdict is evidence about the drawing you
|
|
113
|
-
supplied. Reconciling it with the source is a separate step, and the difference between the two is
|
|
114
|
-
where the finding usually is.
|
|
115
|
-
- **A truncated search decided nothing.** When `truncated` is true, the checks that stopped early
|
|
116
|
-
report themselves as undecided rather than as passes.
|
|
117
|
-
- **A skipped check examined nothing.** `skip` and `pass` are different answers: a check with no
|
|
118
|
-
vocabulary to work with did not look, and saying so is the point.
|
|
119
|
-
|
|
120
|
-
## The Design Review's Three Lists
|
|
121
|
-
|
|
122
|
-
One family splits its findings, and only the first list decides anything:
|
|
123
|
-
|
|
124
|
-
- `violations` — the check level that decides the verdict;
|
|
125
|
-
- `signals` — worth a look, fails nothing;
|
|
126
|
-
- `facts` — no findings at all: one measurement object with the graph's numbers (`density`, `ranks`,
|
|
127
|
-
`articulationPoints`, `feedbackArcs`, `largestCycle`) and `checksSkipped`, which is where a reader
|
|
128
|
-
learns which checks never ran for want of a declaration.
|
|
129
|
-
|
|
130
|
-
No code appears in both lists, so a reader who reads only `violations` sees every finding that
|
|
131
|
-
decided the verdict and none that did not.
|
|
132
|
-
|
|
133
|
-
## Reference
|
|
134
|
-
|
|
135
|
-
[references/reading-a-report.md](references/reading-a-report.md) is the same method with the
|
|
136
|
-
worked detail: the shared skeleton field by field, what each family adds, and where the map that
|
|
137
|
-
structures it lives.
|
|
1
|
+
---
|
|
2
|
+
name: model-everything-report-reading
|
|
3
|
+
description: "Use when reading a report this plugin returned: what does the verdict mean, why did it come out this way, which field do I act on, and what does the report not prove. It gives the reading order — did it run, what did it decide, what next — where each family keeps its findings, and the two readings that are easy to get backwards. It reads reports; it does not produce or check models. Producing one is model-everything."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Model Everything — Reading a Report
|
|
7
|
+
|
|
8
|
+
Every tool in this plugin returns a report, and a report is a claim about a model. This skill is the
|
|
9
|
+
reading order: which field answers which question, where the findings are in each family, and what
|
|
10
|
+
the report does not prove.
|
|
11
|
+
|
|
12
|
+
Load the `model-everything` skill to *produce* a model or to check a claim. Load this one when a
|
|
13
|
+
report is already in hand and the question is what it says.
|
|
14
|
+
|
|
15
|
+
## The Reading Order
|
|
16
|
+
|
|
17
|
+
Three questions, in this order. Each one can make the next unnecessary.
|
|
18
|
+
|
|
19
|
+
### Did it run?
|
|
20
|
+
|
|
21
|
+
`ran` separates a tool that refused from one that ran, but it answers that question for one kind of
|
|
22
|
+
refusal only. A call refused at the boundary — an argument in the wrong shape, a format that does not
|
|
23
|
+
exist, a notation that is not one — comes back with `ran: false`, an `errorCode`, and a
|
|
24
|
+
`verdictReason` beginning "the tool did not run". Fix the call; there is no verdict to route on.
|
|
25
|
+
|
|
26
|
+
An input the engine's own schema rejects is a different shape and looks like a review: `ran: true`,
|
|
27
|
+
`ok: false`, `verdict: fail`, no `errorCode`, one finding under a check whose id is `MODEL`,
|
|
28
|
+
`DATA_MODEL` or `DESIGN`, and `summary.checksRun: 0`. Read it as "the input is the problem, not the
|
|
29
|
+
design". A model that was actually reviewed examined something, so a non-zero `checksRun` is what
|
|
30
|
+
separates a defective model from a malformed one.
|
|
31
|
+
|
|
32
|
+
A refusal and a failing review need different responses, and `ran` alone does not tell them apart:
|
|
33
|
+
the test is `ran: false`, or `checksRun: 0` beside a check named after the validation.
|
|
34
|
+
|
|
35
|
+
### What did it decide?
|
|
36
|
+
|
|
37
|
+
`verdict` is the answer:
|
|
38
|
+
|
|
39
|
+
| `verdict` | Means |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `pass` | The checks ran and found nothing to report |
|
|
42
|
+
| `pass_with_findings` | Warnings only; nothing decided against the model |
|
|
43
|
+
| `fail` | At least one error finding |
|
|
44
|
+
|
|
45
|
+
Read `verdict`, never `ok`. `ok` carries a different historical meaning in each family — in most it
|
|
46
|
+
is `true` for a failing review, and a composition sets it to `false` when any error-severity finding
|
|
47
|
+
exists — so it cannot be read as a verdict, and reading it as one is how a reader concludes the
|
|
48
|
+
opposite of what the report says. A `verdict: "fail"` is a successful run that found something;
|
|
49
|
+
that is the normal way this plugin tells you about a defect.
|
|
50
|
+
|
|
51
|
+
`verdictReason` is the one-line why and names the first error code. When the verdict is not what you
|
|
52
|
+
expected, start there.
|
|
53
|
+
|
|
54
|
+
### What should I do?
|
|
55
|
+
|
|
56
|
+
`nextSteps` is never empty and is derived from the findings rather than written beside them. It
|
|
57
|
+
names the route to whatever covers the ground the report does not: another check, a
|
|
58
|
+
source-side scan, or an external checker.
|
|
59
|
+
|
|
60
|
+
## Where the Findings Are
|
|
61
|
+
|
|
62
|
+
This is the part that varies, and it is worth knowing before you go looking:
|
|
63
|
+
|
|
64
|
+
| Where | Families |
|
|
65
|
+
|---|---|
|
|
66
|
+
| Inside each check, as `checks[].findings` | `model_verify`, `model_compose_verify`, `model_datamodel_verify`, `model_design_review` |
|
|
67
|
+
| At the top level, as `findings` | `uml_structure_verify`, the `uml` review path, `model_interleaving_scan` |
|
|
68
|
+
| Nowhere — compare `added` / `changed` / `removed` | `report_diff` |
|
|
69
|
+
|
|
70
|
+
A check-carried finding is attributed to a check, and the check carries a status of its own: `pass`,
|
|
71
|
+
`fail`, or `skip`. A top-level finding has no check to attribute it to, which is why those families
|
|
72
|
+
report a `summary` instead.
|
|
73
|
+
|
|
74
|
+
`report_diff` has no findings because its subject is the difference between two other reports.
|
|
75
|
+
`verdict` describes the delta and `currentVerdict` describes the current run on its own terms. They
|
|
76
|
+
differ exactly when the run is not clean but the delta is empty: comparing a failing report against
|
|
77
|
+
itself gives `verdict: "pass", currentVerdict: "fail"`, and both are true — nothing changed, and the
|
|
78
|
+
run fails.
|
|
79
|
+
|
|
80
|
+
## Severity Before Code
|
|
81
|
+
|
|
82
|
+
Within a finding, the severity decides whether it changed the verdict:
|
|
83
|
+
|
|
84
|
+
- **error** — what makes the verdict `fail`;
|
|
85
|
+
- **warning** — reported, does not fail the delta;
|
|
86
|
+
- **info** — context, for example that one element makes an inseparable group of findings;
|
|
87
|
+
- **undecided** — the search stopped before deciding. Not a pass. `summary.truncated` says the same
|
|
88
|
+
for the report as a whole.
|
|
89
|
+
|
|
90
|
+
The code prefix names the check that owns the finding (`S1`, `A4`, `DS5`, `DA2`, `DES12`, `UML023`),
|
|
91
|
+
so a code tells you where to look before you read its message.
|
|
92
|
+
|
|
93
|
+
### A code is for you, not for the person you are answering
|
|
94
|
+
|
|
95
|
+
The codes exist so you can route the finding to the right reference and re-check it. A user who never
|
|
96
|
+
read the report has no idea what `A4_NO_RELEASE_EVENT` means, and a reply that recites codes makes the
|
|
97
|
+
work look opaque while telling them nothing they can act on.
|
|
98
|
+
|
|
99
|
+
**Say what the finding says, in words.** The finding's own `message` is already a sentence written for a
|
|
100
|
+
reader who does not know the codes - use it. Name the code afterwards only if it helps the user find the
|
|
101
|
+
same line in the report, and then give it a meaning: not "S7 failed" but "the invariant is violated - the
|
|
102
|
+
model reaches C after two events (`S7_INVARIANT_VIOLATION`)".
|
|
103
|
+
|
|
104
|
+
The same goes for a check id: `Event Completeness` is the name, `S5` is the label. Lead with the name.
|
|
105
|
+
|
|
106
|
+
A model whose reachable state space is infinite is reported as `S6_UNBOUNDED_VARIABLE`, an error. It
|
|
107
|
+
is a property of the model, not a defect of the search: no search bound would have decided the
|
|
108
|
+
question. When you relay this one, relay the reason: *any* search bound would leave it undecided.
|
|
109
|
+
|
|
110
|
+
## What a Report Does Not Tell You
|
|
111
|
+
|
|
112
|
+
- **It is about the model, not the code.** A clean verdict is evidence about the drawing you
|
|
113
|
+
supplied. Reconciling it with the source is a separate step, and the difference between the two is
|
|
114
|
+
where the finding usually is.
|
|
115
|
+
- **A truncated search decided nothing.** When `truncated` is true, the checks that stopped early
|
|
116
|
+
report themselves as undecided rather than as passes.
|
|
117
|
+
- **A skipped check examined nothing.** `skip` and `pass` are different answers: a check with no
|
|
118
|
+
vocabulary to work with did not look, and saying so is the point.
|
|
119
|
+
|
|
120
|
+
## The Design Review's Three Lists
|
|
121
|
+
|
|
122
|
+
One family splits its findings, and only the first list decides anything:
|
|
123
|
+
|
|
124
|
+
- `violations` — the check level that decides the verdict;
|
|
125
|
+
- `signals` — worth a look, fails nothing;
|
|
126
|
+
- `facts` — no findings at all: one measurement object with the graph's numbers (`density`, `ranks`,
|
|
127
|
+
`articulationPoints`, `feedbackArcs`, `largestCycle`) and `checksSkipped`, which is where a reader
|
|
128
|
+
learns which checks never ran for want of a declaration.
|
|
129
|
+
|
|
130
|
+
No code appears in both lists, so a reader who reads only `violations` sees every finding that
|
|
131
|
+
decided the verdict and none that did not.
|
|
132
|
+
|
|
133
|
+
## Reference
|
|
134
|
+
|
|
135
|
+
[references/reading-a-report.md](references/reading-a-report.md) is the same method with the
|
|
136
|
+
worked detail: the shared skeleton field by field, what each family adds, and where the map that
|
|
137
|
+
structures it lives.
|