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.
- package/README.en-US.md +79 -14
- package/README.md +79 -14
- package/lib/baseline.js +142 -0
- package/lib/client.js +56 -1
- package/lib/compose-tool.js +1 -1
- package/lib/concurrency-tool.js +1 -1
- package/lib/concurrency.js +14 -0
- package/lib/data-engine.js +42 -8
- package/lib/engine.js +609 -118
- package/lib/export-tool.js +27 -2
- package/lib/granularity.js +237 -0
- package/lib/index.js +63 -32
- package/lib/report-diff-tool.js +39 -0
- package/lib/structure-tool.js +64 -0
- package/lib/structure.js +648 -0
- package/lib/tool.js +3 -3
- package/lib/types/baseline.d.ts +69 -0
- package/lib/types/concurrency.d.ts +10 -0
- package/lib/types/data-engine.d.ts +13 -1
- package/lib/types/engine.d.ts +139 -2
- package/lib/types/granularity.d.ts +106 -0
- package/lib/types/index.d.ts +9 -8
- package/lib/types/report-diff-tool.d.ts +8 -0
- package/lib/types/structure-tool.d.ts +10 -0
- package/lib/types/structure.d.ts +156 -0
- package/lib/types/tool.d.ts +2 -2
- package/lib/types/uml.d.ts +89 -0
- package/lib/uml-tool.js +36 -7
- package/lib/uml.js +217 -23
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +16 -14
- package/skills/logicprobe/SKILL.md +97 -58
- package/skills/logicprobe/references/dsh-model-schema.md +87 -15
- package/skills/logicprobe/references/gap-routing-guide.md +37 -36
- package/skills/logicprobe/references/hash-spec.md +128 -0
- package/skills/logicprobe/references/logic-verification-guide.md +29 -248
- package/skills/logicprobe-concurrency/SKILL.md +43 -0
- package/skills/{logicprobe → logicprobe-concurrency}/references/concurrency-risk-guide.md +1 -1
- package/skills/logicprobe-datamodel/SKILL.md +5 -4
- package/skills/logicprobe-datamodel/references/data-model-guide.md +1 -1
- package/skills/logicprobe-datamodel/references/data-model-schema.md +1 -1
- package/skills/logicprobe-structure/SKILL.md +95 -0
- package/skills/logicprobe-structure/references/structure-review-guide.md +177 -0
- package/skills/logicprobe-uml/SKILL.md +55 -0
- package/skills/{logicprobe → logicprobe-uml}/references/uml-modeling-guide.md +120 -8
- package/src/baseline.ts +180 -0
- package/src/client.js +56 -1
- package/src/compose-tool.ts +47 -47
- package/src/concurrency-tool.ts +2 -2
- package/src/concurrency.ts +23 -0
- package/src/data-engine.ts +51 -9
- package/src/engine.ts +656 -101
- package/src/export-tool.ts +73 -48
- package/src/granularity.ts +312 -0
- package/src/index.ts +63 -32
- package/src/report-diff-tool.ts +43 -0
- package/src/structure-tool.ts +68 -0
- package/src/structure.ts +724 -0
- package/src/tool.ts +4 -4
- package/src/uml-tool.ts +38 -8
- package/src/uml.ts +275 -24
- package/skills/logicprobe/references/logicprobe-engine.py +0 -4780
- package/skills/logicprobe/references/verification-harness.py +0 -959
- package/skills/logicprobe-datamodel/references/data-model-harness.py +0 -528
package/lib/types/uml.d.ts
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*
|
|
31
31
|
* @module logicprobe-uml
|
|
32
32
|
*/
|
|
33
|
+
import { type HashSpec, type NarrativeCoverage, type ReportSchema, type Verdict } from './engine.js';
|
|
33
34
|
import type { GuardNode, LogicModelV1, UpdateSpec } from './engine.js';
|
|
34
35
|
export type UmlNotation = 'mermaid' | 'plantuml';
|
|
35
36
|
export type UmlDiagram = 'state' | 'activity' | 'sequence';
|
|
@@ -52,8 +53,29 @@ export interface UmlParseResult {
|
|
|
52
53
|
* symbol, which the review reports.
|
|
53
54
|
*/
|
|
54
55
|
labels: Record<string, string>;
|
|
56
|
+
/**
|
|
57
|
+
* Declarations found in the text that LogicModelV1 cannot represent. A non-empty
|
|
58
|
+
* list means the parsed model is NOT this diagram: the text belongs to another
|
|
59
|
+
* diagram family (component, package, class, deployment, …), so whatever came
|
|
60
|
+
* out is a by-product of reading keywords the parser does not own.
|
|
61
|
+
*/
|
|
62
|
+
discardedConstructs: DiscardedConstruct[];
|
|
63
|
+
/**
|
|
64
|
+
* Arrows whose endpoints were declared by a discarded construct. They survive
|
|
65
|
+
* parsing as state transitions, which is exactly why they are counted out loud.
|
|
66
|
+
*/
|
|
67
|
+
discardedEdges: number;
|
|
55
68
|
warnings: string[];
|
|
56
69
|
}
|
|
70
|
+
/** One declaration the parser could not represent, with the source line that carried it. */
|
|
71
|
+
export interface DiscardedConstruct {
|
|
72
|
+
/** The construct keyword, lower-cased (`component`, `package`, `classDiagram`). */
|
|
73
|
+
construct: string;
|
|
74
|
+
/** 1-based line number in the diagram source. */
|
|
75
|
+
line: number;
|
|
76
|
+
/** The source line, trimmed. */
|
|
77
|
+
text: string;
|
|
78
|
+
}
|
|
57
79
|
export interface UmlFinding {
|
|
58
80
|
code: string;
|
|
59
81
|
severity: 'error' | 'warning' | 'info';
|
|
@@ -66,18 +88,28 @@ export interface UmlFinding {
|
|
|
66
88
|
to: string;
|
|
67
89
|
}>;
|
|
68
90
|
detail?: string;
|
|
91
|
+
/** Machine-readable context for the finding (ids, coverage, rule matches), same shape as the engine's findings. */
|
|
92
|
+
evidence?: Record<string, unknown>;
|
|
69
93
|
}
|
|
70
94
|
export interface UmlRoundTripReport {
|
|
71
95
|
notation: UmlNotation;
|
|
72
96
|
diagram: UmlDiagram;
|
|
73
97
|
ok: boolean;
|
|
98
|
+
/** Which published hash specification the two hashes follow (see references/hash-spec.md). */
|
|
99
|
+
hashSpec: HashSpec;
|
|
74
100
|
modelHash: string;
|
|
75
101
|
parsedHash: string;
|
|
76
102
|
diffs: string[];
|
|
77
103
|
warnings: string[];
|
|
78
104
|
}
|
|
79
105
|
export interface UmlReviewReport {
|
|
106
|
+
/** The review ran and produced this report. It does NOT mean the modelling passed — read `verdict`. */
|
|
80
107
|
ok: boolean;
|
|
108
|
+
ran: boolean;
|
|
109
|
+
verdict: Verdict;
|
|
110
|
+
verdictReason: string;
|
|
111
|
+
/** The versioned report contract this result follows. */
|
|
112
|
+
schema: ReportSchema;
|
|
81
113
|
source: 'model' | 'diagram' | 'model+diagram';
|
|
82
114
|
summary: {
|
|
83
115
|
errors: number;
|
|
@@ -98,10 +130,30 @@ export interface UmlReviewReport {
|
|
|
98
130
|
model?: LogicModelV1;
|
|
99
131
|
/** Diagram rendered from the model, when only a model was given. */
|
|
100
132
|
primary?: string;
|
|
133
|
+
/** Paths of the `_`-prefixed metadata keys found in the supplied model, when it carried any. */
|
|
134
|
+
metadataKeys?: string[];
|
|
135
|
+
/** How much of the model the narrative documents; absent when there is no narrative. */
|
|
136
|
+
narrativeCoverage?: NarrativeCoverage;
|
|
137
|
+
/** Hashes of what was reviewed, in one place. */
|
|
138
|
+
hashes: {
|
|
139
|
+
hashSpec: HashSpec;
|
|
140
|
+
modelHash?: string;
|
|
141
|
+
parsedHash?: string;
|
|
142
|
+
};
|
|
143
|
+
/** Declarations the parser could not represent; a non-empty list fails the review. */
|
|
144
|
+
discardedConstructs?: DiscardedConstruct[];
|
|
145
|
+
/** Arrows whose endpoints came from a discarded construct. */
|
|
146
|
+
discardedEdges?: number;
|
|
101
147
|
warnings: string[];
|
|
102
148
|
nextSteps: string[];
|
|
103
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* A UML front-end refusal. `code` lets the tool report *why* it refused instead of
|
|
152
|
+
* collapsing every refusal into a generic input error.
|
|
153
|
+
*/
|
|
104
154
|
export declare class UmlError extends Error {
|
|
155
|
+
readonly code: string;
|
|
156
|
+
constructor(message: string, code?: string);
|
|
105
157
|
}
|
|
106
158
|
/** Canonical guard text. Rendering wraps every composite node in parentheses, and the parser flattens same-operator chains, so render∘parse is the identity. */
|
|
107
159
|
export declare function guardText(node: GuardNode): string;
|
|
@@ -124,6 +176,43 @@ export declare function renderUml(input: unknown, notation?: UmlNotation, diagra
|
|
|
124
176
|
export declare function parseGuardText(text: string): GuardNode;
|
|
125
177
|
/** Parse a UML action clause such as `retry := retry + 1, armed := true`. */
|
|
126
178
|
export declare function parseUpdatesText(text: string, warnings: string[]): UpdateSpec[];
|
|
179
|
+
/**
|
|
180
|
+
* Findings a parse result carries on its own. A diagram from another family parses
|
|
181
|
+
* into something; without this finding that something is presented as a model of the
|
|
182
|
+
* file, which is a false guarantee of exactly the kind this plugin exists to prevent.
|
|
183
|
+
*/
|
|
184
|
+
export declare function parseFindings(parsed: UmlParseResult): UmlFinding[];
|
|
185
|
+
/** One accepted way of writing a state's meaning into a diagram label. */
|
|
186
|
+
export interface LabelForm {
|
|
187
|
+
form: string;
|
|
188
|
+
example: string;
|
|
189
|
+
note: string;
|
|
190
|
+
}
|
|
191
|
+
export interface LabelExplanation {
|
|
192
|
+
notation: UmlNotation;
|
|
193
|
+
/** The comment prefix a `logicprobe:` directive uses in this notation. */
|
|
194
|
+
directivePrefix: string;
|
|
195
|
+
directiveLines: Array<{
|
|
196
|
+
example: string;
|
|
197
|
+
meaning: string;
|
|
198
|
+
}>;
|
|
199
|
+
/** Accepted label spellings, in the order `documentedMeaning` accepts them. */
|
|
200
|
+
acceptedLabelForms: LabelForm[];
|
|
201
|
+
/** What the renderer itself writes, so a hand-edited diagram can match it. */
|
|
202
|
+
renderedForm: string;
|
|
203
|
+
ignoredLines: Array<{
|
|
204
|
+
pattern: string;
|
|
205
|
+
behaviour: string;
|
|
206
|
+
}>;
|
|
207
|
+
rules: string[];
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* What the front end expects of labels and comments, so "why does my hand-drawn diagram
|
|
211
|
+
* report label drift or ignored lines?" has a printed answer instead of a guess.
|
|
212
|
+
* `tests/uml/run.mjs` feeds every claim here back through the parser, so this text cannot
|
|
213
|
+
* drift away from the behaviour it describes.
|
|
214
|
+
*/
|
|
215
|
+
export declare function explainLabels(notation?: UmlNotation): LabelExplanation;
|
|
127
216
|
/**
|
|
128
217
|
* Parse a Mermaid or PlantUML diagram back into a LogicModelV1.
|
|
129
218
|
*
|
package/lib/uml-tool.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
-
import {
|
|
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, UmlError } from './uml.js';
|
|
3
5
|
export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml';
|
|
4
6
|
/**
|
|
5
7
|
* DSH tool wrapping the UML front end: model a code flow as a UML diagram
|
|
@@ -15,7 +17,7 @@ export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml';
|
|
|
15
17
|
*/
|
|
16
18
|
export const logicProbeUmlTool = defineTool({
|
|
17
19
|
name: LOGICPROBE_UML_TOOL_NAME,
|
|
18
|
-
description: 'Model a code flow as UML, and
|
|
20
|
+
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. 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. Read `verdict`, not `ok`: error findings mean a failed review.',
|
|
19
21
|
parameters: {
|
|
20
22
|
action: {
|
|
21
23
|
type: 'string',
|
|
@@ -76,13 +78,32 @@ export const logicProbeUmlTool = defineTool({
|
|
|
76
78
|
if (args.model === undefined)
|
|
77
79
|
return errorResult('action=render needs `model` (a LogicModelV1 object)');
|
|
78
80
|
const result = renderUml(args.model, (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation), (args.kind ?? 'state'), args.maxSteps);
|
|
79
|
-
return { ok: true, action: 'render', notation: result.notation, kind: result.diagram, diagram: result.primary, warnings: result.warnings };
|
|
81
|
+
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 };
|
|
80
82
|
}
|
|
81
83
|
if (args.action === 'parse') {
|
|
82
84
|
if (args.diagram === undefined)
|
|
83
85
|
return errorResult('action=parse needs `diagram` (Mermaid or PlantUML text)');
|
|
84
86
|
const result = parseUml(args.diagram, args.notation ?? 'auto');
|
|
85
|
-
|
|
87
|
+
// A diagram from another family still parses into *something*. `parseFindings`
|
|
88
|
+
// says so, and the verdict is what stops that something from being trusted:
|
|
89
|
+
// `ok: true` only means the parser ran.
|
|
90
|
+
const findings = parseFindings(result);
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
ran: true,
|
|
94
|
+
...parseVerdict(result, findings),
|
|
95
|
+
schema: REPORT_SCHEMAS.umlParse,
|
|
96
|
+
action: 'parse',
|
|
97
|
+
notation: result.notation,
|
|
98
|
+
kind: result.diagram,
|
|
99
|
+
model: result.model,
|
|
100
|
+
labels: result.labels,
|
|
101
|
+
findings,
|
|
102
|
+
discardedConstructs: result.discardedConstructs,
|
|
103
|
+
discardedEdges: result.discardedEdges,
|
|
104
|
+
hashes: { hashSpec: DEFAULT_HASH_SPEC, modelHash: modelHash(result.model), diagram: createHash('sha256').update(args.diagram).digest('hex') },
|
|
105
|
+
warnings: result.warnings,
|
|
106
|
+
};
|
|
86
107
|
}
|
|
87
108
|
const report = reviewUml({
|
|
88
109
|
model: args.model,
|
|
@@ -95,10 +116,18 @@ export const logicProbeUmlTool = defineTool({
|
|
|
95
116
|
return report;
|
|
96
117
|
}
|
|
97
118
|
catch (error) {
|
|
98
|
-
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
return errorResult(message, error instanceof UmlError ? error.code : undefined);
|
|
99
121
|
}
|
|
100
122
|
},
|
|
101
123
|
});
|
|
102
|
-
|
|
103
|
-
|
|
124
|
+
/** The parse verdict counts parser notes as findings: an ignored line is information the model does not carry. */
|
|
125
|
+
function parseVerdict(result, findings) {
|
|
126
|
+
const errors = findings.filter((finding) => finding.severity === 'error').length;
|
|
127
|
+
const warnings = findings.filter((finding) => finding.severity === 'warning').length + result.warnings.length;
|
|
128
|
+
const firstCode = findings.find((finding) => finding.severity === 'error')?.code ?? findings.find((finding) => finding.severity === 'warning')?.code;
|
|
129
|
+
return verdictOf(errors, warnings, firstCode);
|
|
130
|
+
}
|
|
131
|
+
function errorResult(message, errorCode = 'UML_INPUT') {
|
|
132
|
+
return { ok: false, ran: false, ...refusalVerdict(message), errorCode, error: message };
|
|
104
133
|
}
|