dsh-logicprobe 0.8.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.en-US.md +79 -14
  2. package/README.md +79 -14
  3. package/lib/baseline.js +142 -0
  4. package/lib/client.js +56 -1
  5. package/lib/compose-tool.js +1 -1
  6. package/lib/concurrency-tool.js +1 -1
  7. package/lib/concurrency.js +14 -0
  8. package/lib/data-engine.js +42 -8
  9. package/lib/engine.js +609 -118
  10. package/lib/export-tool.js +27 -2
  11. package/lib/granularity.js +237 -0
  12. package/lib/index.js +63 -32
  13. package/lib/report-diff-tool.js +39 -0
  14. package/lib/structure-tool.js +64 -0
  15. package/lib/structure.js +648 -0
  16. package/lib/tool.js +3 -3
  17. package/lib/types/baseline.d.ts +69 -0
  18. package/lib/types/concurrency.d.ts +10 -0
  19. package/lib/types/data-engine.d.ts +13 -1
  20. package/lib/types/engine.d.ts +139 -2
  21. package/lib/types/granularity.d.ts +106 -0
  22. package/lib/types/index.d.ts +9 -8
  23. package/lib/types/report-diff-tool.d.ts +8 -0
  24. package/lib/types/structure-tool.d.ts +10 -0
  25. package/lib/types/structure.d.ts +156 -0
  26. package/lib/types/tool.d.ts +2 -2
  27. package/lib/types/uml.d.ts +89 -0
  28. package/lib/uml-tool.js +36 -7
  29. package/lib/uml.js +217 -23
  30. package/locale/en.json +6 -0
  31. package/locale/zh.json +6 -0
  32. package/package.json +16 -14
  33. package/skills/logicprobe/SKILL.md +97 -58
  34. package/skills/logicprobe/references/dsh-model-schema.md +87 -15
  35. package/skills/logicprobe/references/gap-routing-guide.md +37 -36
  36. package/skills/logicprobe/references/hash-spec.md +128 -0
  37. package/skills/logicprobe/references/logic-verification-guide.md +29 -248
  38. package/skills/logicprobe-concurrency/SKILL.md +43 -0
  39. package/skills/{logicprobe → logicprobe-concurrency}/references/concurrency-risk-guide.md +1 -1
  40. package/skills/logicprobe-datamodel/SKILL.md +5 -4
  41. package/skills/logicprobe-datamodel/references/data-model-guide.md +1 -1
  42. package/skills/logicprobe-datamodel/references/data-model-schema.md +1 -1
  43. package/skills/logicprobe-structure/SKILL.md +95 -0
  44. package/skills/logicprobe-structure/references/structure-review-guide.md +177 -0
  45. package/skills/logicprobe-uml/SKILL.md +55 -0
  46. package/skills/{logicprobe → logicprobe-uml}/references/uml-modeling-guide.md +120 -8
  47. package/src/baseline.ts +180 -0
  48. package/src/client.js +56 -1
  49. package/src/compose-tool.ts +47 -47
  50. package/src/concurrency-tool.ts +2 -2
  51. package/src/concurrency.ts +23 -0
  52. package/src/data-engine.ts +51 -9
  53. package/src/engine.ts +656 -101
  54. package/src/export-tool.ts +73 -48
  55. package/src/granularity.ts +312 -0
  56. package/src/index.ts +63 -32
  57. package/src/report-diff-tool.ts +43 -0
  58. package/src/structure-tool.ts +68 -0
  59. package/src/structure.ts +724 -0
  60. package/src/tool.ts +4 -4
  61. package/src/uml-tool.ts +38 -8
  62. package/src/uml.ts +275 -24
  63. package/skills/logicprobe/references/logicprobe-engine.py +0 -4780
  64. package/skills/logicprobe/references/verification-harness.py +0 -959
  65. package/skills/logicprobe-datamodel/references/data-model-harness.py +0 -528
@@ -90,284 +90,67 @@ which wastes more time than no verification at all.
90
90
 
91
91
  ## Probe Design Patterns
92
92
 
93
+ Every probe below is implemented by `logicprobe_verify`. These patterns are here to read a finding, not to reimplement it.
94
+
93
95
  ### A1: Unexpected Event Injection
94
96
 
95
- ```python
96
- # For each state S, list all events defined anywhere in the machine
97
- # For each event E not handled by S, test: what happens?
98
- def unexpected_event_probe(states):
99
- all_events = set()
100
- for trans in states.values():
101
- all_events.update(trans.keys())
102
-
103
- findings = []
104
- for state, transitions in states.items():
105
- unhandled = all_events - set(transitions.keys())
106
- if unhandled:
107
- findings.append({
108
- "state": state,
109
- "unhandled_events": list(unhandled),
110
- "risk": "Silent ignore or undefined behavior"
111
- })
112
- return findings
113
- ```
97
+ For every state, list the events declared anywhere in the machine that this state does not handle. An unhandled event is a silent ignore or undefined behaviour.
114
98
 
115
99
  ### A2: Race Interleaving
116
100
 
117
- For pairs of events that can arrive within the same tick (e.g., timer expiry + message reception):
118
-
119
- ```python
120
- def race_interleaving(states, init, event_pairs):
121
- # event_pairs = [("timeout", "ack"), ("error", "done"), ...]
122
- findings = []
123
- for e1, e2 in event_pairs:
124
- # Path 1: e1 then e2
125
- s1 = step(init, [e1, e2])
126
- # Path 2: e2 then e1
127
- s2 = step(init, [e2, e1])
128
- if s1 != s2:
129
- findings.append({
130
- "events": (e1, e2),
131
- "order_e1_e2_ends_in": s1,
132
- "order_e2_e1_ends_in": s2,
133
- "risk": "Order-dependent outcome not documented"
134
- })
135
- return findings
136
- ```
101
+ For event pairs that can arrive in the same tick, compare `e1` then `e2` against `e2` then `e1`. A different outcome is order dependence the design must state.
137
102
 
138
103
  ### A3: Order Permutation
139
104
 
140
- Extends A2 to N events. For small N (≤5), do full permutation. For larger N, sample:
141
-
142
- ```python
143
- from itertools import permutations
144
-
145
- def order_permutation_probe(states, init, events, invariant_check=None):
146
- findings = []
147
- terminals = set()
148
- for perm in permutations(events):
149
- final = step(init, list(perm))
150
- terminals.add(final)
151
- if invariant_check and not invariant_check(final):
152
- findings.append({
153
- "sequence": list(perm),
154
- "final_state": final,
155
- "violates": "invariant"
156
- })
157
- if len(terminals) > 1:
158
- findings.append({
159
- "terminal_states": list(terminals),
160
- "risk": f"Same events produce {len(terminals)} different outcomes depending on order"
161
- })
162
- return findings
163
- ```
105
+ The same question at N events. Up to 5 events permute fully; beyond that the probe samples. More than one terminal state means the outcome depends on arrival order.
164
106
 
165
107
  ### A4: Pair Symmetry
166
108
 
167
- ```python
168
- # Define paired operations
169
- PAIRS = [
170
- ("lock", "unlock"),
171
- ("start", "stop"),
172
- ("alloc", "free"),
173
- ("enable_irq", "disable_irq"),
174
- ("open", "close"),
175
- ]
176
-
177
- def pair_symmetry_probe(states, init):
178
- findings = []
179
- for acquire, release in PAIRS:
180
- if acquire not in all_events(states) and release not in all_events(states):
181
- continue # This pair type is not used
182
- # DFS from init: every path that calls acquire must eventually call release
183
- # before reaching a terminal state (or acquire again)
184
- unbalanced = find_unbalanced_paths(states, init, acquire, release)
185
- if unbalanced:
186
- findings.append({
187
- "pair": (acquire, release),
188
- "unbalanced_paths": unbalanced,
189
- "risk": "Resource leak or deadlock"
190
- })
191
- return findings
192
- ```
109
+ For paired operations (lock/unlock, start/stop, alloc/free, enable_irq/disable_irq, open/close), search from the initial state for a path that acquires without releasing before it terminates or acquires again. That is a leak or a deadlock.
193
110
 
194
111
  ### A5: Boundary Blast
195
112
 
196
- ```python
197
- def boundary_blast(counters, timestamps):
198
- findings = []
199
- for name, val in counters.items():
200
- test_values = [0, 1, val-1, val, val+1, 2**32-1, 2**32]
201
- for tv in test_values:
202
- if tv < 0 or tv > val:
203
- findings.append({
204
- "variable": name,
205
- "value": tv,
206
- "risk": f"Counter overflow/underflow at {tv} (max defined: {val})"
207
- })
208
- # Timestamp wraparound (millis() / sys_tick())
209
- # On 32-bit ARM: wraparound at ~49.7 days for 1ms tick
210
- for name, val in timestamps.items():
211
- findings.append({
212
- "variable": name,
213
- "risk": f"Timestamp wraparound not handled — check elapsed_ms() / elapsed_ticks() pattern"
214
- })
215
- return findings
216
- ```
113
+ Test each counter at 0, 1, max-1, max and max+1. Flag overflow and underflow. Timestamps wrap too: a 32-bit millisecond clock wraps after about 49.7 days, so check every `elapsed_*()` pattern.
217
114
 
218
115
  ### A6: Resource Injection
219
116
 
220
- ```python
221
- RESOURCE_FAILURES = {
222
- "malloc": "returns NULL",
223
- "queue_send": "queue full",
224
- "semaphore_take": "timeout",
225
- "message_alloc": "pool exhausted",
226
- }
227
-
228
- def resource_injection_probe(states):
229
- findings = []
230
- for state, transitions in states.items():
231
- for event in transitions:
232
- for resource, failure in RESOURCE_FAILURES.items():
233
- if resource in event.lower() or resource in state.lower():
234
- # Simulate: what if this resource call fails in this state?
235
- # Does the machine have a recovery transition?
236
- has_recovery = any(
237
- "error" in t.lower() or "fail" in t.lower() or "retry" in t.lower()
238
- for t in transitions.values()
239
- )
240
- if not has_recovery:
241
- findings.append({
242
- "state": state,
243
- "resource": resource,
244
- "failure_mode": failure,
245
- "risk": f"No recovery path if {resource} {failure} in state {state}"
246
- })
247
- return findings
248
- ```
117
+ For every state and transition, ask what happens when the resource call fails: `malloc` returns NULL, a queue is full, a semaphore times out, a pool is exhausted. No error, fail or retry transition means no recovery path.
249
118
 
250
119
  ### A7: Minimal Counter-Example
251
120
 
252
- ```python
253
- from collections import deque
254
-
255
- def shortest_violating_path(states, init, invariant_check):
256
- """BFS to find the shortest event sequence that violates an invariant."""
257
- queue = deque([(init, [])])
258
- visited = set()
259
- while queue:
260
- state, path = queue.popleft()
261
- if state in visited:
262
- continue
263
- visited.add(state)
264
-
265
- if not invariant_check(state):
266
- return path # Shortest violating path found
267
-
268
- for event, next_state in states.get(state, {}).items():
269
- queue.append((next_state, path + [event]))
270
-
271
- return None # Invariant holds for all reachable states
272
- ```
121
+ Breadth-first search for the shortest event sequence that violates an invariant. It decides one runtime state at a time, so it fits the state-predicate kinds only. `leads-to`, `sequence`, `atomicity`, `budget` and `probability` assert a property of a whole run and use the search in their own sections (A9-A13).
273
122
 
274
123
  ### S8: Monotonic Variables
275
124
 
276
- For counters or progress variables that must only move in one direction, list the events that increase/decrease them. Any event that moves against the declared direction is a finding.
277
-
278
- ```python
279
- MONOTONIC_VARS = [
280
- {"name": "retry_count", "direction": "inc", "increase_events": ["retry"], "decrease_events": []},
281
- ]
282
- ```
125
+ For counters that must move one way only, list the events that change them. An event that moves against the declared direction is a finding.
283
126
 
284
127
  ### A8: Idempotent Replay
285
128
 
286
- For events that must be safe to retry/replay, apply the event twice from every reachable state. If the second application changes state or is not possible, flag it.
287
-
288
- ```python
289
- IDEMPOTENT_EVENTS = {"retry", "sync", "webhook_delivery"}
290
- ```
129
+ Apply each retryable event twice from every reachable state. A second application that changes state, or cannot happen, is a finding.
291
130
 
292
131
  ### A9: Leads-To
293
132
 
294
- For progress claims ("from MIGRATING, eventually DONE"), check every path from the source state. If a path dead-ends or loops before reaching the target, flag it.
295
-
296
- ```python
297
- LEADS_TO = [
298
- ("MIGRATING", "DONE"),
299
- ]
300
- ```
133
+ For a progress claim ("from MIGRATING, eventually DONE"), check every path from the source state. A path that dead-ends or loops before the target fails. A target set is still a universal claim: a branch that reaches none of its members fails.
301
134
 
302
135
  ### A10: Sequence Order
303
136
 
304
- For ordered event sequences ("backup before modify before commit"), search for any path where a later event occurs before an earlier one.
305
-
306
- ```python
307
- SEQUENCES = [
308
- ["backup", "modify", "commit"],
309
- ]
310
- ```
137
+ For an ordered sequence ("backup, then modify, then commit"), search for a path where a later event occurs before an earlier one.
311
138
 
312
139
  ### A11: Atomicity
313
140
 
314
- For all-or-nothing groups, track whether an atomic event has started and whether commit/rollback has occurred. Leaving the atomic scope or reaching a terminal state before commit/rollback is a violation.
315
-
316
- ```python
317
- ATOMIC_GROUPS = [
318
- {"events": ["write"], "commit": "commit", "rollback": "rollback"},
319
- ]
320
- ```
141
+ For all-or-nothing groups, track whether the atomic event started and whether commit or rollback followed. Leaving the scope, or terminating, before commit or rollback is a violation.
321
142
 
322
143
  ### A12: Budget (Worst-Case Path Cost)
323
144
 
324
- For performance-sensitive claims ("the dispatch path stays within 100 cycles", "worst-case latency ≤ budget"), model each transition with an execution cost and declare a budget:
325
-
326
- ```python
327
- TRANSITION_COSTS = { # (from, event): cost; absent entries count as 1
328
- ("BOOT", "calibrate_done"): 40,
329
- ("IDLE", "enable"): 5,
330
- ("RUN", "watchdog_expiry"): 15,
331
- }
332
- BUDGETS = [
333
- {"id": "dispatch-budget", "description": "worst-case path within budget", "budget": 100},
334
- ]
335
- ```
336
-
337
- Probe: find the shortest path from init whose accumulated cost exceeds the budget; report it verbatim. A reachable cycle with positive total cost means cost can grow without bound — flag it as a budget violation regardless of the declared budget.
338
-
339
- In DSH (`logicprobe_verify`), `cost` is an optional field on transitions (absent = 1) and `budget` is an invariant kind; A12 runs automatically. Cost values are modeler-provided static labels — they verify the model against the declared budget, not the real WCET, which needs binary-level timing analysis.
145
+ Give each transition a `cost` and declare a `budget`. The probe reports the shortest path whose accumulated cost exceeds it. A reachable cycle with positive cost means cost grows without bound, which is a violation whatever the budget says. Costs are modeler labels: this checks the model against its budget, not real WCET.
340
146
 
341
147
  ### A13: Probability Reachability (DTMC)
342
148
 
343
- For claims with a reliability/probability flavor ("at least 90% of runs reach SAFE", "P(broke first) ≥ 0.75"), attach a relative weight to each branch and declare the bound. Absent weight = 1; weight 0 means the branch never fires probabilistically.
344
-
345
- ```python
346
- TRANSITION_WEIGHTS = { # (from_state, event): weight; absent entries count as 1
347
- ("RUN", "ok"): 9,
348
- ("RUN", "bad"): 1,
349
- }
350
- PROBABILITY_INVARIANTS = [
351
- {"id": "reliability", "target": "SAFE", "op": ">=", "p": 0.9},
352
- ]
353
- ```
354
-
355
- Probe: solve P(ever reaching `target`) from INIT by value iteration over the reachable absorbing chain; compare against the bound. In DSH (`logicprobe_verify`), `weight` on transitions plus a `probability` invariant runs A13 automatically. This is a qualitative DTMC check of the modeler's weights — real MTBF/failure-rate numbers need PRISM/Storm or fault-tree analysis.
149
+ Give each branch a relative `weight` and declare a `probability` bound. The probe solves P(ever reaching the target) by value iteration and compares it with the bound. Absent weight is 1; weight 0 never fires. This is a qualitative check of the weights, not a measured failure rate.
356
150
 
357
151
  ### A14: Deadline (Discrete Tick Clock)
358
152
 
359
- For real-time-sounding claims ("must leave BUSY within 3 ticks", "watchdog resets before deadline"), declare which events advance the clock and cap per-state residency:
360
-
361
- ```python
362
- TICK_EVENTS = {"tick"}
363
- STATE_MAX_TICKS = {"BUSY": 3}
364
- ```
365
-
366
- Probe: explore residency — a `tick` step that keeps the machine resident past `maxTicks` is a deadline miss; report the over-residency path. In DSH (`logicprobe_verify`), state `maxTicks` plus top-level `tickEvents` run A14 automatically. This checks the model's declared deadlines, not real execution time (see Known Limitations below; hard real-time semantics route to UPPAAL).
367
-
368
- ### Standalone engine (non-DSH, JSON models)
369
-
370
- `references/logicprobe-engine.py` is an exact Python mirror of the DSH tools: `verify model.json` runs all 22 checks + D1-D4 with a `--before-model`/optional `--state-mapping`; `compose m1.json m2.json ... --rendezvous a,b` runs C1/C2 composition; `export model.json --format uppaal|tla|prism|spin` reproduces the four exporters byte-for-byte. It reads the same LogicModelV1 JSON as DSH, so a model verified in one host verifies identically in the other (checked by tests/python/run.mjs).
153
+ Declare which events advance the clock (`tickEvents`) and cap per-state residency with `maxTicks`. A tick that keeps the machine resident past its cap is a deadline miss, and the probe reports the over-residency path. This checks declared deadlines, not execution time.
371
154
 
372
155
  ## Counter-Example Interpretation
373
156
 
@@ -405,7 +188,7 @@ When a probe finds a counter-example, classify it:
405
188
  ### What This Method CANNOT Detect
406
189
 
407
190
  - **Implementation bugs**: The C code may have errors not present in the model
408
- - **Timing-dependent bugs**: Python model does not simulate real-time constraints
191
+ - **Timing-dependent bugs**: the model does not simulate real-time constraints
409
192
  - **Compiler/optimization issues**: Volatile omission, reordering, inlining effects
410
193
  - **Hardware-specific behavior**: Memory-mapped I/O timing, DMA races, cache coherency
411
194
  - **Undocumented behavior**: If the plan doesn't describe a transition, the model can't either
@@ -419,7 +202,7 @@ When a probe finds a counter-example, classify it:
419
202
 
420
203
  ### Model Fidelity Warning
421
204
 
422
- The Python model is an APPROXIMATION. It models state transitions, not execution semantics. A model that passes all 22 checks (S1-S8 structural, A1-A14) means the plan's LOGIC is consistent — NOT that the implementation will work. Always follow logic verification with code-level review.
205
+ The model is an APPROXIMATION. It models state transitions, not execution semantics. A model that passes all 22 checks (S1-S8 structural, A1-A14) means the plan's LOGIC is consistent — NOT that the implementation will work. Always follow logic verification with code-level review.
423
206
 
424
207
  ## Refactoring Verification
425
208
 
@@ -438,7 +221,7 @@ The BEFORE model comes from **code, not the plan**. The plan may describe the cu
438
221
 
439
222
  **Behavioral preservation**: For each event sequence accepted by BEFORE, trace the same sequence in AFTER. If AFTER ends in a different state (or rejects the sequence), flag as BEHAVIORAL DELTA. The plan must explicitly document this delta — if it doesn't, it's a regression.
440
223
 
441
- **Invariant continuity**: Re-run S7 invariant checks on both models. Any invariant that passes on BEFORE but fails on AFTER is a regression — the refactoring broke an existing guarantee.
224
+ **Invariant continuity**: Re-decide every invariant on AFTER, mapped through `stateMapping`. Any invariant that holds on BEFORE but fails on AFTER is a regression — the refactoring broke an existing guarantee. State predicates go through the same reachability search as S7; `leads-to`, `sequence`, `atomicity`, `budget` and `probability` are re-decided by the searches their own probes (A9-A13) use, so continuity agrees with the standalone verdict. A state reference that the mapping drops is reported as a mapped-state warning instead of silently changing the property.
442
225
 
443
226
  **Complexity delta**: Count objectively:
444
227
 
@@ -451,7 +234,7 @@ If the plan claims "simplification" but the numbers don't decrease, flag as UNSU
451
234
 
452
235
  **Deadlock regression**: A refactoring that splits one state into two should not introduce a deadlock path that didn't exist before. Run S2 on AFTER and compare to BEFORE's deadlock report.
453
236
 
454
- In the Python harness, set `BEFORE_STATES`, `BEFORE_INIT`, `BEFORE_TERMINALS`, and `STATE_MAPPING` to run D1-D4 automatically after the standard checks.
237
+ For before/after comparison, pass the earlier model as `beforeModel` and an optional `stateMapping`; the D1-D4 checks then run after the standard ones.
455
238
 
456
239
  ### Common Refactoring Bugs Caught by This Method
457
240
 
@@ -462,20 +245,20 @@ In the Python harness, set `BEFORE_STATES`, `BEFORE_INIT`, `BEFORE_TERMINALS`, a
462
245
 
463
246
  ## Manual Verification Mode
464
247
 
465
- When Python is NOT available (air-gapped embedded dev machine, locked-down Windows), execute each check manually. The agent performs the verification using its own reasoning — the methodology is identical, only the execution engine changes.
248
+ When no engine can run (air-gapped machine, locked-down host), execute each check manually. The agent performs the verification using its own reasoning — the methodology is identical, only the execution engine changes.
466
249
 
467
250
  ### Size Limit
468
251
 
469
- Manual verification is reliable for state machines with **≤ 10 states and ≤ 30 transitions**. For larger machines, manual BFS and cycle detection become error-prone. If the machine exceeds this threshold and Python is unavailable, either:
252
+ Manual verification is reliable for state machines with **≤ 10 states and ≤ 30 transitions**. For larger machines, manual BFS and cycle detection become error-prone. If the machine exceeds this threshold and no engine can run, either:
470
253
 
471
254
  - Decompose the machine into sub-machines and verify each independently, then check cross-machine contracts manually
472
- - Flag the size limitation as a finding and recommend the user run the Python harness offline
255
+ - Flag the size limitation as a finding and recommend running the engine offline from the repository
473
256
 
474
257
  ### Prerequisites
475
258
 
476
259
  - Transition table and model narrative have been extracted and confirmed with the user
477
260
  - You have the full table in context (from Phase 2 extraction step)
478
- - **Harness validation** (Python mode only): After filling in `verification-harness.py`, translate the Python `STATES` dict BACK into a transition table and compare it against the confirmed extraction table. If they differ, fix the harness. This catches typo and whitespace errors in manual dict construction.
261
+ - **Model validation**: when a host with an engine becomes available, run the extracted model there and compare its findings with the hand-checked ones.
479
262
 
480
263
  ### Phase 2a — Manual Structural Checks
481
264
 
@@ -507,11 +290,11 @@ Manual verification is reliable for state machines with **≤ 10 states and ≤
507
290
 
508
291
  **A6 Resource Injection**: For each state that appears to allocate (names containing "alloc", "init", "start", "open", "connect", "begin"), check if there is a recovery/error path if the allocation fails. If no recovery exists, flag as RESOURCE VULNERABLE.
509
292
 
510
- **A7 Minimal Counter-Example**: For each invariant that failed in S7, find the SHORTEST event sequence that violates it. This is the most useful output — it gives the plan author an exact repro.
293
+ **A7 Minimal Counter-Example**: For each invariant that failed, find the event sequence that violates it. This is the most useful output — it gives the plan author an exact repro. The searches for state predicates, `sequence`, `atomicity` and `budget` are breadth-first, so their witness is the shortest one; the `leads-to` walk and the `probability` bound report what they found without claiming minimality.
511
294
 
512
295
  ### Manual Mode Output Format
513
296
 
514
- For each finding, output the same structured format as the Python harness:
297
+ For each finding, output the engine structured format:
515
298
 
516
299
  ```text
517
300
  [CHECK] S1 Reachability
@@ -533,9 +316,7 @@ Manual verification takes longer but produces identical-quality findings. The ke
533
316
  2. Read plan → Phase 1 (enumerate claims)
534
317
  3. Detect behavioral claims → trigger logic-primitive escalation
535
318
  4. Load this guide
536
- 5. Check Python: `python3 --version` or `python --version`
537
- 6a. Python ≥ 3.6 → load verification-harness.py → fill in MODEL → run
538
- 6b. No Python → use Manual Verification Mode (see above) — execute each check step by step
319
+ 5. Run the checks: the native tool when the host has it, otherwise Manual Verification Mode (see above), step by step
539
320
  7. Extract model → show transition table → GET USER CONFIRMATION
540
321
  8. Run Phase 2a (8 structural primitives) → log results
541
322
  9. Run Phase 2b (14 adversarial probes) → log results
@@ -544,4 +325,4 @@ Manual verification takes longer but produces identical-quality findings. The ke
544
325
  12. Include in Phase 5 (structured output)
545
326
  ```
546
327
 
547
- Never skip step 5. A verified model based on wrong extraction is worse than no verification — it creates false confidence.
328
+ Never skip the check. A verified model based on wrong extraction is worse than no verification — it creates false confidence.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: logicprobe-concurrency
3
+ description: "Use when a design or plan claims concurrency safety (thread-safe, lock-free, no data race, ISR-safe) or names shared state, mutexes, atomics or interrupts. Mines the claims and routes each to dedicated verification (TSan, Helgrind, CBMC, TLA+); it does not prove concurrency safety. Doctrine: the logicprobe skill."
4
+ ---
5
+
6
+ # Logic Probe — Concurrency Claims
7
+
8
+ This skill does **not** prove concurrency safety, and no report from it should ever be read as a proof. It mines a document, plan or code review for concurrency claims, marks them `UNVERIFIED`, and routes each one to the tool that can actually decide it.
9
+
10
+ The claim-verification doctrine it inherits — enumerate verifiable claims, cite `file:line`, read `verdict` and never `ok` — lives in the `logicprobe` skill. Load that one when the task is behavioural verification of a state machine rather than a judgement about concurrency claims.
11
+
12
+ ## When This Skill Applies
13
+
14
+ **Only after confirming the target actually has concurrency requirements or behaviour** — multiple threads, async tasks, interrupts, shared state, or parallel execution. If the target is purely sequential, do not invoke concurrency mining; there is nothing to mine.
15
+
16
+ | The claim | How it is treated |
17
+ |---|---|
18
+ | Absolute: "thread-safe", "lock-free", "no data race", "interrupt-safe", "ISR-safe" | error / `UNVERIFIED` unless dedicated evidence is provided |
19
+ | Risk keyword: "race condition", "shared variable", "mutex", "atomic", "shared memory", "critical section", `ISR`, `IRQ`, `NMI`, `disable_irq` / `enable_irq` | warning — review whether the plan actually addresses it |
20
+ | Lock/unlock, alloc/free, start/stop pairing where ordering matters | routed to `logicprobe` model verification (A4 pair symmetry), which can decide balance inside the modelled machine |
21
+
22
+ In DSH, run the `logicprobe_concurrency_scan` tool. For manual review, follow [`references/concurrency-risk-guide.md`](references/concurrency-risk-guide.md).
23
+
24
+ ## Routing: What Decides Each Claim
25
+
26
+ Mining a claim is not verifying it. Hand each one to a tool that can:
27
+
28
+ | Dimension | Tool that decides it |
29
+ |---|---|
30
+ | Data races, lock-ordering, lock-free algorithms | ThreadSanitizer, Helgrind, or a model checker over the real code (CBMC) |
31
+ | Interrupt/ISR races, critical sections | RTOS-aware interrupt analysis, or a TLA+ model of the ISR/task interleaving |
32
+ | Protocol-level interleaving of discrete events | `logicprobe_verify` (S1-S8 / A1-A14) and `logicprobe_compose_verify` (C1/C2) |
33
+ | Hard real time: deadlines, periods, preemption | Scheduling analysis (RMA/EDF) and a timing tool — not logicprobe |
34
+ | Probability or reliability claims | PRISM/Storm, fault-tree analysis |
35
+
36
+ The full routing table, including hard-real-time, hybrid-control and execution-cost dimensions, is [`../logicprobe/references/gap-routing-guide.md`](../logicprobe/references/gap-routing-guide.md). `logicprobe_verify` reports carry the same routing as informational `coverageNotes` on matching models.
37
+
38
+ ## Rules for This Mode
39
+
40
+ 1. **Mining, not proof.** Report a concurrency claim as `UNVERIFIED` with the routing target; never as passing.
41
+ 2. **No absolute language in the answer.** "This is thread-safe" is exactly the claim being mined, not a result this skill can produce.
42
+ 3. **One counterexample is enough to refute.** If a race is suspected, the deliverable is the interleaving that breaks it plus the harness that demonstrates it (TSan/Helgrind output), not an argument.
43
+ 4. **Sequential targets are out of scope.** If nothing runs concurrently, say so and stop.
@@ -53,4 +53,4 @@ Manual checklist:
53
53
  - General: TLA+, SPIN, Alloy
54
54
  - Rust: loom, Shuttle
55
55
 
56
- Other semantic dimensions (hard real time, hybrid control, probability/reliability, execution cost) route through the same pattern — see `references/gap-routing-guide.md`.
56
+ Other semantic dimensions (hard real time, hybrid control, probability/reliability, execution cost) route through the same pattern — see the full table in the `logicprobe` skill: `../logicprobe/references/gap-routing-guide.md`.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: logicprobe-datamodel
3
- description: "Use when reviewing design documents, architecture specs, technical proposals, schema changes, data contracts, or refactoring plans that make claims about entities, fields, constraints, relationships, data invariants, migration coverage, or before/after data-model equivalence. When the document contains schema changes, data migration logic, or data-behavioral assertions ('all records must have X', 'target count equals source count', 'no orphan rows', 'migration is non-breaking'), escalate into data-model verification — generate and run executable checks for structural consistency, data invariants, migration coverage, copy consistency, and breaking changes before trusting any claim. Also proactively SUGGEST this skill for code-level data/schema behavioral questions."
3
+ description: "Use when reviewing design docs, specs, plans or migrations claiming entities, fields, constraints, relationships, data invariants, or before/after data-model equivalence: schema changes, migration logic, or data guarantees ('all records must have X', 'no orphan rows', 'ids unique') want executable data-model verification (migration coverage, copy consistency, rollback symmetry, DD1-DD4 regression). State machines and protocols: logicprobe; diagrams: logicprobe-uml."
4
4
  ---
5
5
 
6
6
  # Logic Probe Data
7
7
 
8
- Documents are not truth — data models are. Verify every verifiable data claim before accepting or acting on a design.
8
+ An unverified data model fails later. Model the schema and the migration, run them, and it becomes clear. Check every data claim before you act on it.
9
9
 
10
10
  ## Methodology
11
11
 
@@ -44,8 +44,7 @@ Escalate immediately if the document contains ANY of:
44
44
  ```text
45
45
  Document claims → Extract DataModelV1 → Runtime check:
46
46
  ├── DSH + `logicprobe_datamodel_verify` tool available → build DataModelV1 → call tool → structured report
47
- ├── Python available → fill references/data-model-harness.py → run → report
48
- └── No Python → Manual Verification Mode (references/data-model-guide.md)
47
+ └── No engine available → Manual Verification Mode (references/data-model-guide.md)
49
48
 
50
49
  Refactoring variant:
51
50
  Extract BEFORE DataModelV1 + AFTER DataModelV1
@@ -54,6 +53,8 @@ Refactoring variant:
54
53
  → Flag any invariant that held in BEFORE but fails in AFTER
55
54
  ```
56
55
 
56
+ The **DataModelV1 shape** is in [`references/data-model-schema.md`](references/data-model-schema.md): entities, fields, keys, relationships, invariants, boundary checks, and the optional before/mapping/copy/migration/backup blocks. Those field names are the tool contract. A hand-written model that disagrees with them is rejected, not guessed at.
57
+
57
58
  ## Checks
58
59
 
59
60
  ### DS — Data structure
@@ -1,6 +1,6 @@
1
1
  # Data Model Manual Verification Mode
2
2
 
3
- Use this when neither the DSH `logicprobe_datamodel_verify` tool nor Python is available. It mirrors the automated DS/DA/DD checks as a structured checklist.
3
+ Use this when the DSH `logicprobe_datamodel_verify` tool is not available. It mirrors the automated DS/DA/DD checks as a structured checklist.
4
4
 
5
5
  ## 1. Extract the DataModelV1 table
6
6
 
@@ -1,6 +1,6 @@
1
1
  # DataModelV1 — logicprobe-datamodel Schema
2
2
 
3
- The `logicprobe_datamodel_verify` tool accepts a structured JSON data model. The engine runs DS/DA checks, and DD1-DD4 when `beforeModel` is supplied. It is host-agnostic; DSH uses the native tool, non-DSH hosts use `data-model-harness.py`.
3
+ The `logicprobe_datamodel_verify` tool accepts a structured JSON data model. The engine runs DS/DA checks, and DD1-DD4 when `beforeModel` is supplied. The schema is host-agnostic: the native tool and the repository engine take the same JSON.
4
4
 
5
5
  ## Top-level model
6
6
 
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: logicprobe-structure
3
+ description: "Use when reviewing architecture, module structure or a dependency diagram: parse a component, package, class or deployment diagram into a dependency graph and check isolated nodes, dangling endpoints, cycles, allowed-dependency violations, layer violations and missing required edges. Audits the diagram, not the code — reconcile it with a source-side scan. Doctrine: the logicprobe skill."
4
+ ---
5
+
6
+ # Logic Probe — 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 `logicprobe` 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 | `logicprobe_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 `logicprobe` (verification) and `logicprobe-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
+
95
+ The full matrix schema, a worked example and the reconciliation worksheet are in [`references/structure-review-guide.md`](references/structure-review-guide.md).