dsh-logicprobe 1.2.0 → 1.4.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 +2 -2
- package/README.md +2 -2
- package/lib/engine.js +3446 -3151
- package/lib/types/uml.d.ts +293 -256
- package/lib/uml-tool.js +153 -145
- package/lib/uml.js +1812 -1748
- package/package.json +1 -1
- package/skills/logicprobe/SKILL.md +46 -1
- package/skills/logicprobe/references/dsh-model-schema.md +20 -3
- package/skills/logicprobe-concurrency/SKILL.md +1 -0
- package/skills/logicprobe-datamodel/SKILL.md +5 -0
- package/skills/logicprobe-structure/SKILL.md +1 -0
- package/skills/logicprobe-uml/SKILL.md +3 -0
- package/skills/logicprobe-uml/references/uml-modeling-guide.md +70 -2
- package/src/engine.ts +325 -27
- package/src/uml-tool.ts +11 -3
- package/src/uml.ts +94 -11
package/src/uml.ts
CHANGED
|
@@ -42,6 +42,20 @@ export const UML_NOTATIONS: readonly UmlNotation[] = ['mermaid', 'plantuml']
|
|
|
42
42
|
|
|
43
43
|
export const UML_DIAGRAMS: readonly UmlDiagram[] = ['state', 'activity', 'sequence']
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* What the caller is going to do with the artifact, declared up front.
|
|
47
|
+
*
|
|
48
|
+
* The view follows the question, not the machine, so the tool cannot pick it. What it
|
|
49
|
+
* can do is refuse a pairing that cannot serve the question — before the work, not as
|
|
50
|
+
* a finding after it.
|
|
51
|
+
*/
|
|
52
|
+
export type UmlIntent = 'present' | 'verify' | 'fidelity'
|
|
53
|
+
|
|
54
|
+
export const UML_INTENTS: readonly UmlIntent[] = ['present', 'verify', 'fidelity']
|
|
55
|
+
|
|
56
|
+
/** The tool actions, for the entry check. `labels` is `action=explain-labels`. */
|
|
57
|
+
export type UmlIntentAction = 'render' | 'parse' | 'review' | 'labels'
|
|
58
|
+
|
|
45
59
|
/** Marker every generated diagram carries; renderers ignore it, the parser uses it. */
|
|
46
60
|
const DIRECTIVE_NAMESPACE = 'logicprobe:'
|
|
47
61
|
|
|
@@ -401,26 +415,92 @@ function renderSequence(context: RenderContext, notation: UmlNotation, maxSteps:
|
|
|
401
415
|
return { text: lines.join('\n') + '\n', truncated }
|
|
402
416
|
}
|
|
403
417
|
|
|
418
|
+
/** What each action hands back, so a declared intent can be matched against it. */
|
|
419
|
+
const ACTION_PRODUCES: Record<UmlIntentAction, string> = {
|
|
420
|
+
render: 'a diagram',
|
|
421
|
+
parse: 'a LogicModelV1 read out of a diagram',
|
|
422
|
+
review: 'an audit of the modelling',
|
|
423
|
+
labels: 'the label and comment conventions the parser accepts',
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* The intents each action can serve.
|
|
428
|
+
*
|
|
429
|
+
* `fidelity` is a review job: render produces a diagram, and a diagram nothing has
|
|
430
|
+
* compared back to its model proves nothing about it.
|
|
431
|
+
*/
|
|
432
|
+
const ACTION_INTENTS: Record<UmlIntentAction, readonly UmlIntent[]> = {
|
|
433
|
+
render: ['present', 'verify'],
|
|
434
|
+
parse: ['verify'],
|
|
435
|
+
review: ['present', 'verify', 'fidelity'],
|
|
436
|
+
labels: [],
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* The `(notation, view)` cells this module can draw.
|
|
441
|
+
*
|
|
442
|
+
* PlantUML activity is the cell that does not exist: its activity syntax is a
|
|
443
|
+
* structured flowchart language, so a graph with merges or cycles needs a while/if
|
|
444
|
+
* reconstruction this module does not perform. Refusing is the honest outcome —
|
|
445
|
+
* quietly emitting a state diagram under an "activity" request would mislabel the
|
|
446
|
+
* model.
|
|
447
|
+
*/
|
|
448
|
+
export function assertUmlView(notation: UmlNotation, diagram: UmlDiagram): void {
|
|
449
|
+
if (!UML_NOTATIONS.includes(notation)) throw new UmlError('unknown notation "' + String(notation) + '"; expected ' + UML_NOTATIONS.join(' | '))
|
|
450
|
+
if (!UML_DIAGRAMS.includes(diagram)) throw new UmlError('unknown diagram "' + String(diagram) + '"; expected ' + UML_DIAGRAMS.join(' | '))
|
|
451
|
+
if (notation === 'plantuml' && diagram === 'activity') {
|
|
452
|
+
throw new UmlError('plantuml has no faithful activity view here (its activity syntax is a structured flowchart language; a graph with merges or cycles needs a while/if reconstruction logicprobe does not perform) — use notation "mermaid" for the activity view, or diagram "state"', 'UML_VIEW_UNREPRESENTABLE')
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Refuse, before any work, a request whose declared purpose the view cannot serve.
|
|
458
|
+
*
|
|
459
|
+
* Without an intent this changes nothing: the review still reports `UML019` when the
|
|
460
|
+
* fidelity check does not apply to the view it was given. With one, an impossible
|
|
461
|
+
* pairing is an error carrying a code, instead of a report about the wrong artifact —
|
|
462
|
+
* which is the failure this check exists to prevent.
|
|
463
|
+
*
|
|
464
|
+
* @param intent - the declared purpose, or `undefined` to leave the behaviour unchanged.
|
|
465
|
+
* @param action - which action is about to run.
|
|
466
|
+
* @param view - the requested `(notation, diagram)`, for the actions that have one.
|
|
467
|
+
*/
|
|
468
|
+
export function checkUmlIntent(intent: UmlIntent | undefined, action: UmlIntentAction, view?: { notation: UmlNotation; diagram: UmlDiagram }): void {
|
|
469
|
+
if (intent === undefined) return
|
|
470
|
+
if (!UML_INTENTS.includes(intent)) throw new UmlError('unknown intent "' + String(intent) + '"; expected ' + UML_INTENTS.join(' | '))
|
|
471
|
+
const served = ACTION_INTENTS[action]
|
|
472
|
+
if (!served.includes(intent)) {
|
|
473
|
+
throw new UmlError(
|
|
474
|
+
'intent "' + intent + '" cannot be served by action=' + action + ', which returns ' + ACTION_PRODUCES[action] + '; it serves ' +
|
|
475
|
+
(served.length === 0 ? 'no intent — leave `intent` off' : served.join(' | ')),
|
|
476
|
+
'UML_INTENT_MISMATCH')
|
|
477
|
+
}
|
|
478
|
+
if (view === undefined) return
|
|
479
|
+
assertUmlView(view.notation, view.diagram)
|
|
480
|
+
if (intent === 'present' || view.diagram !== 'sequence') return
|
|
481
|
+
throw new UmlError(
|
|
482
|
+
'intent "' + intent + '" needs a machine, and a sequence view is one BFS trace: ' +
|
|
483
|
+
(intent === 'fidelity'
|
|
484
|
+
? 'the render/parse fidelity check does not apply to a trace, so nothing would be proven. Review kind "state" or "activity" to have the diagram checked against the model, and render the trace separately with intent "present".'
|
|
485
|
+
: '`parse` refuses a trace because it cannot reconstruct a machine, so the diagram cannot feed verification. Model the machine as a state or activity diagram, and render the trace separately with intent "present".'),
|
|
486
|
+
'UML_INTENT_UNSERVABLE')
|
|
487
|
+
}
|
|
488
|
+
|
|
404
489
|
/**
|
|
405
490
|
* Render a LogicModelV1 as UML.
|
|
406
491
|
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
* while/if reconstruction this module does not perform. Refusing is the honest
|
|
410
|
-
* outcome — quietly emitting a state diagram under an "activity" request would
|
|
411
|
-
* mislabel the model. Mermaid covers all three views.
|
|
492
|
+
* Mermaid covers all three views; PlantUML covers state and sequence (see
|
|
493
|
+
* `assertUmlView` for why activity is refused).
|
|
412
494
|
*
|
|
413
495
|
* @param input - candidate LogicModelV1.
|
|
414
496
|
* @param notation - `mermaid` (default) or `plantuml`.
|
|
415
497
|
* @param diagram - `state` (default), `activity`, or `sequence`.
|
|
416
498
|
* @param maxSteps - cap on the sequence trace length.
|
|
499
|
+
* @param intent - what the diagram is for; a view that cannot serve it is refused first.
|
|
417
500
|
*/
|
|
418
|
-
export function renderUml(input: unknown, notation: UmlNotation = 'mermaid', diagram: UmlDiagram = 'state', maxSteps = 60): UmlRenderResult {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (notation === 'plantuml' && diagram === 'activity') {
|
|
422
|
-
throw new UmlError('plantuml has no faithful activity view here (its activity syntax is a structured flowchart language; a graph with merges or cycles needs a while/if reconstruction logicprobe does not perform) — use notation "mermaid" for the activity view, or diagram "state"')
|
|
423
|
-
}
|
|
501
|
+
export function renderUml(input: unknown, notation: UmlNotation = 'mermaid', diagram: UmlDiagram = 'state', maxSteps = 60, intent?: UmlIntent): UmlRenderResult {
|
|
502
|
+
assertUmlView(notation, diagram)
|
|
503
|
+
checkUmlIntent(intent, 'render', { notation, diagram })
|
|
424
504
|
const context = prepareRender(input)
|
|
425
505
|
const warnings = [...context.warnings]
|
|
426
506
|
let primary: string
|
|
@@ -1524,6 +1604,8 @@ export interface UmlReviewOptions {
|
|
|
1524
1604
|
roundTrip?: boolean
|
|
1525
1605
|
/** Cap on the sequence trace used for the fidelity check. */
|
|
1526
1606
|
maxSteps?: number
|
|
1607
|
+
/** What the review is for; a view that cannot serve it is refused before any work. */
|
|
1608
|
+
intent?: UmlIntent
|
|
1527
1609
|
}
|
|
1528
1610
|
|
|
1529
1611
|
/**
|
|
@@ -1549,6 +1631,7 @@ export function reviewUml(options: UmlReviewOptions): UmlReviewReport {
|
|
|
1549
1631
|
|
|
1550
1632
|
let notation: UmlNotation = options.notation === undefined || options.notation === 'auto' ? 'mermaid' : options.notation
|
|
1551
1633
|
const kind: UmlDiagram = options.diagramKind ?? 'state'
|
|
1634
|
+
checkUmlIntent(options.intent, 'review', { notation, diagram: kind })
|
|
1552
1635
|
let model: LogicModelV1 | undefined
|
|
1553
1636
|
let labels: Record<string, string> | undefined
|
|
1554
1637
|
let parsed: UmlParseResult | undefined
|