dsh-logicprobe 0.9.0 → 1.1.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 (63) hide show
  1. package/README.en-US.md +128 -66
  2. package/README.md +128 -66
  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.js +14 -0
  7. package/lib/data-engine.js +42 -8
  8. package/lib/engine.js +3151 -2801
  9. package/lib/export-tool.js +27 -2
  10. package/lib/granularity.js +237 -0
  11. package/lib/index.js +63 -41
  12. package/lib/report-diff-tool.js +39 -0
  13. package/lib/structure-tool.js +64 -0
  14. package/lib/structure.js +648 -0
  15. package/lib/tool.js +3 -3
  16. package/lib/types/baseline.d.ts +69 -0
  17. package/lib/types/concurrency.d.ts +10 -0
  18. package/lib/types/data-engine.d.ts +13 -1
  19. package/lib/types/engine.d.ts +419 -277
  20. package/lib/types/granularity.d.ts +106 -0
  21. package/lib/types/index.d.ts +9 -8
  22. package/lib/types/report-diff-tool.d.ts +8 -0
  23. package/lib/types/structure-tool.d.ts +10 -0
  24. package/lib/types/structure.d.ts +156 -0
  25. package/lib/types/tool.d.ts +2 -2
  26. package/lib/types/uml.d.ts +89 -0
  27. package/lib/uml-tool.js +146 -104
  28. package/lib/uml.js +1748 -1544
  29. package/locale/en.json +6 -0
  30. package/locale/zh.json +6 -0
  31. package/package.json +16 -14
  32. package/skills/logicprobe/SKILL.md +84 -58
  33. package/skills/logicprobe/references/dsh-model-schema.md +102 -10
  34. package/skills/logicprobe/references/gap-routing-guide.md +37 -36
  35. package/skills/logicprobe/references/hash-spec.md +153 -0
  36. package/skills/logicprobe/references/logic-verification-guide.md +27 -250
  37. package/skills/logicprobe-concurrency/SKILL.md +43 -0
  38. package/skills/{logicprobe → logicprobe-concurrency}/references/concurrency-risk-guide.md +1 -1
  39. package/skills/logicprobe-datamodel/SKILL.md +5 -4
  40. package/skills/logicprobe-datamodel/references/data-model-guide.md +1 -1
  41. package/skills/logicprobe-datamodel/references/data-model-schema.md +1 -1
  42. package/skills/logicprobe-structure/SKILL.md +95 -0
  43. package/skills/logicprobe-structure/references/structure-review-guide.md +177 -0
  44. package/skills/logicprobe-uml/SKILL.md +57 -0
  45. package/skills/{logicprobe → logicprobe-uml}/references/uml-modeling-guide.md +121 -8
  46. package/src/baseline.ts +180 -0
  47. package/src/client.js +56 -1
  48. package/src/compose-tool.ts +47 -47
  49. package/src/concurrency.ts +23 -0
  50. package/src/data-engine.ts +51 -9
  51. package/src/engine.ts +503 -72
  52. package/src/export-tool.ts +73 -48
  53. package/src/granularity.ts +312 -0
  54. package/src/index.ts +63 -41
  55. package/src/report-diff-tool.ts +43 -0
  56. package/src/structure-tool.ts +68 -0
  57. package/src/structure.ts +724 -0
  58. package/src/tool.ts +3 -3
  59. package/src/uml-tool.ts +53 -10
  60. package/src/uml.ts +288 -24
  61. package/skills/logicprobe/references/logicprobe-engine.py +0 -4943
  62. package/skills/logicprobe/references/verification-harness.py +0 -959
  63. package/skills/logicprobe-datamodel/references/data-model-harness.py +0 -528
package/src/tool.ts CHANGED
@@ -10,13 +10,13 @@ export const LOGICPROBE_VERIFY_TOOL_NAME = 'logicprobe_verify'
10
10
  * the 22-check report (S1-S8 structural, A1-A14), or a 26-check
11
11
  * report when beforeModel is supplied (D1 behavioral preservation, D2
12
12
  * invariant continuity, D3 regression delta, D4 deadlock/liveness regression).
13
- * This is the dsh-native replacement for hand-filling the Python template
14
- * shipped in the skill references.
13
+ * This is the dsh-native path: the model is built and run here, with no
14
+ * template to fill in by hand.
15
15
  */
16
16
  export const logicProbeVerifyTool = defineTool({
17
17
  name: LOGICPROBE_VERIFY_TOOL_NAME,
18
18
  description:
19
- 'Run executable state-machine verification (logicprobe). Takes a LogicModelV1 object with schemaVersion=1, init, states ({id, terminal?}), transitions ({from, event, to, guard?, updates?, cost?}), variables?, invariants?, concurrentPairs?, boundaryChecks?, resourcePairs?, idempotentEvents?, narrative?. Guards are structured ({variable, op, value} | {all} | {any} | {not}); invariants support never-states, var-in-range ({variable, min?, max?, when?} — when scopes the range to a state ({state}) or a guard node, is evaluated on the post-state of every transition, leaves the initial state checked unconditionally, and is rejected on any other invariant kind), event-before-state, leads-to (`to` is one state id or a non-empty array of them: every path must reach at least one member), sequence, atomicity, budget (transition cost defaults to 1; A12 checks every reachable path stays within the declared budget and reports the shortest over-budget counterexample), and probability (transition weight, default 1, makes the model a DTMC; A13 checks P(hit target) against the bound by value iteration), and deadline (declaring maxTicks on a state is how many tick steps it may stay resident and the top-level tickEvents list names those steps; A14 reports the over-residency path, and A14_NO_TICK_EVENTS when maxTicks is declared without tickEvents). The schema is closed: an undeclared key on any model part (model, state, transition, update, variable, invariant, boundaryCheck, resourcePair, narrative, scenario) is a validation error, never ignored. Variables support monotonic inc/dec. The optional narrative block carries natural-language descriptions of states (narrative.states), events (narrative.events), and (state, event) scenarios (narrative.scenarios: [{from, event, scenario}]); when present it must fully cover the model and is echoed in the report. Returns a report with S1-S8 structural checks and A1-A14 adversarial, cost, probability and deadline probes, including counterexample paths (shortest where the search is breadth-first). If beforeModel is provided, also runs D1-D4 before/after regression checks. See skills/logicprobe/references/dsh-model-schema.md.',
19
+ 'Run state-machine verification on a LogicModelV1 object. Model shape: schemaVersion 1, init, states ({id, terminal?}), transitions ({from, event, to, guard?, updates?, cost?}), plus optional variables, invariants, concurrentPairs, boundaryChecks, resourcePairs, idempotentEvents and narrative. Guards are structured: {variable, op, value}, {all}, {any} or {not}. Invariants cover never-states, var-in-range, event-before-state, leads-to, sequence, atomicity, budget, probability and deadline. `cost` defaults to 1 and drives the A12 budget check. `weight` defaults to 1 and makes the model a DTMC for A13. A state with `maxTicks` needs top-level `tickEvents`; A14 reports over-residency. The schema is closed: an undeclared key is a validation error. Keys starting with `_` are metadata: ignored, kept out of `modelHash`, and echoed as `metadataKeys`. Variables support monotonic inc and dec. Pass `beforeModel` and `stateMapping` to add the D1-D4 regression. Read `verdict`, never `ok`. The full schema is in the `logicprobe` skill: references/dsh-model-schema.md.',
20
20
  parameters: {
21
21
  model: {
22
22
  type: 'json',
package/src/uml-tool.ts CHANGED
@@ -1,6 +1,8 @@
1
- import { defineTool } from '@deepseek-ai/dsh-tools'
1
+ import { defineTool } from '@deepseek-ai/dsh-tools'
2
+ import { createHash } from 'node:crypto'
2
3
  import type { JsonValue } from './json-value.js'
3
- import { renderUml, parseUml, reviewUml, UmlError, type UmlDiagram, type UmlNotation } from './uml.js'
4
+ import { REPORT_SCHEMAS, DEFAULT_HASH_SPEC, modelHash, refusalVerdict, verdictOf, verdictOfFindings, type VerdictSummary } from './engine.js'
5
+ import { renderUml, parseUml, parseFindings, reviewUml, explainLabels, UmlError, type UmlDiagram, type UmlFinding, type UmlNotation, type UmlParseResult } from './uml.js'
4
6
 
5
7
  export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml'
6
8
 
@@ -19,13 +21,13 @@ export const LOGICPROBE_UML_TOOL_NAME = 'logicprobe_uml'
19
21
  export const logicProbeUmlTool = defineTool({
20
22
  name: LOGICPROBE_UML_TOOL_NAME,
21
23
  description:
22
- 'Model a code flow as UML, and review the modelling. action="render" turns a LogicModelV1 (schemaVersion=1) into diagram source: notation mermaid (state | activity flowchart | sequence) or plantuml (state | sequence). action="parse" reads Mermaid/PlantUML state or activity text back into a LogicModelV1 (plus the display labels it found), so a hand-drawn diagram can be verified with logicprobe_verify; a sequence diagram is refused because a trace cannot reconstruct a machine. action="review" audits the modelling: structural defects (UML002 unreachable state, UML003 dead end, UML004 ambiguous branch, UML005 overlapping guard, UML006 inexhaustive branch, UML007 unused event, UML008 self-loop with no exit, UML009 duplicate transition), documentation gaps (UML010 unused variable, UML011 unbounded variable, UML012 no terminal, UML013 no narrative, UML014 undocumented state, UML015 label drift), and the fidelity check — the diagram is rendered and re-parsed and any structural difference is reported as UML017 round-trip mismatch. Give review a model (checks it, renders and re-parses it), a diagram (parses and reviews that), or both (checks the diagram against the model). Rendering never invents structure and never silently drops a construct the notation cannot express: those become warnings. Review does NOT replace logicprobe_verify — it covers the modelling, not behaviour; findings name the engine check to run next.',
24
+ '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. Read `verdict`, not `ok`: error findings mean a failed review.',
23
25
  parameters: {
24
26
  action: {
25
27
  type: 'string',
26
28
  required: true,
27
- enum: ['render', 'parse', 'review'],
28
- description: 'render = model → UML source; parse = UML source → model; review = audit the modelling (and its fidelity to the model).',
29
+ enum: ['render', 'parse', 'review', 'explain-labels'],
30
+ 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.',
29
31
  },
30
32
  model: {
31
33
  type: 'json',
@@ -79,12 +81,44 @@ export const logicProbeUmlTool = defineTool({
79
81
  if (args.action === 'render') {
80
82
  if (args.model === undefined) return errorResult('action=render needs `model` (a LogicModelV1 object)')
81
83
  const result = renderUml(args.model, (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation) as UmlNotation, (args.kind ?? 'state') as UmlDiagram, args.maxSteps)
82
- return { ok: true, action: 'render', notation: result.notation, kind: result.diagram, diagram: result.primary, warnings: result.warnings } as unknown as JsonValue
84
+ 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 } as unknown as JsonValue
83
85
  }
84
86
  if (args.action === 'parse') {
85
87
  if (args.diagram === undefined) return errorResult('action=parse needs `diagram` (Mermaid or PlantUML text)')
86
88
  const result = parseUml(args.diagram, args.notation ?? 'auto')
87
- return { ok: true, action: 'parse', notation: result.notation, kind: result.diagram, model: result.model, labels: result.labels, warnings: result.warnings } as unknown as JsonValue
89
+ // A diagram from another family still parses into *something*. `parseFindings`
90
+ // says so, and the verdict is what stops that something from being trusted:
91
+ // `ok: true` only means the parser ran.
92
+ const findings = parseFindings(result)
93
+ return {
94
+ ok: true,
95
+ ran: true,
96
+ ...parseVerdict(result, findings),
97
+ schema: REPORT_SCHEMAS.umlParse,
98
+ action: 'parse',
99
+ notation: result.notation,
100
+ kind: result.diagram,
101
+ model: result.model,
102
+ labels: result.labels,
103
+ findings,
104
+ discardedConstructs: result.discardedConstructs,
105
+ discardedEdges: result.discardedEdges,
106
+ hashes: { hashSpec: DEFAULT_HASH_SPEC, modelHash: modelHash(result.model), diagram: createHash('sha256').update(args.diagram).digest('hex') },
107
+ warnings: result.warnings,
108
+ } as unknown as JsonValue
109
+ }
110
+ // R9: the label contract was documented in the library and in `explainLabels()`, but no
111
+ // tool exposed it, so it was reachable only by reading the source or the docs.
112
+ if (args.action === 'explain-labels') {
113
+ const notation = (args.notation === undefined || args.notation === 'auto' ? 'mermaid' : args.notation) as UmlNotation
114
+ return {
115
+ ok: true,
116
+ ran: true,
117
+ ...verdictOf(0, 0),
118
+ schema: REPORT_SCHEMAS.umlExplainLabels,
119
+ action: 'explain-labels',
120
+ ...explainLabels(notation),
121
+ } as unknown as JsonValue
88
122
  }
89
123
  const report = reviewUml({
90
124
  model: args.model,
@@ -96,11 +130,20 @@ export const logicProbeUmlTool = defineTool({
96
130
  })
97
131
  return report as unknown as JsonValue
98
132
  } catch (error) {
99
- return errorResult(error instanceof Error ? error.message : String(error), error instanceof UmlError) as unknown as JsonValue
133
+ const message = error instanceof Error ? error.message : String(error)
134
+ return errorResult(message, error instanceof UmlError ? error.code : undefined) as unknown as JsonValue
100
135
  }
101
136
  },
102
137
  })
103
138
 
104
- function errorResult(message: string, isUmlError = false): JsonValue {
105
- return { ok: false, ...(isUmlError ? { errorCode: 'UML_INPUT' } : {}), error: message } as unknown as JsonValue
139
+ /** The parse verdict counts parser notes as findings: an ignored line is information the model does not carry. */
140
+ function parseVerdict(result: UmlParseResult, findings: UmlFinding[]): VerdictSummary {
141
+ const errors = findings.filter((finding) => finding.severity === 'error').length
142
+ const warnings = findings.filter((finding) => finding.severity === 'warning').length + result.warnings.length
143
+ const firstCode = findings.find((finding) => finding.severity === 'error')?.code ?? findings.find((finding) => finding.severity === 'warning')?.code
144
+ return verdictOf(errors, warnings, firstCode)
145
+ }
146
+
147
+ function errorResult(message: string, errorCode = 'UML_INPUT'): JsonValue {
148
+ return { ok: false, ran: false, ...refusalVerdict(message), errorCode, error: message } as unknown as JsonValue
106
149
  }