structured-context 0.12.0 → 0.13.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 (94) hide show
  1. package/README.md +29 -216
  2. package/dist/api.d.ts +17 -17
  3. package/dist/api.js +12 -12
  4. package/dist/commands/docs.js +1 -0
  5. package/dist/commands/providers.d.ts +4 -0
  6. package/dist/commands/providers.js +52 -0
  7. package/dist/commands/schemas.js +21 -2
  8. package/dist/commands/validate-file.d.ts +3 -0
  9. package/dist/commands/validate-file.js +17 -2
  10. package/dist/commands/validate.d.ts +4 -0
  11. package/dist/commands/validate.js +42 -3
  12. package/dist/config.d.ts +7 -0
  13. package/dist/config.js +26 -3
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +59 -18
  16. package/dist/judge/context.d.ts +20 -0
  17. package/dist/judge/context.js +105 -0
  18. package/dist/judge/factory.d.ts +44 -0
  19. package/dist/judge/factory.js +148 -0
  20. package/dist/judge/index.d.ts +13 -0
  21. package/dist/judge/index.js +8 -0
  22. package/dist/judge/judge-node.d.ts +27 -0
  23. package/dist/judge/judge-node.js +122 -0
  24. package/dist/judge/prompt.d.ts +10 -0
  25. package/dist/judge/prompt.js +92 -0
  26. package/dist/judge/provider/anthropic.d.ts +17 -0
  27. package/dist/judge/provider/anthropic.js +43 -0
  28. package/dist/judge/provider/fake.d.ts +26 -0
  29. package/dist/judge/provider/fake.js +56 -0
  30. package/dist/judge/provider/jev.d.ts +37 -0
  31. package/dist/judge/provider/jev.js +169 -0
  32. package/dist/judge/provider/openai-decisions.d.ts +21 -0
  33. package/dist/judge/provider/openai-decisions.js +84 -0
  34. package/dist/judge/provider/openai.d.ts +20 -0
  35. package/dist/judge/provider/openai.js +53 -0
  36. package/dist/judge/provider/per-rule.d.ts +10 -0
  37. package/dist/judge/provider/per-rule.js +18 -0
  38. package/dist/judge/trim.d.ts +27 -0
  39. package/dist/judge/trim.js +62 -0
  40. package/dist/judge/types.d.ts +92 -0
  41. package/dist/judge/types.js +9 -0
  42. package/dist/judge/verdicts.d.ts +24 -0
  43. package/dist/judge/verdicts.js +82 -0
  44. package/dist/load-space-graph.d.ts +3 -3
  45. package/dist/load-space-graph.js +4 -4
  46. package/dist/plugins/loader.js +1 -1
  47. package/dist/plugins/markdown/parse-embedded.d.ts +2 -0
  48. package/dist/plugins/markdown/parse-embedded.js +161 -24
  49. package/dist/plugins/markdown/read-space.js +4 -9
  50. package/dist/plugins/markdown/render-directory.js +5 -5
  51. package/dist/plugins/markdown/template-sync.js +3 -4
  52. package/dist/plugins/markdown/util.d.ts +26 -5
  53. package/dist/plugins/markdown/util.js +56 -15
  54. package/dist/render/render.d.ts +1 -1
  55. package/dist/render/render.js +1 -1
  56. package/dist/schema/evaluate-rule.d.ts +33 -2
  57. package/dist/schema/evaluate-rule.js +58 -4
  58. package/dist/schema/gating.d.ts +69 -0
  59. package/dist/schema/gating.js +253 -0
  60. package/dist/schema/metadata-contract.d.ts +625 -12
  61. package/dist/schema/metadata-contract.js +110 -4
  62. package/dist/schema/rule-filter.d.ts +12 -0
  63. package/dist/schema/rule-filter.js +46 -0
  64. package/dist/schema/rule-severity.d.ts +13 -0
  65. package/dist/schema/rule-severity.js +33 -0
  66. package/dist/schema/schema.js +31 -1
  67. package/dist/schema/validate-rules.d.ts +17 -8
  68. package/dist/schema/validate-rules.js +91 -48
  69. package/dist/schemas/_knowledge_wiki.json +4 -4
  70. package/dist/schemas/_okf.json +172 -0
  71. package/dist/schemas/_ost_strict.json +15 -6
  72. package/dist/schemas/_sctx_base.json +1 -1
  73. package/dist/schemas/generated/0.13.0/_structured_context_schema_meta.json +400 -0
  74. package/dist/schemas/generated/latest/_structured_context_schema_meta.json +177 -6
  75. package/dist/schemas/okf.json +8 -0
  76. package/dist/space-context.d.ts +2 -2
  77. package/dist/space-context.js +3 -3
  78. package/dist/space-graph.d.ts +1 -1
  79. package/dist/testing.d.ts +1 -1
  80. package/dist/types.d.ts +11 -4
  81. package/dist/validate.d.ts +59 -9
  82. package/dist/validate.js +225 -120
  83. package/docs/concepts.md +52 -6
  84. package/docs/config.md +70 -1
  85. package/docs/rules.md +140 -49
  86. package/docs/schemas.md +43 -18
  87. package/package.json +6 -5
  88. package/schemas/_knowledge_wiki.json +4 -4
  89. package/schemas/_okf.json +172 -0
  90. package/schemas/_ost_strict.json +15 -6
  91. package/schemas/_sctx_base.json +1 -1
  92. package/schemas/generated/0.13.0/_structured_context_schema_meta.json +400 -0
  93. package/schemas/generated/latest/_structured_context_schema_meta.json +177 -6
  94. package/schemas/okf.json +8 -0
@@ -0,0 +1,172 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/latest/_structured_context_schema_meta.json",
3
+ "$id": "sctx://core/_okf",
4
+ // Open Knowledge Format v0.2 frontmatter families: core, provenance, trust, lifecycle, attested computation.
5
+ // OKF §4.1: consumers MUST NOT reject unknown fields, so every object here allows additional properties.
6
+ // Concept types are not registered centrally (§4.1), so there is deliberately no oneOf over `type` consts:
7
+ // type checking would reject every type not listed. `Attested Computation` is handled with if/then instead.
8
+ // index.md and log.md are reserved bundle files without a `type`; they are reported as excluded during parsing.
9
+ "$metadata": {
10
+ "rules": [
11
+ {
12
+ "id": "okf-description",
13
+ "category": "best-practice",
14
+ "severity": "warning",
15
+ "description": "Concepts should have a one-line description (used by index generators and search snippets)",
16
+ "expression": "$exists(current.description) and $length(current.description) > 0"
17
+ },
18
+ {
19
+ "id": "okf-stale-after-generated",
20
+ "category": "validation",
21
+ "description": "stale_after must be later than generated.at",
22
+ "expression": "$not($exists(current.stale_after)) or $not($exists(current.generated.at)) or $toMillis(current.stale_after) > $toMillis(current.generated.at)"
23
+ },
24
+ {
25
+ "id": "okf-usage-count-window",
26
+ "category": "coherence",
27
+ "severity": "warning",
28
+ "description": "A source's usage_count is only meaningful within a usage_window (on the source or shared at the top level)",
29
+ "expression": "$not($exists(current.sources)) or $count(current.sources[$exists(usage_count) and $not($exists(usage_window)) and $not($exists($$.current.usage_window))]) = 0"
30
+ },
31
+ {
32
+ "id": "okf-computation-present",
33
+ "category": "validation",
34
+ "type": "Attested Computation",
35
+ "description": "An Attested Computation needs its computation: a `computation` path or a body `# Computation` section. Known limitation: the section is found by matching a `# Computation` line in the body text, so such a line inside a code fence also counts",
36
+ "expression": "$exists(current.computation) or $contains(current.content, /^# Computation[ \\t]*$/m)"
37
+ }
38
+ ]
39
+ },
40
+ "$defs": {
41
+ "timestamp": {
42
+ "type": "string",
43
+ "format": "date-time",
44
+ "description": "ISO 8601 datetime with an explicit UTC offset, e.g. 2026-06-30T14:00:00Z (§5)"
45
+ },
46
+ "actor": {
47
+ "type": "string",
48
+ "minLength": 1,
49
+ "description": "<producer>/<version> for agents, human:<id> for people, process:<id> for automated processes (§7)"
50
+ },
51
+ "usageWindow": {
52
+ "type": "object",
53
+ "properties": {
54
+ "from": { "$ref": "#/$defs/timestamp" },
55
+ "to": { "$ref": "#/$defs/timestamp" }
56
+ },
57
+ "required": ["from", "to"]
58
+ },
59
+ "source": {
60
+ "type": "object",
61
+ "properties": {
62
+ "resource": {
63
+ "type": "string",
64
+ "minLength": 1,
65
+ "description": "Followable artifact (URL or path) or a scope descriptor"
66
+ },
67
+ "id": { "type": "string", "description": "Stable key for footnote attribution" },
68
+ "title": { "type": "string" },
69
+ "author": { "$ref": "#/$defs/actor" },
70
+ "usage_count": { "type": "integer", "minimum": 0 },
71
+ "last_modified": { "$ref": "#/$defs/timestamp" },
72
+ "usage_window": { "$ref": "#/$defs/usageWindow" }
73
+ },
74
+ "required": ["resource"]
75
+ },
76
+ "generated": {
77
+ "type": "object",
78
+ "properties": {
79
+ "by": { "$ref": "#/$defs/actor" },
80
+ "at": { "$ref": "#/$defs/timestamp" }
81
+ },
82
+ "required": ["by"]
83
+ },
84
+ "verification": {
85
+ "type": "object",
86
+ "properties": {
87
+ "by": { "$ref": "#/$defs/actor" },
88
+ "at": { "$ref": "#/$defs/timestamp" }
89
+ },
90
+ "required": ["by", "at"]
91
+ },
92
+ "okfProps": {
93
+ "type": "object",
94
+ "properties": {
95
+ "type": {
96
+ "type": "string",
97
+ "minLength": 1,
98
+ "description": "Kind of concept; unregistered and open-ended (§4.1)"
99
+ },
100
+ "title": { "type": "string" },
101
+ "description": { "type": "string" },
102
+ "resource": { "type": "string", "minLength": 1, "description": "URI or path of the underlying asset" },
103
+ "tags": { "type": "array", "items": { "type": "string" } },
104
+ "sources": { "type": "array", "items": { "$ref": "#/$defs/source" } },
105
+ "usage_window": { "$ref": "#/$defs/usageWindow" },
106
+ "generated": { "$ref": "#/$defs/generated" },
107
+ "verified": {
108
+ "description": "One verification event, or a list of them (§5.2: a bare mapping is a one-element list)",
109
+ "anyOf": [
110
+ { "$ref": "#/$defs/verification" },
111
+ { "type": "array", "items": { "$ref": "#/$defs/verification" } }
112
+ ]
113
+ },
114
+ "status": { "type": "string", "enum": ["draft", "stable", "deprecated"], "default": "stable" },
115
+ "stale_after": { "$ref": "#/$defs/timestamp" }
116
+ },
117
+ "required": ["type"]
118
+ },
119
+ "concept": {
120
+ "type": "object",
121
+ "allOf": [
122
+ { "$ref": "#/$defs/okfProps" },
123
+ {
124
+ "if": { "properties": { "type": { "const": "Attested Computation" } }, "required": ["type"] },
125
+ "then": { "$ref": "#/$defs/attestedComputation" }
126
+ }
127
+ ],
128
+ "additionalProperties": true
129
+ },
130
+ "attestedComputation": {
131
+ "type": "object",
132
+ "properties": {
133
+ "runtime": {
134
+ "type": "string",
135
+ "minLength": 1,
136
+ "description": "How to run the computation; defines what `parameters` mean (§10.2)"
137
+ },
138
+ "parameters": {
139
+ "type": "array",
140
+ "items": {
141
+ "type": "object",
142
+ "properties": {
143
+ "name": { "type": "string", "minLength": 1 },
144
+ "type": { "type": "string", "minLength": 1 },
145
+ "required": { "type": "boolean" }
146
+ },
147
+ "required": ["name", "type"]
148
+ }
149
+ },
150
+ "computation": {
151
+ "type": "string",
152
+ "minLength": 1,
153
+ "description": "Path to a file holding the computation, instead of the body fence"
154
+ },
155
+ "executor": {
156
+ "type": "object",
157
+ "properties": {
158
+ "resource": { "type": "string", "minLength": 1 },
159
+ "receipt": { "type": "array", "items": { "type": "string" }, "description": "Fields a run must return" }
160
+ },
161
+ "required": ["resource"]
162
+ },
163
+ "attester": {
164
+ "type": "object",
165
+ "properties": { "resource": { "type": "string", "minLength": 1 } },
166
+ "required": ["resource"]
167
+ }
168
+ },
169
+ "required": ["runtime"]
170
+ }
171
+ }
172
+ }
@@ -13,28 +13,37 @@
13
13
  "id": "active-outcome-count",
14
14
  "category": "workflow",
15
15
  "description": "Only one outcome should be active at a time",
16
- "scope": "global",
17
- "check": "$count(nodes[resolvedType='outcome' and status='active']) <= 1"
16
+ "global": true,
17
+ "expression": "$count(nodes[resolvedType='outcome' and status='active']) <= 1"
18
18
  },
19
19
  {
20
20
  "id": "active-opportunity-count",
21
21
  "category": "workflow",
22
22
  "description": "Only one target opportunity should be active at a time",
23
- "scope": "global",
24
- "check": "$count(nodes[resolvedType='opportunity' and status='active']) <= 1"
23
+ "global": true,
24
+ "expression": "$count(nodes[resolvedType='opportunity' and status='active']) <= 1"
25
25
  },
26
26
  {
27
27
  "id": "active-node-parent-active",
28
28
  "category": "workflow",
29
29
  "description": "An active node's parent should also be active",
30
- "check": "current.status != 'active' or $exists(parent) = false or parent.status = 'active'"
30
+ "expression": "current.status != 'active' or $exists(parent) = false or parent.status = 'active'"
31
31
  },
32
32
  {
33
33
  "id": "solution-quantity",
34
34
  "category": "best-practice",
35
35
  "description": "Explore multiple candidate solutions (aim for at least three) for the target opportunity",
36
36
  "type": "opportunity",
37
- "check": "(current.status != 'exploring' and current.status != 'active') or $count(nodes[resolvedParentTitle=$$.current.title and resolvedType='solution']) >= 3"
37
+ "expression": "(current.status != 'exploring' and current.status != 'active') or $count(nodes[resolvedParentTitle=$$.current.title and resolvedType='solution']) >= 3"
38
+ },
39
+ {
40
+ "id": "opportunity-customer-voice",
41
+ "category": "best-practice",
42
+ "type": "opportunity",
43
+ "description": "Opportunity must be framed in the customer's voice (something a customer would actually say) as a need or pain point, not a solution or business goal",
44
+ "semantic": {
45
+ "rubric": "Verify that the opportunity is stated from the customer's perspective as a problem, pain point, or desire. It must not be a solution in disguise (e.g. 'build X feature') or a business metric (e.g. 'increase revenue')."
46
+ }
38
47
  }
39
48
  ]
40
49
  },
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "content": {
11
11
  "type": "string",
12
- "description": "Body content"
12
+ "description": "Markdown source of the node's body, excluding its own heading or list marker and any nested embedded nodes"
13
13
  },
14
14
  "summary": {
15
15
  "type": "string",
@@ -0,0 +1,400 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://raw.githubusercontent.com/mindsocket/structured-context/main/schemas/generated/0.13.0/_structured_context_schema_meta.json",
4
+ "title": "structured-context schema dialect",
5
+ "description": "Extends JSON Schema 2020-12 with top-level $metadata for imports, hierarchy, relationships, aliases and rules.",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "https://json-schema.org/draft/2020-12/schema"
10
+ }
11
+ ],
12
+ "properties": {
13
+ "$metadata": {
14
+ "type": "object",
15
+ "properties": {
16
+ "imports": {
17
+ "type": "array",
18
+ "items": {
19
+ "type": "string",
20
+ "minLength": 1
21
+ }
22
+ },
23
+ "hierarchy": {
24
+ "oneOf": [
25
+ {
26
+ "type": "object",
27
+ "properties": {
28
+ "name": {
29
+ "type": "string",
30
+ "minLength": 1
31
+ },
32
+ "override": {
33
+ "type": "boolean"
34
+ },
35
+ "levels": {
36
+ "type": "array",
37
+ "minItems": 1,
38
+ "items": {
39
+ "oneOf": [
40
+ {
41
+ "type": "string",
42
+ "minLength": 1
43
+ },
44
+ {
45
+ "type": "object",
46
+ "properties": {
47
+ "type": {
48
+ "type": "string",
49
+ "minLength": 1
50
+ },
51
+ "field": {
52
+ "type": "string",
53
+ "minLength": 1
54
+ },
55
+ "fieldOn": {
56
+ "enum": ["child", "parent"]
57
+ },
58
+ "multiple": {
59
+ "type": "boolean"
60
+ },
61
+ "templateFormat": {
62
+ "enum": ["heading", "list", "table", "page"]
63
+ },
64
+ "matchers": {
65
+ "type": "array",
66
+ "items": {
67
+ "type": "string",
68
+ "minLength": 1
69
+ },
70
+ "minItems": 1
71
+ },
72
+ "embeddedTemplateFields": {
73
+ "type": "array",
74
+ "items": {
75
+ "type": "string",
76
+ "minLength": 1
77
+ }
78
+ },
79
+ "selfRef": {
80
+ "type": "boolean"
81
+ },
82
+ "selfRefField": {
83
+ "type": "string",
84
+ "minLength": 1
85
+ }
86
+ },
87
+ "required": ["type"],
88
+ "additionalProperties": false
89
+ }
90
+ ]
91
+ }
92
+ },
93
+ "allowSkipLevels": {
94
+ "type": "boolean"
95
+ }
96
+ },
97
+ "required": ["levels"],
98
+ "additionalProperties": false
99
+ },
100
+ {
101
+ "type": "object",
102
+ "properties": {
103
+ "$ref": {
104
+ "type": "string",
105
+ "minLength": 1
106
+ }
107
+ },
108
+ "required": ["$ref"],
109
+ "additionalProperties": false
110
+ }
111
+ ]
112
+ },
113
+ "relationships": {
114
+ "type": "array",
115
+ "items": {
116
+ "type": "object",
117
+ "properties": {
118
+ "parent": {
119
+ "type": "string",
120
+ "minLength": 1
121
+ },
122
+ "type": {
123
+ "type": "string",
124
+ "minLength": 1
125
+ },
126
+ "field": {
127
+ "type": "string",
128
+ "minLength": 1
129
+ },
130
+ "fieldOn": {
131
+ "enum": ["child", "parent"]
132
+ },
133
+ "multiple": {
134
+ "type": "boolean"
135
+ },
136
+ "templateFormat": {
137
+ "enum": ["heading", "list", "table", "page"]
138
+ },
139
+ "matchers": {
140
+ "type": "array",
141
+ "items": {
142
+ "type": "string",
143
+ "minLength": 1
144
+ },
145
+ "minItems": 1
146
+ },
147
+ "embeddedTemplateFields": {
148
+ "type": "array",
149
+ "items": {
150
+ "type": "string",
151
+ "minLength": 1
152
+ }
153
+ },
154
+ "override": {
155
+ "type": "boolean"
156
+ }
157
+ },
158
+ "required": ["parent", "type"],
159
+ "additionalProperties": false
160
+ }
161
+ },
162
+ "aliases": {
163
+ "type": "object",
164
+ "additionalProperties": {
165
+ "oneOf": [
166
+ {
167
+ "type": "string",
168
+ "minLength": 1
169
+ },
170
+ {
171
+ "type": "object",
172
+ "properties": {
173
+ "type": {
174
+ "type": "string",
175
+ "minLength": 1
176
+ },
177
+ "override": {
178
+ "type": "boolean"
179
+ }
180
+ },
181
+ "required": ["type"],
182
+ "additionalProperties": false
183
+ }
184
+ ]
185
+ }
186
+ },
187
+ "rules": {
188
+ "type": "array",
189
+ "items": {
190
+ "type": "object",
191
+ "properties": {
192
+ "id": {
193
+ "type": "string",
194
+ "minLength": 1
195
+ },
196
+ "category": {
197
+ "enum": ["validation", "coherence", "workflow", "best-practice"]
198
+ },
199
+ "severity": {
200
+ "enum": ["error", "warning", "info"]
201
+ },
202
+ "description": {
203
+ "type": "string",
204
+ "minLength": 1
205
+ },
206
+ "expression": {
207
+ "type": "string",
208
+ "minLength": 1
209
+ },
210
+ "when": {
211
+ "type": "string",
212
+ "minLength": 1
213
+ },
214
+ "semantic": {
215
+ "oneOf": [
216
+ {
217
+ "const": true
218
+ },
219
+ {
220
+ "type": "object",
221
+ "properties": {
222
+ "rubric": {
223
+ "type": "string",
224
+ "minLength": 1
225
+ },
226
+ "verdicts": {
227
+ "type": "object",
228
+ "additionalProperties": {
229
+ "oneOf": [
230
+ {
231
+ "type": "string",
232
+ "minLength": 1
233
+ },
234
+ {
235
+ "type": "object",
236
+ "properties": {
237
+ "description": {
238
+ "type": "string",
239
+ "minLength": 1
240
+ },
241
+ "guidance": {
242
+ "type": "string",
243
+ "minLength": 1
244
+ }
245
+ },
246
+ "required": ["description"],
247
+ "additionalProperties": false
248
+ }
249
+ ]
250
+ },
251
+ "minProperties": 2
252
+ },
253
+ "severity": {
254
+ "type": "object",
255
+ "additionalProperties": {
256
+ "enum": ["error", "warning", "info", "ignore"]
257
+ },
258
+ "minProperties": 1
259
+ },
260
+ "scope": {
261
+ "type": "array",
262
+ "items": {
263
+ "enum": ["parent", "siblings", "children", "ancestors", "descendants", "branch"]
264
+ },
265
+ "uniqueItems": true
266
+ },
267
+ "includeContent": {
268
+ "enum": ["nodeOnly", "fullScope", true]
269
+ }
270
+ },
271
+ "additionalProperties": false
272
+ }
273
+ ]
274
+ },
275
+ "type": {
276
+ "type": "string",
277
+ "minLength": 1
278
+ },
279
+ "global": {
280
+ "type": "boolean"
281
+ },
282
+ "override": {
283
+ "type": "boolean"
284
+ },
285
+ "gatesDescendants": {
286
+ "type": "boolean"
287
+ }
288
+ },
289
+ "required": ["id", "category", "description"],
290
+ "additionalProperties": false,
291
+ "allOf": [
292
+ {
293
+ "anyOf": [
294
+ {
295
+ "required": ["expression"]
296
+ },
297
+ {
298
+ "required": ["semantic"]
299
+ }
300
+ ]
301
+ },
302
+ {
303
+ "if": {
304
+ "required": ["global"],
305
+ "properties": {
306
+ "global": {
307
+ "const": true
308
+ }
309
+ }
310
+ },
311
+ "then": {
312
+ "not": {
313
+ "anyOf": [
314
+ {
315
+ "required": ["when"]
316
+ },
317
+ {
318
+ "required": ["type"]
319
+ },
320
+ {
321
+ "required": ["semantic"]
322
+ }
323
+ ]
324
+ }
325
+ }
326
+ },
327
+ {
328
+ "if": {
329
+ "required": ["semantic"],
330
+ "properties": {
331
+ "semantic": {
332
+ "type": "object",
333
+ "required": ["verdicts"]
334
+ }
335
+ }
336
+ },
337
+ "then": {
338
+ "properties": {
339
+ "semantic": {
340
+ "type": "object",
341
+ "required": ["severity"]
342
+ }
343
+ }
344
+ }
345
+ },
346
+ {
347
+ "if": {
348
+ "required": ["semantic"],
349
+ "properties": {
350
+ "semantic": {
351
+ "type": "object",
352
+ "not": {
353
+ "required": ["verdicts"]
354
+ }
355
+ }
356
+ }
357
+ },
358
+ "then": {
359
+ "properties": {
360
+ "semantic": {
361
+ "type": "object",
362
+ "properties": {
363
+ "severity": {
364
+ "type": "object",
365
+ "not": {
366
+ "required": ["fail"]
367
+ }
368
+ }
369
+ }
370
+ }
371
+ }
372
+ }
373
+ },
374
+ {
375
+ "if": {
376
+ "required": ["semantic"],
377
+ "properties": {
378
+ "semantic": {
379
+ "type": "object",
380
+ "required": ["verdicts"]
381
+ }
382
+ },
383
+ "not": {
384
+ "required": ["expression"]
385
+ }
386
+ },
387
+ "then": {
388
+ "not": {
389
+ "required": ["severity"]
390
+ }
391
+ }
392
+ }
393
+ ]
394
+ }
395
+ }
396
+ },
397
+ "additionalProperties": false
398
+ }
399
+ }
400
+ }