@kanonak-protocol/expression 1.0.0 → 1.2.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.
@@ -0,0 +1,1983 @@
1
+ {
2
+ "expressionRuntimeVersion": "2",
3
+ "description": "Behavior parity vectors for expressionRuntimeVersion 2 — the value-domain extension (number | string | ref | list), the list/aggregate/membership family, polymorphic Equals, kind predicates, lambda binding (Filter/ListMap/ForEach), Matches over the pinned RE2-compatible XSD-regex subset, and NameBasedUuid, the RFC 9562 version 5 UUID of a name (1.2.0). Runs IN ADDITION to expression-vectors.json (v1), every vector of which must pass unchanged under a v2 kernel — that file is the numeric-regression gate. `expected` and `env` values are Values: JSON numbers, strings, arrays (lists), and {\"ref\": \"publisher/package/name\"} objects (refs). Normative list-family semantics are the SDK reference engine's: Sum on empty = 0; Min/Max/Average on empty = loud error; Count type-agnostic; absence is the empty list; predicates fail closed on type-confused operands, computations fail loud.",
4
+ "vectors": [
5
+ {
6
+ "id": "string-literal",
7
+ "expr": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "hello" },
8
+ "expected": "hello"
9
+ },
10
+ {
11
+ "id": "uri-literal-as-value",
12
+ "description": "UriLiteral is a kernel-known literal in v2: it evaluates to its ref identity",
13
+ "expr": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/red" },
14
+ "expected": { "ref": "example.org/colors/red" }
15
+ },
16
+ {
17
+ "id": "var-ref-binds-list",
18
+ "description": "The caller's resolve may return any Value — here a list",
19
+ "expr": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
20
+ "env": { "xs": [1, 2, 3] },
21
+ "expected": [1, 2, 3]
22
+ },
23
+ {
24
+ "id": "count-basic",
25
+ "expr": {
26
+ "type": "kanonak.org/transformations/Count",
27
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
28
+ },
29
+ "env": { "xs": [1, 2, 3] },
30
+ "expected": 3
31
+ },
32
+ {
33
+ "id": "count-empty-is-zero",
34
+ "description": "Absence is the empty list; Count of absence is 0",
35
+ "expr": {
36
+ "type": "kanonak.org/transformations/Count",
37
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
38
+ },
39
+ "env": { "xs": [] },
40
+ "expected": 0
41
+ },
42
+ {
43
+ "id": "count-is-type-agnostic",
44
+ "expr": {
45
+ "type": "kanonak.org/transformations/Count",
46
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
47
+ },
48
+ "env": { "xs": [1, "a", { "ref": "example.org/p/x" }] },
49
+ "expected": 3
50
+ },
51
+ {
52
+ "id": "count-scalar-promotes",
53
+ "description": "A scalar source promotes to a one-element list (the reference-engine rule)",
54
+ "expr": {
55
+ "type": "kanonak.org/transformations/Count",
56
+ "source": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 5 }
57
+ },
58
+ "expected": 1
59
+ },
60
+ {
61
+ "id": "sum-basic",
62
+ "expr": {
63
+ "type": "kanonak.org/transformations/Sum",
64
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
65
+ },
66
+ "env": { "xs": [1, 2.5, 3] },
67
+ "expected": 6.5
68
+ },
69
+ {
70
+ "id": "sum-empty-is-zero",
71
+ "expr": {
72
+ "type": "kanonak.org/transformations/Sum",
73
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
74
+ },
75
+ "env": { "xs": [] },
76
+ "expected": 0
77
+ },
78
+ {
79
+ "id": "sum-non-numeric-errors",
80
+ "expr": {
81
+ "type": "kanonak.org/transformations/Sum",
82
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
83
+ },
84
+ "env": { "xs": [1, "two"] },
85
+ "expectError": true
86
+ },
87
+ {
88
+ "id": "min-basic",
89
+ "expr": {
90
+ "type": "kanonak.org/transformations/Min",
91
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
92
+ },
93
+ "env": { "xs": [3, 1, 2] },
94
+ "expected": 1
95
+ },
96
+ {
97
+ "id": "max-basic",
98
+ "expr": {
99
+ "type": "kanonak.org/transformations/Max",
100
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
101
+ },
102
+ "env": { "xs": [3, 1, 2] },
103
+ "expected": 3
104
+ },
105
+ {
106
+ "id": "average-basic",
107
+ "expr": {
108
+ "type": "kanonak.org/transformations/Average",
109
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
110
+ },
111
+ "env": { "xs": [1, 2, 3, 4] },
112
+ "expected": 2.5
113
+ },
114
+ {
115
+ "id": "min-empty-errors",
116
+ "expr": {
117
+ "type": "kanonak.org/transformations/Min",
118
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
119
+ },
120
+ "env": { "xs": [] },
121
+ "expectError": true
122
+ },
123
+ {
124
+ "id": "average-empty-errors",
125
+ "expr": {
126
+ "type": "kanonak.org/transformations/Average",
127
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
128
+ },
129
+ "env": { "xs": [] },
130
+ "expectError": true
131
+ },
132
+ {
133
+ "id": "max-non-numeric-errors",
134
+ "expr": {
135
+ "type": "kanonak.org/transformations/Max",
136
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
137
+ },
138
+ "env": { "xs": ["a"] },
139
+ "expectError": true
140
+ },
141
+ {
142
+ "id": "join-basic",
143
+ "expr": {
144
+ "type": "kanonak.org/transformations/Join",
145
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
146
+ "separator": ", "
147
+ },
148
+ "env": { "xs": ["a", "b", "c"] },
149
+ "expected": "a, b, c"
150
+ },
151
+ {
152
+ "id": "join-empty-is-empty-string",
153
+ "expr": {
154
+ "type": "kanonak.org/transformations/Join",
155
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
156
+ "separator": ","
157
+ },
158
+ "env": { "xs": [] },
159
+ "expected": ""
160
+ },
161
+ {
162
+ "id": "join-numbers-ecmascript-tostring",
163
+ "description": "Numbers stringify per the ECMAScript rule RFC 8785 pins (1 not 1.0; 2.5 stays 2.5)",
164
+ "expr": {
165
+ "type": "kanonak.org/transformations/Join",
166
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
167
+ "separator": "-"
168
+ },
169
+ "env": { "xs": [1, 2.5, 100] },
170
+ "expected": "1-2.5-100"
171
+ },
172
+ {
173
+ "id": "join-refs-use-local-name",
174
+ "expr": {
175
+ "type": "kanonak.org/transformations/Join",
176
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
177
+ "separator": "+"
178
+ },
179
+ "env": { "xs": [{ "ref": "example.org/colors/red" }, { "ref": "example.org/colors/blue" }] },
180
+ "expected": "red+blue"
181
+ },
182
+ {
183
+ "id": "join-nested-list-errors",
184
+ "expr": {
185
+ "type": "kanonak.org/transformations/Join",
186
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
187
+ "separator": ","
188
+ },
189
+ "env": { "xs": ["a", ["b", "c"]] },
190
+ "expectError": true
191
+ },
192
+ {
193
+ "id": "contains-string-hit",
194
+ "expr": {
195
+ "type": "kanonak.org/transformations/Contains",
196
+ "haystack": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
197
+ "needle": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "b" }
198
+ },
199
+ "env": { "xs": ["a", "b", "c"] },
200
+ "expected": 1
201
+ },
202
+ {
203
+ "id": "contains-miss",
204
+ "expr": {
205
+ "type": "kanonak.org/transformations/Contains",
206
+ "haystack": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
207
+ "needle": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "z" }
208
+ },
209
+ "env": { "xs": ["a", "b", "c"] },
210
+ "expected": 0
211
+ },
212
+ {
213
+ "id": "contains-ref-identity",
214
+ "description": "Refs compare by canonical versionless URI identity — the tx.Equals rule",
215
+ "expr": {
216
+ "type": "kanonak.org/transformations/Contains",
217
+ "haystack": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
218
+ "needle": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/blue" }
219
+ },
220
+ "env": { "xs": [{ "ref": "example.org/colors/red" }, { "ref": "example.org/colors/blue" }] },
221
+ "expected": 1
222
+ },
223
+ {
224
+ "id": "contains-cross-kind-is-false",
225
+ "description": "The string \"1\" is not the number 1 — cross-kind equality is false, never an error",
226
+ "expr": {
227
+ "type": "kanonak.org/transformations/Contains",
228
+ "haystack": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
229
+ "needle": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "1" }
230
+ },
231
+ "env": { "xs": [1, 2, 3] },
232
+ "expected": 0
233
+ },
234
+ {
235
+ "id": "contains-empty-haystack",
236
+ "expr": {
237
+ "type": "kanonak.org/transformations/Contains",
238
+ "haystack": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
239
+ "needle": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 1 }
240
+ },
241
+ "env": { "xs": [] },
242
+ "expected": 0
243
+ },
244
+ {
245
+ "id": "equals-strings",
246
+ "expr": {
247
+ "type": "kanonak.org/transformations/Equals",
248
+ "compareLeft": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc" },
249
+ "compareRight": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc" }
250
+ },
251
+ "expected": 1
252
+ },
253
+ {
254
+ "id": "equals-strings-differ",
255
+ "expr": {
256
+ "type": "kanonak.org/transformations/Equals",
257
+ "compareLeft": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc" },
258
+ "compareRight": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abd" }
259
+ },
260
+ "expected": 0
261
+ },
262
+ {
263
+ "id": "equals-cross-kind-string-number",
264
+ "expr": {
265
+ "type": "kanonak.org/transformations/Equals",
266
+ "compareLeft": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "1" },
267
+ "compareRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 1 }
268
+ },
269
+ "expected": 0
270
+ },
271
+ {
272
+ "id": "equals-refs-same-identity",
273
+ "expr": {
274
+ "type": "kanonak.org/transformations/Equals",
275
+ "compareLeft": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/red" },
276
+ "compareRight": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/red" }
277
+ },
278
+ "expected": 1
279
+ },
280
+ {
281
+ "id": "equals-refs-differ",
282
+ "expr": {
283
+ "type": "kanonak.org/transformations/Equals",
284
+ "compareLeft": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/red" },
285
+ "compareRight": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/blue" }
286
+ },
287
+ "expected": 0
288
+ },
289
+ {
290
+ "id": "equals-ref-vs-string-is-false",
291
+ "description": "A URI is never equal to the string spelling of it",
292
+ "expr": {
293
+ "type": "kanonak.org/transformations/Equals",
294
+ "compareLeft": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/colors/red" },
295
+ "compareRight": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "example.org/colors/red" }
296
+ },
297
+ "expected": 0
298
+ },
299
+ {
300
+ "id": "equals-lists-never-equal",
301
+ "description": "A list is a container, not a comparable value",
302
+ "expr": {
303
+ "type": "kanonak.org/transformations/Equals",
304
+ "compareLeft": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
305
+ "compareRight": { "type": "kanonak.org/transformations/VarRef", "varName": "ys" }
306
+ },
307
+ "env": { "xs": [1, 2], "ys": [1, 2] },
308
+ "expected": 0
309
+ },
310
+ {
311
+ "id": "isset-empty-list-is-unset",
312
+ "expr": {
313
+ "type": "kanonak.org/transformations/IsSet",
314
+ "checkExpr": { "type": "kanonak.org/transformations/VarRef", "varName": "x" }
315
+ },
316
+ "env": { "x": [] },
317
+ "expected": 0
318
+ },
319
+ {
320
+ "id": "isset-empty-string-is-unset",
321
+ "expr": {
322
+ "type": "kanonak.org/transformations/IsSet",
323
+ "checkExpr": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "" }
324
+ },
325
+ "expected": 0
326
+ },
327
+ {
328
+ "id": "isset-zero-is-set",
329
+ "description": "0 is a value, not absence",
330
+ "expr": {
331
+ "type": "kanonak.org/transformations/IsSet",
332
+ "checkExpr": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 0 }
333
+ },
334
+ "expected": 1
335
+ },
336
+ {
337
+ "id": "isset-nonempty-list-is-set",
338
+ "expr": {
339
+ "type": "kanonak.org/transformations/IsSet",
340
+ "checkExpr": { "type": "kanonak.org/transformations/VarRef", "varName": "x" }
341
+ },
342
+ "env": { "x": ["a"] },
343
+ "expected": 1
344
+ },
345
+ {
346
+ "id": "kind-predicates-hit",
347
+ "description": "IsString/IsNumber/IsReference/IsList each recognize their own kind",
348
+ "expr": {
349
+ "type": "kanonak.org/transformations/And",
350
+ "operands": [
351
+ { "type": "kanonak.org/transformations/IsString", "kindCheck": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "s" } },
352
+ { "type": "kanonak.org/transformations/IsNumber", "kindCheck": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 7 } },
353
+ { "type": "kanonak.org/transformations/IsReference", "kindCheck": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/p/x" } },
354
+ { "type": "kanonak.org/transformations/IsList", "kindCheck": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" } }
355
+ ]
356
+ },
357
+ "env": { "xs": [1] },
358
+ "expected": 1
359
+ },
360
+ {
361
+ "id": "kind-predicates-miss",
362
+ "description": "No kind predicate accepts a foreign kind",
363
+ "expr": {
364
+ "type": "kanonak.org/transformations/Or",
365
+ "operands": [
366
+ { "type": "kanonak.org/transformations/IsString", "kindCheck": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 7 } },
367
+ { "type": "kanonak.org/transformations/IsNumber", "kindCheck": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "7" } },
368
+ { "type": "kanonak.org/transformations/IsReference", "kindCheck": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" } },
369
+ { "type": "kanonak.org/transformations/IsList", "kindCheck": { "type": "kanonak.org/transformations/UriLiteral", "refTo": "example.org/p/x" } }
370
+ ]
371
+ },
372
+ "env": { "xs": [1] },
373
+ "expected": 0
374
+ },
375
+ {
376
+ "id": "filter-basic",
377
+ "expr": {
378
+ "type": "kanonak.org/transformations/Filter",
379
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
380
+ "loopVar": "x",
381
+ "predicate": {
382
+ "type": "kanonak.org/transformations/GreaterThan",
383
+ "compareLeft": { "type": "kanonak.org/transformations/VarRef", "varName": "x" },
384
+ "compareRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 2 }
385
+ }
386
+ },
387
+ "env": { "xs": [1, 2, 3, 4] },
388
+ "expected": [3, 4]
389
+ },
390
+ {
391
+ "id": "list-map-basic",
392
+ "expr": {
393
+ "type": "kanonak.org/transformations/ListMap",
394
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
395
+ "loopVar": "x",
396
+ "mapBody": {
397
+ "type": "kanonak.org/transformations/Add",
398
+ "arithLeft": { "type": "kanonak.org/transformations/VarRef", "varName": "x" },
399
+ "arithRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 1 }
400
+ }
401
+ },
402
+ "env": { "xs": [1, 2, 3] },
403
+ "expected": [2, 3, 4]
404
+ },
405
+ {
406
+ "id": "for-each-flattens-one-level",
407
+ "description": "A body list flattens; an empty body list contributes nothing (absence = skip)",
408
+ "expr": {
409
+ "type": "kanonak.org/transformations/ForEach",
410
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
411
+ "loopVar": "x",
412
+ "emit": { "type": "kanonak.org/transformations/VarRef", "varName": "x" }
413
+ },
414
+ "env": { "xs": [[1, 2], [], [3]] },
415
+ "expected": [1, 2, 3]
416
+ },
417
+ {
418
+ "id": "lambda-shadowing-innermost-wins",
419
+ "expr": {
420
+ "type": "kanonak.org/transformations/ListMap",
421
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
422
+ "loopVar": "x",
423
+ "mapBody": {
424
+ "type": "kanonak.org/transformations/Sum",
425
+ "source": {
426
+ "type": "kanonak.org/transformations/ListMap",
427
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "ys" },
428
+ "loopVar": "x",
429
+ "mapBody": { "type": "kanonak.org/transformations/VarRef", "varName": "x" }
430
+ }
431
+ }
432
+ },
433
+ "env": { "xs": [100, 200], "ys": [1, 2] },
434
+ "expected": [3, 3]
435
+ },
436
+ {
437
+ "id": "lambda-free-variable-falls-through",
438
+ "description": "A VarRef naming anything but an enclosing loopVar is still the caller's leaf",
439
+ "expr": {
440
+ "type": "kanonak.org/transformations/ListMap",
441
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
442
+ "loopVar": "x",
443
+ "mapBody": {
444
+ "type": "kanonak.org/transformations/Multiply",
445
+ "arithLeft": { "type": "kanonak.org/transformations/VarRef", "varName": "x" },
446
+ "arithRight": { "type": "kanonak.org/transformations/VarRef", "varName": "scale" }
447
+ }
448
+ },
449
+ "env": { "xs": [1, 2], "scale": 10 },
450
+ "expected": [10, 20]
451
+ },
452
+ {
453
+ "id": "reverse-basic",
454
+ "expr": {
455
+ "type": "kanonak.org/transformations/Reverse",
456
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
457
+ },
458
+ "env": { "xs": [1, 2, 3] },
459
+ "expected": [3, 2, 1]
460
+ },
461
+ {
462
+ "id": "list-item-at-basic",
463
+ "expr": {
464
+ "type": "kanonak.org/transformations/ListItemAt",
465
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
466
+ "itemIndex": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 1 }
467
+ },
468
+ "env": { "xs": [10, 20, 30] },
469
+ "expected": 20
470
+ },
471
+ {
472
+ "id": "list-item-at-past-end-is-absence",
473
+ "expr": {
474
+ "type": "kanonak.org/transformations/ListItemAt",
475
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
476
+ "itemIndex": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 9 }
477
+ },
478
+ "env": { "xs": [10, 20] },
479
+ "expected": []
480
+ },
481
+ {
482
+ "id": "list-item-at-negative-errors",
483
+ "expr": {
484
+ "type": "kanonak.org/transformations/ListItemAt",
485
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" },
486
+ "itemIndex": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": -1 }
487
+ },
488
+ "env": { "xs": [10, 20] },
489
+ "expectError": true
490
+ },
491
+ {
492
+ "id": "matches-anchored-hit",
493
+ "expr": {
494
+ "type": "kanonak.org/transformations/Matches",
495
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "E-1002" },
496
+ "pattern": "^E-\\d+$"
497
+ },
498
+ "expected": 1
499
+ },
500
+ {
501
+ "id": "matches-unanchored-substring",
502
+ "description": "fn:matches semantics — unanchored; authors write ^…$ for whole-string",
503
+ "expr": {
504
+ "type": "kanonak.org/transformations/Matches",
505
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc123def" },
506
+ "pattern": "\\d+"
507
+ },
508
+ "expected": 1
509
+ },
510
+ {
511
+ "id": "matches-miss",
512
+ "expr": {
513
+ "type": "kanonak.org/transformations/Matches",
514
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "hello" },
515
+ "pattern": "^\\d+$"
516
+ },
517
+ "expected": 0
518
+ },
519
+ {
520
+ "id": "matches-prefix-case-fold",
521
+ "description": "Flag prefix (?i) at position 0 — the only flag mechanism in the subset",
522
+ "expr": {
523
+ "type": "kanonak.org/transformations/Matches",
524
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "ABC" },
525
+ "pattern": "(?i)abc"
526
+ },
527
+ "expected": 1
528
+ },
529
+ {
530
+ "id": "matches-prefix-multiple-distinct-flags",
531
+ "description": "All three prefix flags together — distinct flags in one prefix are in subset",
532
+ "expr": {
533
+ "type": "kanonak.org/transformations/Matches",
534
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "ABC" },
535
+ "pattern": "(?ims)^abc$"
536
+ },
537
+ "expected": 1
538
+ },
539
+ {
540
+ "id": "matches-repeated-flag-in-prefix-outside-subset",
541
+ "description": "A repeated prefix flag is out of subset — the JS engine rejects (?ii) at compile time while the Go, Python and Rust engines accept it, so the subset decides rather than the host",
542
+ "expr": {
543
+ "type": "kanonak.org/transformations/Matches",
544
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc" },
545
+ "pattern": "(?ii)abc"
546
+ },
547
+ "expectError": true
548
+ },
549
+ {
550
+ "id": "matches-second-flag-prefix-outside-subset",
551
+ "description": "Only ONE whole-pattern prefix — a second (?s) is a mid-pattern group construct",
552
+ "expr": {
553
+ "type": "kanonak.org/transformations/Matches",
554
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc" },
555
+ "pattern": "(?i)(?s)abc"
556
+ },
557
+ "expectError": true
558
+ },
559
+ {
560
+ "id": "matches-mid-pattern-flag-group-outside-subset",
561
+ "description": "Mid-pattern flag groups are not portable (JS/Python engines) — out of subset",
562
+ "expr": {
563
+ "type": "kanonak.org/transformations/Matches",
564
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "ABC" },
565
+ "pattern": "x(?i:abc)"
566
+ },
567
+ "expectError": true
568
+ },
569
+ {
570
+ "id": "matches-class-range-and-quantifier",
571
+ "expr": {
572
+ "type": "kanonak.org/transformations/Matches",
573
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "kanonak-2" },
574
+ "pattern": "^[a-z][a-z0-9-]{2,15}$"
575
+ },
576
+ "expected": 1
577
+ },
578
+ {
579
+ "id": "matches-lookahead-outside-subset",
580
+ "expr": {
581
+ "type": "kanonak.org/transformations/Matches",
582
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abc" },
583
+ "pattern": "a(?=b)"
584
+ },
585
+ "expectError": true
586
+ },
587
+ {
588
+ "id": "matches-backreference-outside-subset",
589
+ "expr": {
590
+ "type": "kanonak.org/transformations/Matches",
591
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "abab" },
592
+ "pattern": "(ab)\\1"
593
+ },
594
+ "expectError": true
595
+ },
596
+ {
597
+ "id": "matches-unicode-property-outside-subset",
598
+ "expr": {
599
+ "type": "kanonak.org/transformations/Matches",
600
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a" },
601
+ "pattern": "\\p{L}"
602
+ },
603
+ "expectError": true
604
+ },
605
+ {
606
+ "id": "matches-posix-class-outside-subset",
607
+ "expr": {
608
+ "type": "kanonak.org/transformations/Matches",
609
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a" },
610
+ "pattern": "[[:alpha:]]"
611
+ },
612
+ "expectError": true
613
+ },
614
+ {
615
+ "id": "matches-lookbehind-outside-subset",
616
+ "expr": {
617
+ "type": "kanonak.org/transformations/Matches",
618
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "ab" },
619
+ "pattern": "(?<=a)b"
620
+ },
621
+ "expectError": true
622
+ },
623
+ {
624
+ "id": "matches-astral-length-counts-code-points",
625
+ "description": "PINNED counting unit: an astral character (surrogate pair in UTF-16 hosts) is ONE code point — 'a🧬b' is exactly 3 under .{3}. A port counting code units sees 4 and must fail this vector, not the tenant.",
626
+ "expr": {
627
+ "type": "kanonak.org/transformations/Matches",
628
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a🧬b" },
629
+ "pattern": "(?s)^.{3}$"
630
+ },
631
+ "expected": 1
632
+ },
633
+ {
634
+ "id": "matches-astral-single-dot",
635
+ "description": "One astral character matches a single `.`",
636
+ "expr": {
637
+ "type": "kanonak.org/transformations/Matches",
638
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "🧬" },
639
+ "pattern": "^.$"
640
+ },
641
+ "expected": 1
642
+ },
643
+ {
644
+ "id": "matches-astral-min-length-lowering",
645
+ "description": "The minLength lowering shape over astral input: 3 code points satisfies minLength 3 in every port",
646
+ "expr": {
647
+ "type": "kanonak.org/transformations/Matches",
648
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a🧬b" },
649
+ "pattern": "(?s)^.{4,}$"
650
+ },
651
+ "expected": 0
652
+ },
653
+ {
654
+ "id": "matches-bare-literal-brace-outside-subset",
655
+ "description": "A bare `{` that is not a quantifier is engine-divergent (lenient-literal in some, error in others) — the subset requires escaping it",
656
+ "expr": {
657
+ "type": "kanonak.org/transformations/Matches",
658
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a{b" },
659
+ "pattern": "a{b"
660
+ },
661
+ "expectError": true
662
+ },
663
+ {
664
+ "id": "matches-digit-shorthand-is-ascii",
665
+ "description": "PINNED: \\d is [0-9] by textual expansion — Arabic-Indic digits do not match (Rust/Python native \\d would say yes; every port must expand)",
666
+ "expr": {
667
+ "type": "kanonak.org/transformations/Matches",
668
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "١٢٣" },
669
+ "pattern": "^\\d+$"
670
+ },
671
+ "expected": 0
672
+ },
673
+ {
674
+ "id": "matches-space-shorthand-is-ascii",
675
+ "description": "PINNED: \\s is ASCII whitespace by textual expansion — NBSP does not match (native JS \\s would say yes; every port must expand)",
676
+ "expr": {
677
+ "type": "kanonak.org/transformations/Matches",
678
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "\u00a0" },
679
+ "pattern": "^\\s$"
680
+ },
681
+ "expected": 0
682
+ },
683
+ {
684
+ "id": "matches-word-boundary-is-ascii",
685
+ "description": "PINNED: \\b is the ASCII word boundary — é is not a word character, so \\bé finds no boundary (Unicode \\b engines would match; Rust uses (?-u:\\b), Python re.ASCII)",
686
+ "expr": {
687
+ "type": "kanonak.org/transformations/Matches",
688
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a é b" },
689
+ "pattern": "\\bé"
690
+ },
691
+ "expected": 0
692
+ },
693
+ {
694
+ "id": "matches-shorthand-in-class-expands",
695
+ "description": "In-class shorthand splices as a fragment: [\\d-] is [0-9-]",
696
+ "expr": {
697
+ "type": "kanonak.org/transformations/Matches",
698
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "12-34" },
699
+ "pattern": "^[\\d-]+$"
700
+ },
701
+ "expected": 1
702
+ },
703
+ {
704
+ "id": "matches-negated-shorthand-in-class-outside-subset",
705
+ "expr": {
706
+ "type": "kanonak.org/transformations/Matches",
707
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a" },
708
+ "pattern": "[\\D]"
709
+ },
710
+ "expectError": true
711
+ },
712
+ {
713
+ "id": "matches-class-b-escape-outside-subset",
714
+ "description": "In-class \\b is backspace in JS but an error in RE2-family engines — rejected",
715
+ "expr": {
716
+ "type": "kanonak.org/transformations/Matches",
717
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "a" },
718
+ "pattern": "[\\b]"
719
+ },
720
+ "expectError": true
721
+ },
722
+ {
723
+ "id": "matches-dot-matches-carriage-return",
724
+ "description": "PINNED: non-dotAll `.` is any code point except \\n — it MATCHES \\r (the RE2/Python/Go norm; JS and Java engines exclude more line terminators natively and must translate `.` to [^\\n])",
725
+ "expr": {
726
+ "type": "kanonak.org/transformations/Matches",
727
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "\r" },
728
+ "pattern": "^.$"
729
+ },
730
+ "expected": 1
731
+ },
732
+ {
733
+ "id": "matches-vertical-tab-escape",
734
+ "description": "PINNED: \\v is the vertical tab U+000B (Java's native \\v is a vertical-whitespace CLASS and must translate to \\x0B)",
735
+ "expr": {
736
+ "type": "kanonak.org/transformations/Matches",
737
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "\u000b" },
738
+ "pattern": "^\\v$"
739
+ },
740
+ "expected": 1
741
+ },
742
+ {
743
+ "id": "matches-case-fold-is-unicode",
744
+ "description": "PINNED: (?i) is Unicode simple case folding — é folds to É (Java adds UNICODE_CASE; C# uses CultureInvariant; Python must NOT pass re.ASCII, which disables it)",
745
+ "expr": {
746
+ "type": "kanonak.org/transformations/Matches",
747
+ "matchSource": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "É" },
748
+ "pattern": "(?i)é"
749
+ },
750
+ "expected": 1
751
+ },
752
+ {
753
+ "id": "matches-non-string-source-errors",
754
+ "expr": {
755
+ "type": "kanonak.org/transformations/Matches",
756
+ "matchSource": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 5 },
757
+ "pattern": "\\d"
758
+ },
759
+ "expectError": true
760
+ },
761
+ {
762
+ "id": "order-comparison-fails-closed-on-string",
763
+ "description": "Ordering comparisons are predicates: non-numeric operands yield 0, never an error",
764
+ "expr": {
765
+ "type": "kanonak.org/transformations/GreaterThan",
766
+ "compareLeft": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "10" },
767
+ "compareRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 5 }
768
+ },
769
+ "expected": 0
770
+ },
771
+ {
772
+ "id": "arithmetic-fails-loud-on-string",
773
+ "description": "Computations fail loud: Add of a string is a type error, never a coercion",
774
+ "expr": {
775
+ "type": "kanonak.org/transformations/Add",
776
+ "arithLeft": { "type": "kanonak.org/transformations/StringLiteral", "stringLiteral": "1" },
777
+ "arithRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 2 }
778
+ },
779
+ "expectError": true
780
+ },
781
+ {
782
+ "id": "shacl-min-count-shape",
783
+ "description": "The sh:minCount lowering shape: Count(values) >= 1",
784
+ "expr": {
785
+ "type": "kanonak.org/transformations/GreaterThanOrEqual",
786
+ "compareLeft": {
787
+ "type": "kanonak.org/transformations/Count",
788
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "values" }
789
+ },
790
+ "compareRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 1 }
791
+ },
792
+ "env": { "values": [] },
793
+ "expected": 0
794
+ },
795
+ {
796
+ "id": "shacl-in-shape",
797
+ "description": "The sh:in lowering shape: every value a member of the closed set (Filter misses = 0)",
798
+ "expr": {
799
+ "type": "kanonak.org/transformations/Equals",
800
+ "compareLeft": {
801
+ "type": "kanonak.org/transformations/Count",
802
+ "source": {
803
+ "type": "kanonak.org/transformations/Filter",
804
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "values" },
805
+ "loopVar": "v",
806
+ "predicate": {
807
+ "type": "kanonak.org/transformations/Not",
808
+ "operand": {
809
+ "type": "kanonak.org/transformations/Contains",
810
+ "haystack": { "type": "kanonak.org/transformations/VarRef", "varName": "allowed" },
811
+ "needle": { "type": "kanonak.org/transformations/VarRef", "varName": "v" }
812
+ }
813
+ }
814
+ }
815
+ },
816
+ "compareRight": { "type": "kanonak.org/transformations/IntegerLiteral", "integerLiteral": 0 }
817
+ },
818
+ "env": { "values": ["draft", "active"], "allowed": ["draft", "active", "done"] },
819
+ "expected": 1
820
+ },
821
+ {
822
+ "id": "count-trace",
823
+ "description": "explain() over a list operator: children = the source trace",
824
+ "expr": {
825
+ "type": "kanonak.org/transformations/Count",
826
+ "source": { "type": "kanonak.org/transformations/VarRef", "varName": "xs" }
827
+ },
828
+ "env": { "xs": ["a", "b"] },
829
+ "expected": 2,
830
+ "trace": {
831
+ "type": "kanonak.org/transformations/Count",
832
+ "value": 2,
833
+ "children": [
834
+ { "type": "kanonak.org/transformations/VarRef", "value": ["a", "b"], "children": [] }
835
+ ]
836
+ }
837
+ },
838
+ {
839
+ "id": "filter-caller-leaf-sees-loop-binding",
840
+ "description": "runtime#25: a caller-resolved leaf inside a Filter body reads the loop element. The caller's PropertyRead evaluates its readSource through the handed-back evaluate, which MUST carry the enclosing loopVar frame — the binding is the kernel's, the read is the caller's",
841
+ "expr": {
842
+ "type": "kanonak.org/transformations/Count",
843
+ "source": {
844
+ "type": "kanonak.org/transformations/Filter",
845
+ "source": {
846
+ "type": "kanonak.org/transformations/VarRef",
847
+ "varName": "items"
848
+ },
849
+ "loopVar": "it",
850
+ "predicate": {
851
+ "type": "kanonak.org/transformations/Equals",
852
+ "compareLeft": {
853
+ "type": "kanonak.org/transformations/PropertyRead",
854
+ "readSource": {
855
+ "type": "kanonak.org/transformations/VarRef",
856
+ "varName": "it"
857
+ },
858
+ "readProp": "size"
859
+ },
860
+ "compareRight": {
861
+ "type": "kanonak.org/transformations/IntegerLiteral",
862
+ "integerLiteral": 20
863
+ }
864
+ }
865
+ }
866
+ },
867
+ "env": {
868
+ "items": [
869
+ {
870
+ "ref": "example.org/inv/item-1"
871
+ },
872
+ {
873
+ "ref": "example.org/inv/item-2"
874
+ }
875
+ ]
876
+ },
877
+ "graph": {
878
+ "example.org/inv/item-1": {
879
+ "size": 10,
880
+ "tags": [
881
+ "a",
882
+ "b"
883
+ ]
884
+ },
885
+ "example.org/inv/item-2": {
886
+ "size": 20,
887
+ "tags": "c",
888
+ "owner": {
889
+ "ref": "example.org/inv/ann"
890
+ }
891
+ }
892
+ },
893
+ "expected": 1
894
+ },
895
+ {
896
+ "id": "list-map-caller-leaf-reads-each-element",
897
+ "description": "ListMap body = a caller leaf over the loop element; the element bound by loopVar is whatever the caller put in the list — for a host graph, a ref — so readSource evaluating to a ref is the shape every port must handle. The trace shows the caller leaf as a leaf with its resolved value",
898
+ "expr": {
899
+ "type": "kanonak.org/transformations/ListMap",
900
+ "source": {
901
+ "type": "kanonak.org/transformations/VarRef",
902
+ "varName": "items"
903
+ },
904
+ "loopVar": "it",
905
+ "mapBody": {
906
+ "type": "kanonak.org/transformations/PropertyRead",
907
+ "readSource": {
908
+ "type": "kanonak.org/transformations/VarRef",
909
+ "varName": "it"
910
+ },
911
+ "readProp": "size"
912
+ }
913
+ },
914
+ "env": {
915
+ "items": [
916
+ {
917
+ "ref": "example.org/inv/item-1"
918
+ },
919
+ {
920
+ "ref": "example.org/inv/item-2"
921
+ }
922
+ ]
923
+ },
924
+ "graph": {
925
+ "example.org/inv/item-1": {
926
+ "size": 10,
927
+ "tags": [
928
+ "a",
929
+ "b"
930
+ ]
931
+ },
932
+ "example.org/inv/item-2": {
933
+ "size": 20,
934
+ "tags": "c",
935
+ "owner": {
936
+ "ref": "example.org/inv/ann"
937
+ }
938
+ }
939
+ },
940
+ "expected": [
941
+ 10,
942
+ 20
943
+ ],
944
+ "trace": {
945
+ "type": "kanonak.org/transformations/ListMap",
946
+ "value": [
947
+ 10,
948
+ 20
949
+ ],
950
+ "children": [
951
+ {
952
+ "type": "kanonak.org/transformations/VarRef",
953
+ "value": [
954
+ {
955
+ "ref": "example.org/inv/item-1"
956
+ },
957
+ {
958
+ "ref": "example.org/inv/item-2"
959
+ }
960
+ ],
961
+ "children": []
962
+ },
963
+ {
964
+ "type": "kanonak.org/transformations/PropertyRead",
965
+ "value": 10,
966
+ "children": []
967
+ },
968
+ {
969
+ "type": "kanonak.org/transformations/PropertyRead",
970
+ "value": 20,
971
+ "children": []
972
+ }
973
+ ]
974
+ }
975
+ },
976
+ {
977
+ "id": "for-each-caller-leaf-flattens-multi-and-single",
978
+ "description": "ForEach over a caller leaf: a multi-valued read contributes each value, a single-valued read contributes itself",
979
+ "expr": {
980
+ "type": "kanonak.org/transformations/ForEach",
981
+ "source": {
982
+ "type": "kanonak.org/transformations/VarRef",
983
+ "varName": "items"
984
+ },
985
+ "loopVar": "it",
986
+ "emit": {
987
+ "type": "kanonak.org/transformations/PropertyRead",
988
+ "readSource": {
989
+ "type": "kanonak.org/transformations/VarRef",
990
+ "varName": "it"
991
+ },
992
+ "readProp": "tags"
993
+ }
994
+ },
995
+ "env": {
996
+ "items": [
997
+ {
998
+ "ref": "example.org/inv/item-1"
999
+ },
1000
+ {
1001
+ "ref": "example.org/inv/item-2"
1002
+ }
1003
+ ]
1004
+ },
1005
+ "graph": {
1006
+ "example.org/inv/item-1": {
1007
+ "size": 10,
1008
+ "tags": [
1009
+ "a",
1010
+ "b"
1011
+ ]
1012
+ },
1013
+ "example.org/inv/item-2": {
1014
+ "size": 20,
1015
+ "tags": "c",
1016
+ "owner": {
1017
+ "ref": "example.org/inv/ann"
1018
+ }
1019
+ }
1020
+ },
1021
+ "expected": [
1022
+ "a",
1023
+ "b",
1024
+ "c"
1025
+ ]
1026
+ },
1027
+ {
1028
+ "id": "property-read-multiple-values-is-a-list-in-loop-body",
1029
+ "description": "The reference engine's convention, pinned INSIDE a loop body so every port's caller leaf agrees: a property with several values reads as a list",
1030
+ "expr": {
1031
+ "type": "kanonak.org/transformations/Filter",
1032
+ "source": {
1033
+ "type": "kanonak.org/transformations/VarRef",
1034
+ "varName": "items"
1035
+ },
1036
+ "loopVar": "it",
1037
+ "predicate": {
1038
+ "type": "kanonak.org/transformations/Equals",
1039
+ "compareLeft": {
1040
+ "type": "kanonak.org/transformations/Count",
1041
+ "source": {
1042
+ "type": "kanonak.org/transformations/PropertyRead",
1043
+ "readSource": {
1044
+ "type": "kanonak.org/transformations/VarRef",
1045
+ "varName": "it"
1046
+ },
1047
+ "readProp": "tags"
1048
+ }
1049
+ },
1050
+ "compareRight": {
1051
+ "type": "kanonak.org/transformations/IntegerLiteral",
1052
+ "integerLiteral": 2
1053
+ }
1054
+ }
1055
+ },
1056
+ "env": {
1057
+ "items": [
1058
+ {
1059
+ "ref": "example.org/inv/item-1"
1060
+ },
1061
+ {
1062
+ "ref": "example.org/inv/item-2"
1063
+ }
1064
+ ]
1065
+ },
1066
+ "graph": {
1067
+ "example.org/inv/item-1": {
1068
+ "size": 10,
1069
+ "tags": [
1070
+ "a",
1071
+ "b"
1072
+ ]
1073
+ },
1074
+ "example.org/inv/item-2": {
1075
+ "size": 20,
1076
+ "tags": "c",
1077
+ "owner": {
1078
+ "ref": "example.org/inv/ann"
1079
+ }
1080
+ }
1081
+ },
1082
+ "expected": [
1083
+ {
1084
+ "ref": "example.org/inv/item-1"
1085
+ }
1086
+ ]
1087
+ },
1088
+ {
1089
+ "id": "property-read-single-value-is-itself-in-loop-body",
1090
+ "description": "A property with one value reads as that value, NOT a one-element list — Equals against the scalar holds (a port wrapping singles in a list fails cross-kind here)",
1091
+ "expr": {
1092
+ "type": "kanonak.org/transformations/Filter",
1093
+ "source": {
1094
+ "type": "kanonak.org/transformations/VarRef",
1095
+ "varName": "items"
1096
+ },
1097
+ "loopVar": "it",
1098
+ "predicate": {
1099
+ "type": "kanonak.org/transformations/Equals",
1100
+ "compareLeft": {
1101
+ "type": "kanonak.org/transformations/PropertyRead",
1102
+ "readSource": {
1103
+ "type": "kanonak.org/transformations/VarRef",
1104
+ "varName": "it"
1105
+ },
1106
+ "readProp": "tags"
1107
+ },
1108
+ "compareRight": {
1109
+ "type": "kanonak.org/transformations/StringLiteral",
1110
+ "stringLiteral": "c"
1111
+ }
1112
+ }
1113
+ },
1114
+ "env": {
1115
+ "items": [
1116
+ {
1117
+ "ref": "example.org/inv/item-1"
1118
+ },
1119
+ {
1120
+ "ref": "example.org/inv/item-2"
1121
+ }
1122
+ ]
1123
+ },
1124
+ "graph": {
1125
+ "example.org/inv/item-1": {
1126
+ "size": 10,
1127
+ "tags": [
1128
+ "a",
1129
+ "b"
1130
+ ]
1131
+ },
1132
+ "example.org/inv/item-2": {
1133
+ "size": 20,
1134
+ "tags": "c",
1135
+ "owner": {
1136
+ "ref": "example.org/inv/ann"
1137
+ }
1138
+ }
1139
+ },
1140
+ "expected": [
1141
+ {
1142
+ "ref": "example.org/inv/item-2"
1143
+ }
1144
+ ]
1145
+ },
1146
+ {
1147
+ "id": "property-read-absent-is-empty-list-in-loop-body",
1148
+ "description": "A property with no value reads as the empty list (absence), never an error and never null: Count is 0 for the element without an owner",
1149
+ "expr": {
1150
+ "type": "kanonak.org/transformations/ListMap",
1151
+ "source": {
1152
+ "type": "kanonak.org/transformations/VarRef",
1153
+ "varName": "items"
1154
+ },
1155
+ "loopVar": "it",
1156
+ "mapBody": {
1157
+ "type": "kanonak.org/transformations/Count",
1158
+ "source": {
1159
+ "type": "kanonak.org/transformations/PropertyRead",
1160
+ "readSource": {
1161
+ "type": "kanonak.org/transformations/VarRef",
1162
+ "varName": "it"
1163
+ },
1164
+ "readProp": "owner"
1165
+ }
1166
+ }
1167
+ },
1168
+ "env": {
1169
+ "items": [
1170
+ {
1171
+ "ref": "example.org/inv/item-1"
1172
+ },
1173
+ {
1174
+ "ref": "example.org/inv/item-2"
1175
+ }
1176
+ ]
1177
+ },
1178
+ "graph": {
1179
+ "example.org/inv/item-1": {
1180
+ "size": 10,
1181
+ "tags": [
1182
+ "a",
1183
+ "b"
1184
+ ]
1185
+ },
1186
+ "example.org/inv/item-2": {
1187
+ "size": 20,
1188
+ "tags": "c",
1189
+ "owner": {
1190
+ "ref": "example.org/inv/ann"
1191
+ }
1192
+ }
1193
+ },
1194
+ "expected": [
1195
+ 0,
1196
+ 1
1197
+ ]
1198
+ },
1199
+ {
1200
+ "id": "is-set-guards-absent-property-in-loop-body",
1201
+ "description": "IsSet over a caller leaf distinguishes the element that has the property",
1202
+ "expr": {
1203
+ "type": "kanonak.org/transformations/Filter",
1204
+ "source": {
1205
+ "type": "kanonak.org/transformations/VarRef",
1206
+ "varName": "items"
1207
+ },
1208
+ "loopVar": "it",
1209
+ "predicate": {
1210
+ "type": "kanonak.org/transformations/IsSet",
1211
+ "checkExpr": {
1212
+ "type": "kanonak.org/transformations/PropertyRead",
1213
+ "readSource": {
1214
+ "type": "kanonak.org/transformations/VarRef",
1215
+ "varName": "it"
1216
+ },
1217
+ "readProp": "owner"
1218
+ }
1219
+ }
1220
+ },
1221
+ "env": {
1222
+ "items": [
1223
+ {
1224
+ "ref": "example.org/inv/item-1"
1225
+ },
1226
+ {
1227
+ "ref": "example.org/inv/item-2"
1228
+ }
1229
+ ]
1230
+ },
1231
+ "graph": {
1232
+ "example.org/inv/item-1": {
1233
+ "size": 10,
1234
+ "tags": [
1235
+ "a",
1236
+ "b"
1237
+ ]
1238
+ },
1239
+ "example.org/inv/item-2": {
1240
+ "size": 20,
1241
+ "tags": "c",
1242
+ "owner": {
1243
+ "ref": "example.org/inv/ann"
1244
+ }
1245
+ }
1246
+ },
1247
+ "expected": [
1248
+ {
1249
+ "ref": "example.org/inv/item-2"
1250
+ }
1251
+ ]
1252
+ },
1253
+ {
1254
+ "id": "inner-loop-var-shadows-outer-through-caller-leaf",
1255
+ "description": "Nested iterators binding the SAME loopVar name: the innermost binder wins, and that precedence reaches a caller leaf too. Were the outer 'it' visible, the inner count would be [0, 2]",
1256
+ "expr": {
1257
+ "type": "kanonak.org/transformations/ListMap",
1258
+ "source": {
1259
+ "type": "kanonak.org/transformations/VarRef",
1260
+ "varName": "items"
1261
+ },
1262
+ "loopVar": "it",
1263
+ "mapBody": {
1264
+ "type": "kanonak.org/transformations/Count",
1265
+ "source": {
1266
+ "type": "kanonak.org/transformations/Filter",
1267
+ "source": {
1268
+ "type": "kanonak.org/transformations/VarRef",
1269
+ "varName": "items"
1270
+ },
1271
+ "loopVar": "it",
1272
+ "predicate": {
1273
+ "type": "kanonak.org/transformations/Equals",
1274
+ "compareLeft": {
1275
+ "type": "kanonak.org/transformations/PropertyRead",
1276
+ "readSource": {
1277
+ "type": "kanonak.org/transformations/VarRef",
1278
+ "varName": "it"
1279
+ },
1280
+ "readProp": "size"
1281
+ },
1282
+ "compareRight": {
1283
+ "type": "kanonak.org/transformations/IntegerLiteral",
1284
+ "integerLiteral": 20
1285
+ }
1286
+ }
1287
+ }
1288
+ }
1289
+ },
1290
+ "env": {
1291
+ "items": [
1292
+ {
1293
+ "ref": "example.org/inv/item-1"
1294
+ },
1295
+ {
1296
+ "ref": "example.org/inv/item-2"
1297
+ }
1298
+ ]
1299
+ },
1300
+ "graph": {
1301
+ "example.org/inv/item-1": {
1302
+ "size": 10,
1303
+ "tags": [
1304
+ "a",
1305
+ "b"
1306
+ ]
1307
+ },
1308
+ "example.org/inv/item-2": {
1309
+ "size": 20,
1310
+ "tags": "c",
1311
+ "owner": {
1312
+ "ref": "example.org/inv/ann"
1313
+ }
1314
+ }
1315
+ },
1316
+ "expected": [
1317
+ 1,
1318
+ 1
1319
+ ]
1320
+ },
1321
+ {
1322
+ "id": "loop-var-shadows-caller-binding-inside-body-only",
1323
+ "description": "A loopVar whose name collides with a name the CALLER binds ('it' is in env as item-1): inside the body the kernel's binding wins — silently, which is why a generator should refuse the collision where it is visible — while outside the body the caller's binding is untouched. size(env.it)=10 + count-of-size-20-elements=1",
1324
+ "expr": {
1325
+ "type": "kanonak.org/transformations/Add",
1326
+ "arithLeft": {
1327
+ "type": "kanonak.org/transformations/PropertyRead",
1328
+ "readSource": {
1329
+ "type": "kanonak.org/transformations/VarRef",
1330
+ "varName": "it"
1331
+ },
1332
+ "readProp": "size"
1333
+ },
1334
+ "arithRight": {
1335
+ "type": "kanonak.org/transformations/Count",
1336
+ "source": {
1337
+ "type": "kanonak.org/transformations/Filter",
1338
+ "source": {
1339
+ "type": "kanonak.org/transformations/VarRef",
1340
+ "varName": "items"
1341
+ },
1342
+ "loopVar": "it",
1343
+ "predicate": {
1344
+ "type": "kanonak.org/transformations/Equals",
1345
+ "compareLeft": {
1346
+ "type": "kanonak.org/transformations/PropertyRead",
1347
+ "readSource": {
1348
+ "type": "kanonak.org/transformations/VarRef",
1349
+ "varName": "it"
1350
+ },
1351
+ "readProp": "size"
1352
+ },
1353
+ "compareRight": {
1354
+ "type": "kanonak.org/transformations/IntegerLiteral",
1355
+ "integerLiteral": 20
1356
+ }
1357
+ }
1358
+ }
1359
+ }
1360
+ },
1361
+ "env": {
1362
+ "items": [
1363
+ {
1364
+ "ref": "example.org/inv/item-1"
1365
+ },
1366
+ {
1367
+ "ref": "example.org/inv/item-2"
1368
+ }
1369
+ ],
1370
+ "it": {
1371
+ "ref": "example.org/inv/item-1"
1372
+ }
1373
+ },
1374
+ "graph": {
1375
+ "example.org/inv/item-1": {
1376
+ "size": 10,
1377
+ "tags": [
1378
+ "a",
1379
+ "b"
1380
+ ]
1381
+ },
1382
+ "example.org/inv/item-2": {
1383
+ "size": 20,
1384
+ "tags": "c",
1385
+ "owner": {
1386
+ "ref": "example.org/inv/ann"
1387
+ }
1388
+ }
1389
+ },
1390
+ "expected": 11
1391
+ },
1392
+ {
1393
+ "id": "filter-trace-pairs-body-k-with-source-element-k",
1394
+ "description": "runtime#26: the trace↔expression contract for an IteratingExpression. children[0] is the source trace; body trace k pairs with element k of the source value — EVERY element, including the one the predicate rejects (value 0): the trace is truthful about what ran, so \"which element failed\" is positional. What was read (readProp) is on the paired expression node, not repeated here",
1395
+ "expr": {
1396
+ "type": "kanonak.org/transformations/Count",
1397
+ "source": {
1398
+ "type": "kanonak.org/transformations/Filter",
1399
+ "source": {
1400
+ "type": "kanonak.org/transformations/VarRef",
1401
+ "varName": "items"
1402
+ },
1403
+ "loopVar": "it",
1404
+ "predicate": {
1405
+ "type": "kanonak.org/transformations/Equals",
1406
+ "compareLeft": {
1407
+ "type": "kanonak.org/transformations/PropertyRead",
1408
+ "readSource": {
1409
+ "type": "kanonak.org/transformations/VarRef",
1410
+ "varName": "it"
1411
+ },
1412
+ "readProp": "size"
1413
+ },
1414
+ "compareRight": {
1415
+ "type": "kanonak.org/transformations/IntegerLiteral",
1416
+ "integerLiteral": 20
1417
+ }
1418
+ }
1419
+ }
1420
+ },
1421
+ "env": {
1422
+ "items": [
1423
+ {
1424
+ "ref": "example.org/inv/item-1"
1425
+ },
1426
+ {
1427
+ "ref": "example.org/inv/item-2"
1428
+ }
1429
+ ]
1430
+ },
1431
+ "graph": {
1432
+ "example.org/inv/item-1": {
1433
+ "size": 10,
1434
+ "tags": [
1435
+ "a",
1436
+ "b"
1437
+ ]
1438
+ },
1439
+ "example.org/inv/item-2": {
1440
+ "size": 20,
1441
+ "tags": "c",
1442
+ "owner": {
1443
+ "ref": "example.org/inv/ann"
1444
+ }
1445
+ }
1446
+ },
1447
+ "expected": 1,
1448
+ "trace": {
1449
+ "type": "kanonak.org/transformations/Count",
1450
+ "value": 1,
1451
+ "children": [
1452
+ {
1453
+ "type": "kanonak.org/transformations/Filter",
1454
+ "value": [
1455
+ {
1456
+ "ref": "example.org/inv/item-2"
1457
+ }
1458
+ ],
1459
+ "children": [
1460
+ {
1461
+ "type": "kanonak.org/transformations/VarRef",
1462
+ "value": [
1463
+ {
1464
+ "ref": "example.org/inv/item-1"
1465
+ },
1466
+ {
1467
+ "ref": "example.org/inv/item-2"
1468
+ }
1469
+ ],
1470
+ "children": []
1471
+ },
1472
+ {
1473
+ "type": "kanonak.org/transformations/Equals",
1474
+ "value": 0,
1475
+ "children": [
1476
+ {
1477
+ "type": "kanonak.org/transformations/PropertyRead",
1478
+ "value": 10,
1479
+ "children": []
1480
+ },
1481
+ {
1482
+ "type": "kanonak.org/transformations/IntegerLiteral",
1483
+ "value": 20,
1484
+ "children": []
1485
+ }
1486
+ ]
1487
+ },
1488
+ {
1489
+ "type": "kanonak.org/transformations/Equals",
1490
+ "value": 1,
1491
+ "children": [
1492
+ {
1493
+ "type": "kanonak.org/transformations/PropertyRead",
1494
+ "value": 20,
1495
+ "children": []
1496
+ },
1497
+ {
1498
+ "type": "kanonak.org/transformations/IntegerLiteral",
1499
+ "value": 20,
1500
+ "children": []
1501
+ }
1502
+ ]
1503
+ }
1504
+ ]
1505
+ }
1506
+ ]
1507
+ }
1508
+ },
1509
+ {
1510
+ "id": "filter-trace-scalar-source-is-coerced-to-a-list",
1511
+ "description": "The coercion hole in the pairing rule: a source that resolves to ONE value (the caller convention: one → itself) is iterated as a one-element list, but the source trace carries the SCALAR. So the rule is \"element k of the source value coerced to a list\" — a consumer indexing children[0].value directly indexes a scalar. Pinned here so no port and no consumer gets it wrong",
1512
+ "expr": {
1513
+ "type": "kanonak.org/transformations/Filter",
1514
+ "source": {
1515
+ "type": "kanonak.org/transformations/PropertyRead",
1516
+ "readSource": {
1517
+ "type": "kanonak.org/transformations/VarRef",
1518
+ "varName": "x"
1519
+ },
1520
+ "readProp": "tags"
1521
+ },
1522
+ "loopVar": "t",
1523
+ "predicate": {
1524
+ "type": "kanonak.org/transformations/Equals",
1525
+ "compareLeft": {
1526
+ "type": "kanonak.org/transformations/VarRef",
1527
+ "varName": "t"
1528
+ },
1529
+ "compareRight": {
1530
+ "type": "kanonak.org/transformations/StringLiteral",
1531
+ "stringLiteral": "c"
1532
+ }
1533
+ }
1534
+ },
1535
+ "env": {
1536
+ "x": {
1537
+ "ref": "example.org/inv/item-2"
1538
+ }
1539
+ },
1540
+ "graph": {
1541
+ "example.org/inv/item-1": {
1542
+ "size": 10,
1543
+ "tags": [
1544
+ "a",
1545
+ "b"
1546
+ ]
1547
+ },
1548
+ "example.org/inv/item-2": {
1549
+ "size": 20,
1550
+ "tags": "c",
1551
+ "owner": {
1552
+ "ref": "example.org/inv/ann"
1553
+ }
1554
+ }
1555
+ },
1556
+ "expected": [
1557
+ "c"
1558
+ ],
1559
+ "trace": {
1560
+ "type": "kanonak.org/transformations/Filter",
1561
+ "value": [
1562
+ "c"
1563
+ ],
1564
+ "children": [
1565
+ {
1566
+ "type": "kanonak.org/transformations/PropertyRead",
1567
+ "value": "c",
1568
+ "children": []
1569
+ },
1570
+ {
1571
+ "type": "kanonak.org/transformations/Equals",
1572
+ "value": 1,
1573
+ "children": [
1574
+ {
1575
+ "type": "kanonak.org/transformations/VarRef",
1576
+ "value": "c",
1577
+ "children": []
1578
+ },
1579
+ {
1580
+ "type": "kanonak.org/transformations/StringLiteral",
1581
+ "value": "c",
1582
+ "children": []
1583
+ }
1584
+ ]
1585
+ }
1586
+ ]
1587
+ }
1588
+ },
1589
+ {
1590
+ "id": "property-read-of-an-inherited-name-reads-only-the-node",
1591
+ "description": "runtime#61: a property named after a member every object inherits, such as valueOf, as transformations declares, reads only what the node holds. A node without it reads as absent, the empty list, never the inherited member",
1592
+ "expr": {
1593
+ "type": "kanonak.org/transformations/ForEach",
1594
+ "source": {
1595
+ "type": "kanonak.org/transformations/VarRef",
1596
+ "varName": "items"
1597
+ },
1598
+ "loopVar": "it",
1599
+ "emit": {
1600
+ "type": "kanonak.org/transformations/PropertyRead",
1601
+ "readSource": {
1602
+ "type": "kanonak.org/transformations/VarRef",
1603
+ "varName": "it"
1604
+ },
1605
+ "readProp": "valueOf"
1606
+ }
1607
+ },
1608
+ "env": {
1609
+ "items": [
1610
+ {
1611
+ "ref": "example.org/inv/item-1"
1612
+ },
1613
+ {
1614
+ "ref": "example.org/inv/item-2"
1615
+ }
1616
+ ]
1617
+ },
1618
+ "graph": {
1619
+ "example.org/inv/item-1": {
1620
+ "size": 10
1621
+ },
1622
+ "example.org/inv/item-2": {
1623
+ "size": 20,
1624
+ "valueOf": "own value"
1625
+ }
1626
+ },
1627
+ "expected": [
1628
+ "own value"
1629
+ ]
1630
+ },
1631
+ {
1632
+ "id": "name-based-uuid-dns-namespace",
1633
+ "description": "RFC 9562 version 5 of www.example.com under the RFC's DNS namespace, as Python's uuid.uuid5 gives it; uuidFormat absent is canonical text",
1634
+ "expr": {
1635
+ "type": "kanonak.org/transformations/NameBasedUuid",
1636
+ "uuidNamespace": {
1637
+ "type": "kanonak.org/transformations/StringLiteral",
1638
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1639
+ },
1640
+ "uuidName": {
1641
+ "type": "kanonak.org/transformations/StringLiteral",
1642
+ "stringLiteral": "www.example.com"
1643
+ }
1644
+ },
1645
+ "expected": "2ed6657d-e927-568b-95e1-2665a8aea6a2"
1646
+ },
1647
+ {
1648
+ "id": "name-based-uuid-url-namespace-versionless-uri",
1649
+ "description": "A versionless URI under the RFC's URL namespace: the identifier of a declaration derived from its name",
1650
+ "expr": {
1651
+ "type": "kanonak.org/transformations/NameBasedUuid",
1652
+ "uuidNamespace": {
1653
+ "type": "kanonak.org/transformations/StringLiteral",
1654
+ "stringLiteral": "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
1655
+ },
1656
+ "uuidName": {
1657
+ "type": "kanonak.org/transformations/StringLiteral",
1658
+ "stringLiteral": "example.com/example-package/example-subject"
1659
+ }
1660
+ },
1661
+ "expected": "be8ed0ff-6592-5bdf-a80d-6e7001609a48"
1662
+ },
1663
+ {
1664
+ "id": "name-based-uuid-canonical-uuid-named",
1665
+ "description": "uuidFormat canonical-uuid, named, gives what its absence gives",
1666
+ "expr": {
1667
+ "type": "kanonak.org/transformations/NameBasedUuid",
1668
+ "uuidNamespace": {
1669
+ "type": "kanonak.org/transformations/StringLiteral",
1670
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1671
+ },
1672
+ "uuidName": {
1673
+ "type": "kanonak.org/transformations/StringLiteral",
1674
+ "stringLiteral": "www.example.com"
1675
+ },
1676
+ "uuidFormat": "kanonak.org/transformations/canonical-uuid"
1677
+ },
1678
+ "expected": "2ed6657d-e927-568b-95e1-2665a8aea6a2"
1679
+ },
1680
+ {
1681
+ "id": "name-based-uuid-hex-uuid",
1682
+ "description": "uuidFormat hex-uuid: the same 32 lower-case hex digits, without hyphens",
1683
+ "expr": {
1684
+ "type": "kanonak.org/transformations/NameBasedUuid",
1685
+ "uuidNamespace": {
1686
+ "type": "kanonak.org/transformations/StringLiteral",
1687
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1688
+ },
1689
+ "uuidName": {
1690
+ "type": "kanonak.org/transformations/StringLiteral",
1691
+ "stringLiteral": "www.example.com"
1692
+ },
1693
+ "uuidFormat": "kanonak.org/transformations/hex-uuid"
1694
+ },
1695
+ "expected": "2ed6657de927568b95e12665a8aea6a2"
1696
+ },
1697
+ {
1698
+ "id": "name-based-uuid-upper-case-namespace",
1699
+ "description": "A namespace's hex digits are read in either case (RFC 9562, section 4), and the result is always lower-case",
1700
+ "expr": {
1701
+ "type": "kanonak.org/transformations/NameBasedUuid",
1702
+ "uuidNamespace": {
1703
+ "type": "kanonak.org/transformations/StringLiteral",
1704
+ "stringLiteral": "6BA7B810-9DAD-11D1-80B4-00C04FD430C8"
1705
+ },
1706
+ "uuidName": {
1707
+ "type": "kanonak.org/transformations/StringLiteral",
1708
+ "stringLiteral": "www.example.com"
1709
+ }
1710
+ },
1711
+ "expected": "2ed6657d-e927-568b-95e1-2665a8aea6a2"
1712
+ },
1713
+ {
1714
+ "id": "name-based-uuid-utf8-name",
1715
+ "description": "The name is hashed as its UTF-8 bytes: two-byte, three-byte and an astral four-byte character, never UTF-16 code units",
1716
+ "expr": {
1717
+ "type": "kanonak.org/transformations/NameBasedUuid",
1718
+ "uuidNamespace": {
1719
+ "type": "kanonak.org/transformations/StringLiteral",
1720
+ "stringLiteral": "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
1721
+ },
1722
+ "uuidName": {
1723
+ "type": "kanonak.org/transformations/StringLiteral",
1724
+ "stringLiteral": "café/日本/😀"
1725
+ }
1726
+ },
1727
+ "expected": "ec2bae54-7e46-5219-b4ea-0a44921487e8"
1728
+ },
1729
+ {
1730
+ "id": "name-based-uuid-empty-name",
1731
+ "description": "An empty name hashes the namespace's 16 bytes alone",
1732
+ "expr": {
1733
+ "type": "kanonak.org/transformations/NameBasedUuid",
1734
+ "uuidNamespace": {
1735
+ "type": "kanonak.org/transformations/StringLiteral",
1736
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1737
+ },
1738
+ "uuidName": {
1739
+ "type": "kanonak.org/transformations/StringLiteral",
1740
+ "stringLiteral": ""
1741
+ }
1742
+ },
1743
+ "expected": "4ebd0208-8328-5d69-8c44-ec50939c0967"
1744
+ },
1745
+ {
1746
+ "id": "name-based-uuid-bound-name",
1747
+ "description": "Both operands are expressions: the name here is a bound variable",
1748
+ "expr": {
1749
+ "type": "kanonak.org/transformations/NameBasedUuid",
1750
+ "uuidNamespace": {
1751
+ "type": "kanonak.org/transformations/StringLiteral",
1752
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1753
+ },
1754
+ "uuidName": {
1755
+ "type": "kanonak.org/transformations/VarRef",
1756
+ "varName": "name"
1757
+ }
1758
+ },
1759
+ "env": {
1760
+ "name": "www.example.com"
1761
+ },
1762
+ "expected": "2ed6657d-e927-568b-95e1-2665a8aea6a2"
1763
+ },
1764
+ {
1765
+ "id": "name-based-uuid-namespace-not-a-uuid",
1766
+ "description": "A namespace that isn't a UUID is an error, never a value",
1767
+ "expr": {
1768
+ "type": "kanonak.org/transformations/NameBasedUuid",
1769
+ "uuidNamespace": {
1770
+ "type": "kanonak.org/transformations/StringLiteral",
1771
+ "stringLiteral": "not-a-uuid"
1772
+ },
1773
+ "uuidName": {
1774
+ "type": "kanonak.org/transformations/StringLiteral",
1775
+ "stringLiteral": "www.example.com"
1776
+ }
1777
+ },
1778
+ "expectError": true
1779
+ },
1780
+ {
1781
+ "id": "name-based-uuid-namespace-without-hyphens",
1782
+ "description": "Only canonical text is a namespace: the 32 digits without hyphens are refused, though some UUID parsers accept them",
1783
+ "expr": {
1784
+ "type": "kanonak.org/transformations/NameBasedUuid",
1785
+ "uuidNamespace": {
1786
+ "type": "kanonak.org/transformations/StringLiteral",
1787
+ "stringLiteral": "6ba7b8109dad11d180b400c04fd430c8"
1788
+ },
1789
+ "uuidName": {
1790
+ "type": "kanonak.org/transformations/StringLiteral",
1791
+ "stringLiteral": "www.example.com"
1792
+ }
1793
+ },
1794
+ "expectError": true
1795
+ },
1796
+ {
1797
+ "id": "name-based-uuid-namespace-braced",
1798
+ "description": "A braced namespace is refused, though some UUID parsers accept it",
1799
+ "expr": {
1800
+ "type": "kanonak.org/transformations/NameBasedUuid",
1801
+ "uuidNamespace": {
1802
+ "type": "kanonak.org/transformations/StringLiteral",
1803
+ "stringLiteral": "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"
1804
+ },
1805
+ "uuidName": {
1806
+ "type": "kanonak.org/transformations/StringLiteral",
1807
+ "stringLiteral": "www.example.com"
1808
+ }
1809
+ },
1810
+ "expectError": true
1811
+ },
1812
+ {
1813
+ "id": "name-based-uuid-namespace-urn",
1814
+ "description": "A urn:uuid: namespace is refused, though some UUID parsers accept it",
1815
+ "expr": {
1816
+ "type": "kanonak.org/transformations/NameBasedUuid",
1817
+ "uuidNamespace": {
1818
+ "type": "kanonak.org/transformations/StringLiteral",
1819
+ "stringLiteral": "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1820
+ },
1821
+ "uuidName": {
1822
+ "type": "kanonak.org/transformations/StringLiteral",
1823
+ "stringLiteral": "www.example.com"
1824
+ }
1825
+ },
1826
+ "expectError": true
1827
+ },
1828
+ {
1829
+ "id": "name-based-uuid-namespace-trailing-newline",
1830
+ "description": "A namespace with a trailing newline is refused: a pattern whose $ matches before a final newline, as Python's, Java's and .NET's do, would accept it",
1831
+ "expr": {
1832
+ "type": "kanonak.org/transformations/NameBasedUuid",
1833
+ "uuidNamespace": {
1834
+ "type": "kanonak.org/transformations/StringLiteral",
1835
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8\n"
1836
+ },
1837
+ "uuidName": {
1838
+ "type": "kanonak.org/transformations/StringLiteral",
1839
+ "stringLiteral": "www.example.com"
1840
+ }
1841
+ },
1842
+ "expectError": true
1843
+ },
1844
+ {
1845
+ "id": "name-based-uuid-namespace-fullwidth-digit",
1846
+ "description": "Hex digits are ASCII only: a fullwidth digit, which a Unicode-aware digit class or number parse reads as 8, is refused",
1847
+ "expr": {
1848
+ "type": "kanonak.org/transformations/NameBasedUuid",
1849
+ "uuidNamespace": {
1850
+ "type": "kanonak.org/transformations/StringLiteral",
1851
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1852
+ },
1853
+ "uuidName": {
1854
+ "type": "kanonak.org/transformations/StringLiteral",
1855
+ "stringLiteral": "www.example.com"
1856
+ }
1857
+ },
1858
+ "expectError": true
1859
+ },
1860
+ {
1861
+ "id": "name-based-uuid-namespace-number",
1862
+ "description": "A namespace that isn't a string is an error",
1863
+ "expr": {
1864
+ "type": "kanonak.org/transformations/NameBasedUuid",
1865
+ "uuidNamespace": {
1866
+ "type": "kanonak.org/transformations/IntegerLiteral",
1867
+ "integerLiteral": 5
1868
+ },
1869
+ "uuidName": {
1870
+ "type": "kanonak.org/transformations/StringLiteral",
1871
+ "stringLiteral": "www.example.com"
1872
+ }
1873
+ },
1874
+ "expectError": true
1875
+ },
1876
+ {
1877
+ "id": "name-based-uuid-namespace-ref",
1878
+ "description": "A reference is not the string spelling of its URI, so it isn't a namespace",
1879
+ "expr": {
1880
+ "type": "kanonak.org/transformations/NameBasedUuid",
1881
+ "uuidNamespace": {
1882
+ "type": "kanonak.org/transformations/UriLiteral",
1883
+ "refTo": "example.org/namespaces/dns"
1884
+ },
1885
+ "uuidName": {
1886
+ "type": "kanonak.org/transformations/StringLiteral",
1887
+ "stringLiteral": "www.example.com"
1888
+ }
1889
+ },
1890
+ "expectError": true
1891
+ },
1892
+ {
1893
+ "id": "name-based-uuid-name-number",
1894
+ "description": "A name that isn't a string is an error",
1895
+ "expr": {
1896
+ "type": "kanonak.org/transformations/NameBasedUuid",
1897
+ "uuidNamespace": {
1898
+ "type": "kanonak.org/transformations/StringLiteral",
1899
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1900
+ },
1901
+ "uuidName": {
1902
+ "type": "kanonak.org/transformations/IntegerLiteral",
1903
+ "integerLiteral": 5
1904
+ }
1905
+ },
1906
+ "expectError": true
1907
+ },
1908
+ {
1909
+ "id": "name-based-uuid-name-list",
1910
+ "description": "A list is not a name, even of one string",
1911
+ "expr": {
1912
+ "type": "kanonak.org/transformations/NameBasedUuid",
1913
+ "uuidNamespace": {
1914
+ "type": "kanonak.org/transformations/StringLiteral",
1915
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1916
+ },
1917
+ "uuidName": {
1918
+ "type": "kanonak.org/transformations/VarRef",
1919
+ "varName": "names"
1920
+ }
1921
+ },
1922
+ "env": {
1923
+ "names": [
1924
+ "www.example.com"
1925
+ ]
1926
+ },
1927
+ "expectError": true
1928
+ },
1929
+ {
1930
+ "id": "name-based-uuid-name-absent",
1931
+ "description": "Absence, the empty list, is not a name",
1932
+ "expr": {
1933
+ "type": "kanonak.org/transformations/NameBasedUuid",
1934
+ "uuidNamespace": {
1935
+ "type": "kanonak.org/transformations/StringLiteral",
1936
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1937
+ },
1938
+ "uuidName": {
1939
+ "type": "kanonak.org/transformations/VarRef",
1940
+ "varName": "names"
1941
+ }
1942
+ },
1943
+ "env": {
1944
+ "names": []
1945
+ },
1946
+ "expectError": true
1947
+ },
1948
+ {
1949
+ "id": "name-based-uuid-unknown-format",
1950
+ "description": "A uuidFormat other than canonical-uuid or hex-uuid is an error",
1951
+ "expr": {
1952
+ "type": "kanonak.org/transformations/NameBasedUuid",
1953
+ "uuidNamespace": {
1954
+ "type": "kanonak.org/transformations/StringLiteral",
1955
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1956
+ },
1957
+ "uuidName": {
1958
+ "type": "kanonak.org/transformations/StringLiteral",
1959
+ "stringLiteral": "www.example.com"
1960
+ },
1961
+ "uuidFormat": "kanonak.org/transformations/to-upper"
1962
+ },
1963
+ "expectError": true
1964
+ },
1965
+ {
1966
+ "id": "name-based-uuid-format-inherited-member",
1967
+ "description": "A uuidFormat that names a member every object inherits misses, as any other unknown format does",
1968
+ "expr": {
1969
+ "type": "kanonak.org/transformations/NameBasedUuid",
1970
+ "uuidNamespace": {
1971
+ "type": "kanonak.org/transformations/StringLiteral",
1972
+ "stringLiteral": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
1973
+ },
1974
+ "uuidName": {
1975
+ "type": "kanonak.org/transformations/StringLiteral",
1976
+ "stringLiteral": "www.example.com"
1977
+ },
1978
+ "uuidFormat": "valueOf"
1979
+ },
1980
+ "expectError": true
1981
+ }
1982
+ ]
1983
+ }