dsh-logicprobe 1.3.0 → 1.4.1
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 +22 -1
- package/README.md +16 -1
- package/lib/engine.js +320 -25
- package/lib/types/uml.d.ts +293 -293
- package/lib/uml-tool.js +153 -153
- package/lib/uml.js +14 -1
- package/package.json +11 -9
- package/skills/logicprobe/SKILL.md +50 -1
- package/skills/logicprobe/references/dsh-model-schema.md +20 -3
- package/skills/logicprobe-concurrency/SKILL.md +2 -0
- package/skills/logicprobe-datamodel/SKILL.md +2 -0
- package/skills/logicprobe-structure/SKILL.md +2 -0
- package/skills/logicprobe-uml/SKILL.md +2 -0
- package/src/engine.ts +325 -27
- package/src/uml.ts +15 -1
package/lib/uml-tool.js
CHANGED
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
-
import { createHash } from 'node:crypto';
|
|
3
|
-
import { REPORT_SCHEMAS, DEFAULT_HASH_SPEC, modelHash, refusalVerdict, verdictOf, verdictOfFindings } from './engine.js';
|
|
4
|
-
import { renderUml, parseUml, parseFindings, reviewUml, explainLabels, checkUmlIntent, UmlError } from './uml.js';
|
|
5
|
-
import { renderReportText } from './report-view.js';
|
|
6
|
-
export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml';
|
|
7
|
-
/**
|
|
8
|
-
* DSH tool wrapping the UML front end: model a code flow as a UML diagram
|
|
9
|
-
* (`action: "render"`), read a UML diagram back into a LogicModelV1
|
|
10
|
-
* (`action: "parse"`), or review the modelling itself (`action: "review"`).
|
|
11
|
-
*
|
|
12
|
-
* review is the half that makes the feature a check rather than a drawing
|
|
13
|
-
* utility: it reports structural defects the diagram would present as valid
|
|
14
|
-
* flow (unreachable states, dead ends, ambiguous and non-exhaustive branches,
|
|
15
|
-
* self-loops with no exit), documentation gaps (a symbol no reader can map back
|
|
16
|
-
* to code), and — the fidelity check — whether the rendered diagram reads back
|
|
17
|
-
* as the model it was drawn from.
|
|
18
|
-
*/
|
|
19
|
-
export const logicProbeUmlTool = defineTool({
|
|
20
|
-
name: LOGICPROBE_UML_TOOL_NAME,
|
|
21
|
-
description: 'Model a code flow as UML, and audit the modelling. action="render" turns a LogicModelV1 into diagram source: mermaid (state, activity, sequence) or plantuml (state, sequence). action="parse" reads Mermaid or PlantUML state or activity text back into a LogicModelV1, so a hand-drawn diagram can be verified. A sequence diagram is refused: a trace cannot reconstruct a machine. So is any other diagram family (class, ER, gantt, mindmap). That refusal carries errorCode "UML_NOT_A_STATE_DIAGRAM". A PlantUML component, package or deployment diagram declares no kind, so the parser detects those constructs itself. It returns a by-product model and reports the error UML_NOT_A_STATE_DIAGRAM, with `discardedConstructs` and `discardedEdges`. Never treat that model as a model of the file. action="review" audits the modelling: structural defects (UML002-UML009), documentation gaps (UML010-UML015), and a fidelity check that re-parses the rendered diagram (UML017). Give it a model, a diagram, or both. action="explain-labels" prints the label and comment conventions the parser accepts, without needing a diagram. Rendering never invents structure. A construct the notation cannot express becomes a warning. For dependency questions on a component or class diagram, use `logicprobe_structure_verify` instead. Declare `intent` (present, verify, fidelity) to say what the artifact is for: a view that cannot serve it is refused before any work, with errorCode UML_INTENT_UNSERVABLE, UML_VIEW_UNREPRESENTABLE or UML_INTENT_MISMATCH. Omit it and nothing changes. Read `verdict`, not `ok`: error findings mean a failed review.',
|
|
22
|
-
parameters: {
|
|
23
|
-
action: {
|
|
24
|
-
type: 'string',
|
|
25
|
-
required: true,
|
|
26
|
-
enum: ['render', 'parse', 'review', 'explain-labels'],
|
|
27
|
-
description: 'render = model → UML source; parse = UML source → model; review = audit the modelling (and its fidelity to the model); explain-labels = print the label and comment conventions this parser accepts.',
|
|
28
|
-
},
|
|
29
|
-
model: {
|
|
30
|
-
type: 'json',
|
|
31
|
-
description: 'LogicModelV1 machine. Required for render; accepted by review (alone, or next to diagram to check the two against each other).',
|
|
32
|
-
},
|
|
33
|
-
diagram: {
|
|
34
|
-
type: 'string',
|
|
35
|
-
description: 'UML source text. Required for parse; accepted by review.',
|
|
36
|
-
},
|
|
37
|
-
notation: {
|
|
38
|
-
type: 'string',
|
|
39
|
-
enum: ['auto', 'mermaid', 'plantuml'],
|
|
40
|
-
description: 'Diagram language. Default auto: detected from the text for parse/review, mermaid for render.',
|
|
41
|
-
},
|
|
42
|
-
kind: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
enum: ['state', 'activity', 'sequence'],
|
|
45
|
-
description: 'Diagram kind to render. Default state. sequence is one BFS trace, not the whole machine. PlantUML activity is refused (its structured-flowchart syntax cannot faithfully carry a graph with merges or cycles) — use mermaid for that view.',
|
|
46
|
-
},
|
|
47
|
-
intent: {
|
|
48
|
-
type: 'string',
|
|
49
|
-
enum: ['present', 'verify', 'fidelity'],
|
|
50
|
-
description: 'What the artifact is for, declared up front: present = a human will read the diagram (any view), verify = it must be parseable into a LogicModelV1 for the engine (state or activity), fidelity = the point is the render-and-reparse proof (review only, state or activity). A view that cannot serve the intent is refused before any work, with errorCode UML_INTENT_UNSERVABLE, UML_VIEW_UNREPRESENTABLE or UML_INTENT_MISMATCH. Omit it and nothing changes: a sequence view is still rendered, and review still reports UML019.',
|
|
51
|
-
},
|
|
52
|
-
roundTrip: {
|
|
53
|
-
type: 'boolean',
|
|
54
|
-
description: 'review only: render and re-parse the model to prove the diagram carries it. Default true.',
|
|
55
|
-
},
|
|
56
|
-
maxSteps: {
|
|
57
|
-
type: 'integer',
|
|
58
|
-
description: 'Cap on the sequence trace length when rendering kind=sequence. Default 60.',
|
|
59
|
-
},
|
|
60
|
-
reportView: {
|
|
61
|
-
type: 'string',
|
|
62
|
-
enum: ['digest', 'full'],
|
|
63
|
-
description: 'digest (default) prints the diagram or report body; full prints the structured result as JSON.',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
output: {
|
|
67
|
-
schema: {
|
|
68
|
-
type: 'json',
|
|
69
|
-
description: 'Render result (diagram source), parse result (LogicModelV1 + labels), or the modelling review report with findings, round-trip diff and next steps.',
|
|
70
|
-
},
|
|
71
|
-
render(args, value) {
|
|
72
|
-
// A rendered diagram is source text: the report body prints it verbatim, once.
|
|
73
|
-
// Echoing the whole result as JSON printed the same diagram a second time, escaped.
|
|
74
|
-
return [{ type: 'text', text: renderReportText(value, { full: args.reportView === 'full' }) }];
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
timeoutMs: 10_000,
|
|
78
|
-
isConcurrencySafe: () => true,
|
|
79
|
-
async execute(args) {
|
|
80
|
-
try {
|
|
81
|
-
if (args.action === 'render') {
|
|
82
|
-
if (args.model === undefined)
|
|
83
|
-
return errorResult('action=render needs `model` (a LogicModelV1 object)');
|
|
84
|
-
const result = renderUml(args.model, (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation), (args.kind ?? 'state'), args.maxSteps, args.intent);
|
|
85
|
-
return { ok: true, ran: true, ...verdictOf(0, result.warnings.length), schema: REPORT_SCHEMAS.umlRender, action: 'render', notation: result.notation, kind: result.diagram, diagram: result.primary, hashes: { diagram: createHash('sha256').update(result.primary).digest('hex') }, warnings: result.warnings };
|
|
86
|
-
}
|
|
87
|
-
if (args.action === 'parse') {
|
|
88
|
-
if (args.diagram === undefined)
|
|
89
|
-
return errorResult('action=parse needs `diagram` (Mermaid or PlantUML text)');
|
|
90
|
-
checkUmlIntent(args.intent, 'parse');
|
|
91
|
-
const result = parseUml(args.diagram, args.notation ?? 'auto');
|
|
92
|
-
// A diagram from another family still parses into *something*. `parseFindings`
|
|
93
|
-
// says so, and the verdict is what stops that something from being trusted:
|
|
94
|
-
// `ok: true` only means the parser ran.
|
|
95
|
-
const findings = parseFindings(result);
|
|
96
|
-
return {
|
|
97
|
-
ok: true,
|
|
98
|
-
ran: true,
|
|
99
|
-
...parseVerdict(result, findings),
|
|
100
|
-
schema: REPORT_SCHEMAS.umlParse,
|
|
101
|
-
action: 'parse',
|
|
102
|
-
notation: result.notation,
|
|
103
|
-
kind: result.diagram,
|
|
104
|
-
model: result.model,
|
|
105
|
-
labels: result.labels,
|
|
106
|
-
findings,
|
|
107
|
-
discardedConstructs: result.discardedConstructs,
|
|
108
|
-
discardedEdges: result.discardedEdges,
|
|
109
|
-
hashes: { hashSpec: DEFAULT_HASH_SPEC, modelHash: modelHash(result.model), diagram: createHash('sha256').update(args.diagram).digest('hex') },
|
|
110
|
-
warnings: result.warnings,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// R9: the label contract was documented in the library and in `explainLabels()`, but no
|
|
114
|
-
// tool exposed it, so it was reachable only by reading the source or the docs.
|
|
115
|
-
if (args.action === 'explain-labels') {
|
|
116
|
-
checkUmlIntent(args.intent, 'labels');
|
|
117
|
-
const notation = (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation);
|
|
118
|
-
return {
|
|
119
|
-
ok: true,
|
|
120
|
-
ran: true,
|
|
121
|
-
...verdictOf(0, 0),
|
|
122
|
-
schema: REPORT_SCHEMAS.umlExplainLabels,
|
|
123
|
-
action: 'explain-labels',
|
|
124
|
-
...explainLabels(notation),
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
const report = reviewUml({
|
|
128
|
-
model: args.model,
|
|
129
|
-
diagram: args.diagram,
|
|
130
|
-
notation: args.notation ?? 'auto',
|
|
131
|
-
diagramKind: (args.kind ?? 'state'),
|
|
132
|
-
roundTrip: args.roundTrip,
|
|
133
|
-
maxSteps: args.maxSteps,
|
|
134
|
-
intent: args.intent,
|
|
135
|
-
});
|
|
136
|
-
return report;
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
-
return errorResult(message, error instanceof UmlError ? error.code : undefined);
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
/** The parse verdict counts parser notes as findings: an ignored line is information the model does not carry. */
|
|
145
|
-
function parseVerdict(result, findings) {
|
|
146
|
-
const errors = findings.filter((finding) => finding.severity === 'error').length;
|
|
147
|
-
const warnings = findings.filter((finding) => finding.severity === 'warning').length + result.warnings.length;
|
|
148
|
-
const firstCode = findings.find((finding) => finding.severity === 'error')?.code ?? findings.find((finding) => finding.severity === 'warning')?.code;
|
|
149
|
-
return verdictOf(errors, warnings, firstCode);
|
|
150
|
-
}
|
|
151
|
-
function errorResult(message, errorCode = 'UML_INPUT') {
|
|
152
|
-
return { ok: false, ran: false, ...refusalVerdict(message), errorCode, error: message };
|
|
153
|
-
}
|
|
1
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { REPORT_SCHEMAS, DEFAULT_HASH_SPEC, modelHash, refusalVerdict, verdictOf, verdictOfFindings } from './engine.js';
|
|
4
|
+
import { renderUml, parseUml, parseFindings, reviewUml, explainLabels, checkUmlIntent, UmlError } from './uml.js';
|
|
5
|
+
import { renderReportText } from './report-view.js';
|
|
6
|
+
export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml';
|
|
7
|
+
/**
|
|
8
|
+
* DSH tool wrapping the UML front end: model a code flow as a UML diagram
|
|
9
|
+
* (`action: "render"`), read a UML diagram back into a LogicModelV1
|
|
10
|
+
* (`action: "parse"`), or review the modelling itself (`action: "review"`).
|
|
11
|
+
*
|
|
12
|
+
* review is the half that makes the feature a check rather than a drawing
|
|
13
|
+
* utility: it reports structural defects the diagram would present as valid
|
|
14
|
+
* flow (unreachable states, dead ends, ambiguous and non-exhaustive branches,
|
|
15
|
+
* self-loops with no exit), documentation gaps (a symbol no reader can map back
|
|
16
|
+
* to code), and — the fidelity check — whether the rendered diagram reads back
|
|
17
|
+
* as the model it was drawn from.
|
|
18
|
+
*/
|
|
19
|
+
export const logicProbeUmlTool = defineTool({
|
|
20
|
+
name: LOGICPROBE_UML_TOOL_NAME,
|
|
21
|
+
description: 'Model a code flow as UML, and audit the modelling. action="render" turns a LogicModelV1 into diagram source: mermaid (state, activity, sequence) or plantuml (state, sequence). action="parse" reads Mermaid or PlantUML state or activity text back into a LogicModelV1, so a hand-drawn diagram can be verified. A sequence diagram is refused: a trace cannot reconstruct a machine. So is any other diagram family (class, ER, gantt, mindmap). That refusal carries errorCode "UML_NOT_A_STATE_DIAGRAM". A PlantUML component, package or deployment diagram declares no kind, so the parser detects those constructs itself. It returns a by-product model and reports the error UML_NOT_A_STATE_DIAGRAM, with `discardedConstructs` and `discardedEdges`. Never treat that model as a model of the file. action="review" audits the modelling: structural defects (UML002-UML009), documentation gaps (UML010-UML015), and a fidelity check that re-parses the rendered diagram (UML017). Give it a model, a diagram, or both. action="explain-labels" prints the label and comment conventions the parser accepts, without needing a diagram. Rendering never invents structure. A construct the notation cannot express becomes a warning. For dependency questions on a component or class diagram, use `logicprobe_structure_verify` instead. Declare `intent` (present, verify, fidelity) to say what the artifact is for: a view that cannot serve it is refused before any work, with errorCode UML_INTENT_UNSERVABLE, UML_VIEW_UNREPRESENTABLE or UML_INTENT_MISMATCH. Omit it and nothing changes. Read `verdict`, not `ok`: error findings mean a failed review.',
|
|
22
|
+
parameters: {
|
|
23
|
+
action: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
required: true,
|
|
26
|
+
enum: ['render', 'parse', 'review', 'explain-labels'],
|
|
27
|
+
description: 'render = model → UML source; parse = UML source → model; review = audit the modelling (and its fidelity to the model); explain-labels = print the label and comment conventions this parser accepts.',
|
|
28
|
+
},
|
|
29
|
+
model: {
|
|
30
|
+
type: 'json',
|
|
31
|
+
description: 'LogicModelV1 machine. Required for render; accepted by review (alone, or next to diagram to check the two against each other).',
|
|
32
|
+
},
|
|
33
|
+
diagram: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'UML source text. Required for parse; accepted by review.',
|
|
36
|
+
},
|
|
37
|
+
notation: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
enum: ['auto', 'mermaid', 'plantuml'],
|
|
40
|
+
description: 'Diagram language. Default auto: detected from the text for parse/review, mermaid for render.',
|
|
41
|
+
},
|
|
42
|
+
kind: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
enum: ['state', 'activity', 'sequence'],
|
|
45
|
+
description: 'Diagram kind to render. Default state. sequence is one BFS trace, not the whole machine. PlantUML activity is refused (its structured-flowchart syntax cannot faithfully carry a graph with merges or cycles) — use mermaid for that view.',
|
|
46
|
+
},
|
|
47
|
+
intent: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
enum: ['present', 'verify', 'fidelity'],
|
|
50
|
+
description: 'What the artifact is for, declared up front: present = a human will read the diagram (any view), verify = it must be parseable into a LogicModelV1 for the engine (state or activity), fidelity = the point is the render-and-reparse proof (review only, state or activity). A view that cannot serve the intent is refused before any work, with errorCode UML_INTENT_UNSERVABLE, UML_VIEW_UNREPRESENTABLE or UML_INTENT_MISMATCH. Omit it and nothing changes: a sequence view is still rendered, and review still reports UML019.',
|
|
51
|
+
},
|
|
52
|
+
roundTrip: {
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
description: 'review only: render and re-parse the model to prove the diagram carries it. Default true.',
|
|
55
|
+
},
|
|
56
|
+
maxSteps: {
|
|
57
|
+
type: 'integer',
|
|
58
|
+
description: 'Cap on the sequence trace length when rendering kind=sequence. Default 60.',
|
|
59
|
+
},
|
|
60
|
+
reportView: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
enum: ['digest', 'full'],
|
|
63
|
+
description: 'digest (default) prints the diagram or report body; full prints the structured result as JSON.',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
output: {
|
|
67
|
+
schema: {
|
|
68
|
+
type: 'json',
|
|
69
|
+
description: 'Render result (diagram source), parse result (LogicModelV1 + labels), or the modelling review report with findings, round-trip diff and next steps.',
|
|
70
|
+
},
|
|
71
|
+
render(args, value) {
|
|
72
|
+
// A rendered diagram is source text: the report body prints it verbatim, once.
|
|
73
|
+
// Echoing the whole result as JSON printed the same diagram a second time, escaped.
|
|
74
|
+
return [{ type: 'text', text: renderReportText(value, { full: args.reportView === 'full' }) }];
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
timeoutMs: 10_000,
|
|
78
|
+
isConcurrencySafe: () => true,
|
|
79
|
+
async execute(args) {
|
|
80
|
+
try {
|
|
81
|
+
if (args.action === 'render') {
|
|
82
|
+
if (args.model === undefined)
|
|
83
|
+
return errorResult('action=render needs `model` (a LogicModelV1 object)');
|
|
84
|
+
const result = renderUml(args.model, (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation), (args.kind ?? 'state'), args.maxSteps, args.intent);
|
|
85
|
+
return { ok: true, ran: true, ...verdictOf(0, result.warnings.length), schema: REPORT_SCHEMAS.umlRender, action: 'render', notation: result.notation, kind: result.diagram, diagram: result.primary, hashes: { diagram: createHash('sha256').update(result.primary).digest('hex') }, warnings: result.warnings };
|
|
86
|
+
}
|
|
87
|
+
if (args.action === 'parse') {
|
|
88
|
+
if (args.diagram === undefined)
|
|
89
|
+
return errorResult('action=parse needs `diagram` (Mermaid or PlantUML text)');
|
|
90
|
+
checkUmlIntent(args.intent, 'parse');
|
|
91
|
+
const result = parseUml(args.diagram, args.notation ?? 'auto');
|
|
92
|
+
// A diagram from another family still parses into *something*. `parseFindings`
|
|
93
|
+
// says so, and the verdict is what stops that something from being trusted:
|
|
94
|
+
// `ok: true` only means the parser ran.
|
|
95
|
+
const findings = parseFindings(result);
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
ran: true,
|
|
99
|
+
...parseVerdict(result, findings),
|
|
100
|
+
schema: REPORT_SCHEMAS.umlParse,
|
|
101
|
+
action: 'parse',
|
|
102
|
+
notation: result.notation,
|
|
103
|
+
kind: result.diagram,
|
|
104
|
+
model: result.model,
|
|
105
|
+
labels: result.labels,
|
|
106
|
+
findings,
|
|
107
|
+
discardedConstructs: result.discardedConstructs,
|
|
108
|
+
discardedEdges: result.discardedEdges,
|
|
109
|
+
hashes: { hashSpec: DEFAULT_HASH_SPEC, modelHash: modelHash(result.model), diagram: createHash('sha256').update(args.diagram).digest('hex') },
|
|
110
|
+
warnings: result.warnings,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// R9: the label contract was documented in the library and in `explainLabels()`, but no
|
|
114
|
+
// tool exposed it, so it was reachable only by reading the source or the docs.
|
|
115
|
+
if (args.action === 'explain-labels') {
|
|
116
|
+
checkUmlIntent(args.intent, 'labels');
|
|
117
|
+
const notation = (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation);
|
|
118
|
+
return {
|
|
119
|
+
ok: true,
|
|
120
|
+
ran: true,
|
|
121
|
+
...verdictOf(0, 0),
|
|
122
|
+
schema: REPORT_SCHEMAS.umlExplainLabels,
|
|
123
|
+
action: 'explain-labels',
|
|
124
|
+
...explainLabels(notation),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const report = reviewUml({
|
|
128
|
+
model: args.model,
|
|
129
|
+
diagram: args.diagram,
|
|
130
|
+
notation: args.notation ?? 'auto',
|
|
131
|
+
diagramKind: (args.kind ?? 'state'),
|
|
132
|
+
roundTrip: args.roundTrip,
|
|
133
|
+
maxSteps: args.maxSteps,
|
|
134
|
+
intent: args.intent,
|
|
135
|
+
});
|
|
136
|
+
return report;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
return errorResult(message, error instanceof UmlError ? error.code : undefined);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
/** The parse verdict counts parser notes as findings: an ignored line is information the model does not carry. */
|
|
145
|
+
function parseVerdict(result, findings) {
|
|
146
|
+
const errors = findings.filter((finding) => finding.severity === 'error').length;
|
|
147
|
+
const warnings = findings.filter((finding) => finding.severity === 'warning').length + result.warnings.length;
|
|
148
|
+
const firstCode = findings.find((finding) => finding.severity === 'error')?.code ?? findings.find((finding) => finding.severity === 'warning')?.code;
|
|
149
|
+
return verdictOf(errors, warnings, firstCode);
|
|
150
|
+
}
|
|
151
|
+
function errorResult(message, errorCode = 'UML_INPUT') {
|
|
152
|
+
return { ok: false, ran: false, ...refusalVerdict(message), errorCode, error: message };
|
|
153
|
+
}
|
package/lib/uml.js
CHANGED
|
@@ -618,7 +618,7 @@ function parseTransitionLabel(label, warnings) {
|
|
|
618
618
|
* comes out would be presented as if it described the file.
|
|
619
619
|
*/
|
|
620
620
|
const MERMAID_OTHER_FAMILIES = [
|
|
621
|
-
'classDiagram', 'erDiagram', 'gantt', 'pie', 'journey', 'mindmap', 'gitGraph',
|
|
621
|
+
'classDiagram', 'erDiagram', 'gantt', 'pie', 'journey', 'mindmap', 'gitGraph', 'usecase-beta',
|
|
622
622
|
'C4Context', 'C4Container', 'C4Component', 'C4Dynamic', 'C4Deployment',
|
|
623
623
|
'requirementDiagram', 'timeline', 'quadrantChart', 'sankey-beta', 'block-beta',
|
|
624
624
|
'packet-beta', 'architecture-beta', 'radar-beta', 'treemap-beta', 'xychart-beta',
|
|
@@ -642,6 +642,12 @@ function detectNotation(text) {
|
|
|
642
642
|
return 'mermaid';
|
|
643
643
|
throw new UmlError('cannot tell whether this is Mermaid or PlantUML text: expected `stateDiagram-v2` / `flowchart` / `sequenceDiagram`, or `@startuml`');
|
|
644
644
|
}
|
|
645
|
+
/**
|
|
646
|
+
* PlantUML use case syntax that carries no keyword: a colon actor, or a parenthesised
|
|
647
|
+
* use case, with an optional alias and an optional relation on the same line.
|
|
648
|
+
* `:User: --> (Login)` and `(Login) as (L)` are the whole header of such a file.
|
|
649
|
+
*/
|
|
650
|
+
const USECASE_SYNTAX = /^[ \t]*(?::[^:\n]+:(?:[ \t]+as[ \t]+[A-Za-z_][A-Za-z0-9_]*)?|\([^()\n]+\)(?:[ \t]+as[ \t]+\(?[^()\n]+\)?)?)[ \t]*(?:$|-->|->|\.\.>|\.>|--\|>|<\|--|--)/m;
|
|
645
651
|
function detectDiagram(text) {
|
|
646
652
|
if (/^\s*stateDiagram/m.test(text))
|
|
647
653
|
return 'state';
|
|
@@ -658,6 +664,13 @@ function detectDiagram(text) {
|
|
|
658
664
|
// structural one logicprobe emits, everything else in that family is a state diagram too.
|
|
659
665
|
if (/^\s*participant\b/m.test(text) || /->>\s*/.test(text))
|
|
660
666
|
return 'sequence';
|
|
667
|
+
// A use case diagram has no keyword header at all. The state parser below would read
|
|
668
|
+
// its arrows as transitions and return a plausible machine with no finding, so the
|
|
669
|
+
// family is refused by name. Its own model, and the tool that reads it, live in the
|
|
670
|
+
// dsh-model-everything plugin.
|
|
671
|
+
if (USECASE_SYNTAX.test(text)) {
|
|
672
|
+
throw new UmlError('this text is a use case diagram (`:Actor: --> (UseCase)`, `(UseCase) as (U)`), which this plugin does not model: it models state machines and flows, and reading it as one returns a machine the diagram never drew. The dsh-model-everything plugin renders, reads and reviews use case diagrams; keep this diagram as its own view.', 'UML_NOT_A_STATE_DIAGRAM');
|
|
673
|
+
}
|
|
661
674
|
return 'state';
|
|
662
675
|
}
|
|
663
676
|
throw new UmlError('cannot tell which diagram kind this text declares');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-logicprobe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Verifies claims about designs and code. Facts go against the source. Behaviour goes through an executable model: state machines, protocols, composition, data models, migrations. Structure diagrams are read as dependency graphs, at one level or several. Two reports can be compared for regression. Exports UPPAAL, TLA+, PRISM and SPIN input. Ships a native DeepSeek Harness bundle that injects the verification gate into the first model step.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
]
|
|
41
41
|
},
|
|
42
42
|
"compatibility": {
|
|
43
|
-
"dsh": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.2-alpha.3 || ^0.1.2-alpha.4 || ^0.1.2-alpha.5 || ^0.1.2-rc.1 || ^0.1.3-alpha.1 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1 || ^0.1.5-rc.1 || ^0.1.5-alpha.2 || ^0.1.5-rc.2 || ^0.1.5-rc.3 || ^0.1.6-alpha.1 || ^0.1.6-alpha.2 || ^0.1.7-alpha.1 || ^0.1.7-alpha.2 || ^0.1.7-rc.1 || ^0.1.7-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2",
|
|
43
|
+
"dsh": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.2 || ^0.1.2-alpha.3 || ^0.1.2-alpha.4 || ^0.1.2-alpha.5 || ^0.1.2-rc.1 || ^0.1.3-alpha.1 || ^0.1.3-alpha.2 || ^0.1.5-alpha.1 || ^0.1.5-rc.1 || ^0.1.5-alpha.2 || ^0.1.5-rc.2 || ^0.1.5-rc.3 || ^0.1.6-alpha.1 || ^0.1.6-alpha.2 || ^0.1.7-alpha.1 || ^0.1.7-alpha.2 || ^0.1.7-rc.1 || ^0.1.7-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.1 || ^0.2.1-rc.2",
|
|
44
44
|
"dshReleases": {
|
|
45
45
|
"0.1.0-rc.7": "compatible",
|
|
46
46
|
"0.1.0-rc.8": "compatible",
|
|
@@ -67,7 +67,9 @@
|
|
|
67
67
|
"0.2.0-rc.1": "compatible",
|
|
68
68
|
"0.2.0-rc.2": "compatible",
|
|
69
69
|
"0.2.1-alpha.1": "compatible",
|
|
70
|
-
"0.2.1-alpha.2": "compatible"
|
|
70
|
+
"0.2.1-alpha.2": "compatible",
|
|
71
|
+
"0.2.1-rc.1": "compatible",
|
|
72
|
+
"0.2.1-rc.2": "compatible"
|
|
71
73
|
},
|
|
72
74
|
"profiles": [
|
|
73
75
|
"headless",
|
|
@@ -85,11 +87,11 @@
|
|
|
85
87
|
},
|
|
86
88
|
"peerDependencies": {
|
|
87
89
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
88
|
-
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2",
|
|
89
|
-
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2",
|
|
90
|
-
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2",
|
|
91
|
-
"@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.8 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2",
|
|
92
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2",
|
|
90
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.1 || ^0.2.1-rc.2",
|
|
91
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.1 || ^0.2.1-rc.2",
|
|
92
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.1 || ^0.2.1-rc.2",
|
|
93
|
+
"@deepseek-ai/dsh-skill-filesystem": "^0.1.0-rc.8 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.1 || ^0.2.1-rc.2",
|
|
94
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-alpha.1 || ^0.2.1-alpha.2 || ^0.2.1-rc.1 || ^0.2.1-rc.2",
|
|
93
95
|
"@deepseek-ai/schemastery": "^3.18.4"
|
|
94
96
|
},
|
|
95
97
|
"devDependencies": {
|
|
@@ -110,7 +112,7 @@
|
|
|
110
112
|
"@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
|
|
111
113
|
"@deepseek-ai/dsh-user-approval": "^0.2.0-rc.2",
|
|
112
114
|
"@deepseek-ai/schemastery": "^3.18.4",
|
|
113
|
-
"@types/node": "^26.6.
|
|
115
|
+
"@types/node": "^26.6.5",
|
|
114
116
|
"typescript": "^7.0.2"
|
|
115
117
|
},
|
|
116
118
|
"author": {
|
|
@@ -5,6 +5,15 @@ description: "Use when you want to verify something about code: a design doc, sp
|
|
|
5
5
|
|
|
6
6
|
# Logic Probe
|
|
7
7
|
|
|
8
|
+
> **Successor: [`dsh-model-everything`](https://github.com/AmethystLuna/dsh-model-everything).**
|
|
9
|
+
> This plugin continues as `dsh-model-everything`: the same domains, a superset of the
|
|
10
|
+
> tooling, and a skill for each of them. If both are installed, load the
|
|
11
|
+
> `model-everything` skill for new work — it routes to `model-everything-uml`,
|
|
12
|
+
> `model-everything-structure`, `model-everything-datamodel` and
|
|
13
|
+
> `model-everything-interleaving` — and keep this plugin's skills as the reference for its
|
|
14
|
+
> own tool names (`logicprobe_*`). The two do not share a registry: a claim verified through
|
|
15
|
+
> one is not verified in the other.
|
|
16
|
+
|
|
8
17
|
For an unverified design, failure is only a matter of time. Complex code becomes clear once you model it and run it. Check every claim before you act on it.
|
|
9
18
|
|
|
10
19
|
## When This Skill Applies
|
|
@@ -161,6 +170,24 @@ UML modelling variant (the task is to draw a flow, not to check a claim):
|
|
|
161
170
|
→ logicprobe_verify → the behavioural checks (S1-S8 / A1-A14)
|
|
162
171
|
```
|
|
163
172
|
|
|
173
|
+
### Budgeting the Model Before You Run It
|
|
174
|
+
|
|
175
|
+
Search cost is the declared domain times the number of states — and a declared range is
|
|
176
|
+
not a bound on the search.
|
|
177
|
+
|
|
178
|
+
- **`inc`/`dec` are never clamped to `min`/`max`.** The declaration is a property the
|
|
179
|
+
checks verify, not a limit the engine enforces. A counter whose guard does not pin it
|
|
180
|
+
ranges past its own declaration, so the reachable set is unbounded however small the
|
|
181
|
+
declared range looks; S8 reports that as `S8_DOMAIN_NOT_ENFORCED`.
|
|
182
|
+
- **Bound a counter with a guard, not a narrower declaration.** `max: 4` with a guard
|
|
183
|
+
`retry < 4` closes the loop. `max: 4` alone does not.
|
|
184
|
+
- **Say the budget before running**: values per variable x states. A product in the
|
|
185
|
+
10^5-and-up range will hit the cap and come back undecided.
|
|
186
|
+
- **Model the check where the code performs it.** A cap check that lives inside the tick
|
|
187
|
+
handler is a guarded branch of the tick event, not a separate event: as a separate event
|
|
188
|
+
the environment can starve it, and the leads-to failure you then read is an artefact of
|
|
189
|
+
the model rather than a property of the code.
|
|
190
|
+
|
|
164
191
|
### Native dsh Tools
|
|
165
192
|
|
|
166
193
|
| Tool | Covers |
|
|
@@ -203,6 +230,11 @@ Turn the verdict into an answer, never into a summary of `ok`:
|
|
|
203
230
|
|
|
204
231
|
Exit codes: `0` for `pass` / `pass_with_findings`, `2` for `fail`, a refusal, or an unreadable input. In DSH the native tools return the report as data — read `verdict`, and treat `errorCode`/`error` as the refusal path.
|
|
205
232
|
|
|
233
|
+
`summary.truncated: true`, or a finding whose code ends in `_UNDECIDED`
|
|
234
|
+
(`S7_UNDECIDED`, `A7_UNDECIDED`, `A9_LEADS_TO_UNDECIDED`, `D2_UNDECIDED`), means the search
|
|
235
|
+
stopped before deciding. That is **not** a pass: name the invariant that went undecided,
|
|
236
|
+
and say the property is undecided rather than reading the silence as success.
|
|
237
|
+
|
|
206
238
|
### Writing the Answer: Name the Finding, Not Its Code
|
|
207
239
|
|
|
208
240
|
`S2_NO_TRANSITIONS`, `UML019`, `DA2` are values of `findings[].code`, not names. A reader
|
|
@@ -423,6 +455,20 @@ This skill owns behavioural claims. Three neighbouring domains have their own sk
|
|
|
423
455
|
| Concurrency claims — "thread-safe", "lock-free", "no data race", "ISR-safe" — mined and routed to TSan/Helgrind/CBMC/TLA+, never proven here | `logicprobe-concurrency` |
|
|
424
456
|
| Entities, fields, relationships, data invariants, schema migrations (DS1-DS4, DA1-DA12, DD1-DD4) | `logicprobe-datamodel` |
|
|
425
457
|
|
|
458
|
+
### The Same Domains in the Successor
|
|
459
|
+
|
|
460
|
+
[dsh-model-everything](https://github.com/AmethystLuna/dsh-model-everything) covers each domain above with one skill, and adds two this plugin does not have. Use the mapping when both plugins are installed — the successor's skill is the one to load for new work, because its tools are the ones its own guidance names:
|
|
461
|
+
|
|
462
|
+
| This plugin | `dsh-model-everything` | What the successor adds |
|
|
463
|
+
|---|---|---|
|
|
464
|
+
| `logicprobe` | `model-everything` | the same doctrine over `model_verify`, `model_compose_verify` and `model_export` |
|
|
465
|
+
| `logicprobe-uml` | `model-everything-uml` | more diagram families (timing, profile, composite, communication, interaction overview) |
|
|
466
|
+
| `logicprobe-structure` | `model-everything-structure` | multi-level refinement between drawings |
|
|
467
|
+
| `logicprobe-datamodel` | `model-everything-datamodel` | the same DS/DA/DD families over `model_datamodel_verify` |
|
|
468
|
+
| `logicprobe-concurrency` | `model-everything-interleaving` | async vocabulary, a family per claim, and a lost update decided rather than routed |
|
|
469
|
+
| — | `model-everything-design` | layering, substitutability, interface width (no counterpart here) |
|
|
470
|
+
| — | `model-everything-report-reading` | how to read a report (no counterpart here) |
|
|
471
|
+
|
|
426
472
|
---
|
|
427
473
|
|
|
428
474
|
## Red Flags
|
|
@@ -437,6 +483,8 @@ If you catch yourself thinking any of these, stop:
|
|
|
437
483
|
| "I'll check while implementing" | Check before the decision, not after. |
|
|
438
484
|
| "Reasoning is enough" | Reasoning is not a run. One counter-example kills a universal claim. |
|
|
439
485
|
| "The report passed, so the code is fine" | The report covers the model you gave it. Reconcile it, and say what you did not check. |
|
|
486
|
+
| "The declared range limits the search" | The declaration is a property to verify. `inc`/`dec` are never clamped to it; only a guard bounds the value. |
|
|
487
|
+
| "The check found nothing" | Read the verdict: `truncated` and `_UNDECIDED` mean the search stopped, not that the property holds. |
|
|
440
488
|
|
|
441
489
|
## Rules
|
|
442
490
|
|
|
@@ -449,4 +497,5 @@ If you catch yourself thinking any of these, stop:
|
|
|
449
497
|
7. **Don't verify what the code already checks.** If the existing codebase has compile-time assertions, static analysis, or runtime checks for a property, cite those — don't re-verify in a model.
|
|
450
498
|
8. **Hand a neighbouring domain to its own skill.** A diagram is a model, not evidence: modelling or auditing one belongs to `logicprobe-uml`. A concurrency claim is mined and routed, never proven here: that is `logicprobe-concurrency`. Schema, data invariants and migrations are `logicprobe-datamodel`. This skill keeps the doctrine and the behavioural checks.
|
|
451
499
|
9. **Say what this does not prove.** For dimensions logicprobe does not verify — hard real time (deadlines, periods), preemptive concurrency, hybrid-control stability, probabilistic reliability, execution-cost budgets — route to the dedicated tool named in `references/gap-routing-guide.md`. `logicprobe_verify` reports carry the same routing as informational `coverageNotes` on matching models.
|
|
452
|
-
10. **
|
|
500
|
+
10. **A declared range is a property, not a bound, and a capped search is not a pass.** `inc`/`dec` are never clamped to `min`/`max`; only a guard that pins the value bounds it. When a report is `truncated` or a check is `_UNDECIDED`, say the property is undecided and narrow the model — never report it as verified.
|
|
501
|
+
11. **Write-site analysis is out of scope, and stays there.** "Only the ISR writes this flag", "one owner per register" — logicprobe verifies models and diagrams, not *who writes what in the source*. That claim needs a source-side AST/rule check or a static analyzer; this plugin's contribution is to flag the claim as unverified and route it (`logicprobe_concurrency_scan` does exactly that). Do not imply the diagram or the model settled it.
|
|
@@ -225,6 +225,17 @@ A guard is exactly one of:
|
|
|
225
225
|
|
|
226
226
|
`inc`/`dec` default to 1 when `value` is omitted. `set` defaults to 0.
|
|
227
227
|
|
|
228
|
+
`inc` and `dec` are **never clamped** to the variable's declared `min`/`max`. The declared
|
|
229
|
+
range is a property the checks verify (S7 `var-in-range`), not a bound on the search: a
|
|
230
|
+
counter whose guard does not pin it ranges past its own declaration, so the reachable set
|
|
231
|
+
is unbounded however narrow the declaration looks. S8 reports that as
|
|
232
|
+
`S8_DOMAIN_NOT_ENFORCED` and names the transition, rather than clamping the value and
|
|
233
|
+
deleting the states a range violation is witnessed by. A `set` constant outside the
|
|
234
|
+
declared range contradicts the declaration and is `S8_DOMAIN_SET_OUTSIDE` (error).
|
|
235
|
+
|
|
236
|
+
To bound a counter, add a guard that pins it — `"max": 4` with a guard
|
|
237
|
+
`{ "variable": "retry", "op": "<", "value": 4 }` — not a narrower `max`.
|
|
238
|
+
|
|
228
239
|
## Invariants
|
|
229
240
|
|
|
230
241
|
Every invariant carries `id` and **`description`** — both required strings — plus a
|
|
@@ -396,9 +407,14 @@ The report's `comparison` object includes both model hashes, state/transition co
|
|
|
396
407
|
|
|
397
408
|
List events that must be idempotent in `idempotentEvents`. For every reachable state, applying the event twice must produce the same state as applying it once. This is useful for retries, webhook redelivery, and migration replay.
|
|
398
409
|
|
|
399
|
-
## Advanced constraints (S8, A9-A14)
|
|
410
|
+
## Advanced constraints (S6, S8, A9-A14)
|
|
400
411
|
|
|
401
|
-
- **
|
|
412
|
+
- **S6 Guard Completeness** also reports `S6_DEAD_GUARD`: a guarded branch no reachable
|
|
413
|
+
variable value can satisfy is a branch that can never fire. The test runs over the
|
|
414
|
+
values the model can produce — each variable's `init` plus every `set` constant — not
|
|
415
|
+
over the declared domain, so a guard on a value the model never writes is satisfiable on
|
|
416
|
+
paper and dead in practice.
|
|
417
|
+
- **S8 Variable Declarations**: declare `monotonic: "inc"|"dec"` on a variable; updates must not move in the opposite direction. The same check reads the declared range: `S8_DOMAIN_NOT_ENFORCED` reports an `inc`/`dec` no guard keeps inside `min`/`max`, and `S8_DOMAIN_SET_OUTSIDE` (error) reports a `set` constant outside it.
|
|
402
418
|
- **A9 Leads-To**: `{ kind: "leads-to", from, to }` — every path from `from` must eventually reach `to`. `to` is one state id or a non-empty array of state ids for "reach any one of these"; a duplicated member is a validation error rather than a shorthand. Either way the property stays universal: one branch that loops forever, or that stops before every target, refutes it.
|
|
403
419
|
- **A10 Sequence**: `{ kind: "sequence", events }` — events must appear in order.
|
|
404
420
|
- **A11 Atomicity**: `{ kind: "atomicity", events, commit, rollback? }` — once an atomic event starts, the machine must reach commit/rollback before leaving the atomic scope or terminating.
|
|
@@ -408,7 +424,8 @@ List events that must be idempotent in `idempotentEvents`. For every reachable s
|
|
|
408
424
|
|
|
409
425
|
## Limits
|
|
410
426
|
|
|
411
|
-
-
|
|
427
|
+
- Each breadth-first exploration caps at `maxStates` (default 10000). The leads-to walk caps at twenty times that, **shared across every start state of one check**: it is depth-first over run states, and an unbounded counter makes every one of them distinct, so it has no shorter bound available.
|
|
428
|
+
- A cap that bites is **reported, never silently passed**. The report sets `summary.truncated`, and the check that stopped carries a finding whose code ends in `_UNDECIDED` — `S7_UNDECIDED`, `A7_UNDECIDED`, `A9_LEADS_TO_UNDECIDED`, `D2_UNDECIDED`. An undecided check has proven neither the violation nor its absence; read it as "not decided", never as a pass.
|
|
412
429
|
- A3 samples the first `maxPermutationEvents` events (default 5).
|
|
413
430
|
- The engine is a finite-state model checker. It cannot prove properties of the real implementation; follow with code-level review.
|
|
414
431
|
- `cost` values are modeler-provided static labels — A12 verifies against them; real execution time/WCET needs binary-level timing analysis.
|
|
@@ -5,6 +5,8 @@ description: "Use when a design or plan claims concurrency safety (thread-safe,
|
|
|
5
5
|
|
|
6
6
|
# Logic Probe — Concurrency Claims
|
|
7
7
|
|
|
8
|
+
> **Successor: `dsh-model-everything`.** Its counterpart skill is `model-everything-interleaving`, which covers the same claims plus async and hands a shared location to `model_compose_verify` instead of routing it. Load that one when both plugins are installed; this skill stays accurate for `logicprobe_concurrency_scan`.
|
|
9
|
+
|
|
8
10
|
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
11
|
|
|
10
12
|
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.
|
|
@@ -5,6 +5,8 @@ description: "Use when reviewing design docs, specs, plans or migrations claimin
|
|
|
5
5
|
|
|
6
6
|
# Logic Probe Data
|
|
7
7
|
|
|
8
|
+
> **Successor: `dsh-model-everything`.** Its counterpart skill is `model-everything-datamodel`, over `model_datamodel_verify`. Load that one when both plugins are installed; this skill stays accurate for `logicprobe_datamodel_verify`.
|
|
9
|
+
|
|
8
10
|
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
11
|
|
|
10
12
|
## Methodology
|
|
@@ -5,6 +5,8 @@ description: "Use when reviewing architecture, module structure or a dependency
|
|
|
5
5
|
|
|
6
6
|
# Logic Probe — Structure and Dependency Review
|
|
7
7
|
|
|
8
|
+
> **Successor: `dsh-model-everything`.** Its counterpart skill is `model-everything-structure`, over `uml_structure_verify`, which also checks several drawings against each other. Load that one when both plugins are installed; this skill stays accurate for `logicprobe_structure_verify`.
|
|
9
|
+
|
|
8
10
|
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
11
|
|
|
10
12
|
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*.
|
|
@@ -5,6 +5,8 @@ description: "Use when drawing a code flow as UML or auditing a diagram someone
|
|
|
5
5
|
|
|
6
6
|
# Logic Probe — UML Modelling
|
|
7
7
|
|
|
8
|
+
> **Successor: `dsh-model-everything`.** Its counterpart skill is `model-everything-uml`, which draws and reads more diagram families. Load that one when both plugins are installed; this skill stays accurate for `logicprobe_uml`.
|
|
9
|
+
|
|
8
10
|
A diagram is a model, and a model can be wrong. A tidy flow chart is not evidence about the code, and a diagram that disagrees with its own model is worse than no diagram, because a reader will believe it.
|
|
9
11
|
|
|
10
12
|
This skill covers the diagram half of the Logic Probe toolbox. 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 to verify behaviour rather than to draw or audit a diagram.
|