@pepys/testing 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +35 -2
  2. package/package.json +31 -4
  3. package/src/consumer-entries/build-read-functions.ts +26 -0
  4. package/src/consumer-entries/run-entry.ts +46 -0
  5. package/src/fact-index/build-fact-index.ts +39 -0
  6. package/src/fact-index/collect-scope-facts.ts +42 -0
  7. package/src/fact-index/types.ts +15 -0
  8. package/src/fact-mode/apply-fact-operations.ts +71 -0
  9. package/src/fact-mode/read-row-functions.ts +34 -0
  10. package/src/index.ts +7 -0
  11. package/src/links/read-link-rows.ts +14 -0
  12. package/src/links/remove-link.ts +16 -0
  13. package/src/links/write-link.ts +24 -0
  14. package/src/memory-store-reader/create-memory-store-reader.ts +61 -0
  15. package/src/operations/parse-operations.ts +168 -0
  16. package/src/operations/types.ts +9 -0
  17. package/src/orders/apply-in-both-orders.ts +59 -0
  18. package/src/rows/build-key-filter.ts +19 -0
  19. package/src/rows/build-row-key.ts +8 -0
  20. package/src/rows/find-row-problem.ts +43 -0
  21. package/src/rows/read-table-rows.ts +35 -0
  22. package/src/rows/remove-row.ts +13 -0
  23. package/src/rows/to-row-value.ts +15 -0
  24. package/src/rows/to-sql-value.ts +18 -0
  25. package/src/rows/write-row.ts +21 -0
  26. package/src/scope-replace/build-link-key.ts +6 -0
  27. package/src/scope-replace/build-row-id.ts +4 -0
  28. package/src/scope-replace/collect-scope-result.ts +87 -0
  29. package/src/scope-replace/pick-scope-changes.ts +166 -0
  30. package/src/scope-replace/read-scope-state.ts +61 -0
  31. package/src/scope-replace/replace-scope.ts +42 -0
  32. package/src/scope-replace/scope-replace-cases.json +1360 -0
  33. package/src/scope-replace/types.ts +39 -0
  34. package/src/scope-replace/write-scope-changes.ts +50 -0
  35. package/src/store-database/create-store-database.ts +31 -0
  36. package/src/store-database/format-store-schema.ts +57 -0
  37. package/src/store-database/types.ts +12 -0
  38. package/src/test-store/create-test-store.ts +94 -0
  39. package/src/test-store/parse-applied-facts.ts +64 -0
  40. package/src/test-store/run-consumers.ts +117 -0
  41. package/src/test-store/types.ts +68 -0
  42. package/src/utils/apply-error.ts +13 -0
  43. package/src/utils/types.ts +47 -0
@@ -0,0 +1,1360 @@
1
+ {
2
+ "consumer": {
3
+ "name": "ledger_account",
4
+ "input": {
5
+ "mode": "scope",
6
+ "scopeKind": "ledger:account",
7
+ "streams": ["facts.ledger.account"]
8
+ },
9
+ "tables": {
10
+ "ledger_account": {
11
+ "columns": {
12
+ "id": {
13
+ "kind": "entityID",
14
+ "nullable": false
15
+ },
16
+ "name": {
17
+ "kind": "text",
18
+ "nullable": true
19
+ },
20
+ "balance": {
21
+ "kind": "integer",
22
+ "nullable": false
23
+ },
24
+ "is_closed": {
25
+ "kind": "boolean",
26
+ "nullable": false
27
+ },
28
+ "opened_at": {
29
+ "kind": "timestamp",
30
+ "nullable": true
31
+ },
32
+ "updated_at": {
33
+ "kind": "timestamp",
34
+ "nullable": false
35
+ }
36
+ },
37
+ "primaryKey": ["id"],
38
+ "indexes": []
39
+ },
40
+ "ledger_entry": {
41
+ "columns": {
42
+ "id": {
43
+ "kind": "entityID",
44
+ "nullable": false
45
+ },
46
+ "account_id": {
47
+ "kind": "entityID",
48
+ "nullable": false
49
+ },
50
+ "amount": {
51
+ "kind": "integer",
52
+ "nullable": false
53
+ },
54
+ "rate": {
55
+ "kind": "real",
56
+ "nullable": false
57
+ },
58
+ "memo": {
59
+ "kind": "text",
60
+ "nullable": false
61
+ },
62
+ "details": {
63
+ "kind": "json",
64
+ "nullable": false
65
+ },
66
+ "posted_at": {
67
+ "kind": "timestamp",
68
+ "nullable": false
69
+ }
70
+ },
71
+ "primaryKey": ["id"],
72
+ "indexes": [["account_id"]]
73
+ }
74
+ },
75
+ "links": [
76
+ {
77
+ "name": "entry_of",
78
+ "from": ["ledger:entry"],
79
+ "to": ["ledger:account"],
80
+ "hub": false
81
+ }
82
+ ]
83
+ },
84
+ "cases": [
85
+ {
86
+ "title": "a first result writes each row and link, with an insert entry for each",
87
+ "batches": [
88
+ {
89
+ "scopes": [
90
+ {
91
+ "scope": "ledger:account:a",
92
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
93
+ }
94
+ ],
95
+ "changes": [
96
+ {
97
+ "type": "ledger_account",
98
+ "key": "[\"ledger:account:a\"]",
99
+ "value": {
100
+ "id": "ledger:account:a",
101
+ "name": "Alpha",
102
+ "balance": 5,
103
+ "is_closed": false,
104
+ "opened_at": "2026-11-01T09:00:00.000Z",
105
+ "updated_at": "2026-11-01T09:00:00.000Z"
106
+ },
107
+ "headers": {
108
+ "operation": "insert"
109
+ }
110
+ },
111
+ {
112
+ "type": "ledger_entry",
113
+ "key": "[\"ledger:entry:e1\"]",
114
+ "value": {
115
+ "id": "ledger:entry:e1",
116
+ "account_id": "ledger:account:a",
117
+ "amount": 5,
118
+ "rate": 1.25,
119
+ "memo": "Rope",
120
+ "details": {
121
+ "till": "north"
122
+ },
123
+ "posted_at": "2026-11-01T09:00:00.000Z"
124
+ },
125
+ "headers": {
126
+ "operation": "insert"
127
+ }
128
+ },
129
+ {
130
+ "type": "link",
131
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
132
+ "value": {
133
+ "type": "entry_of",
134
+ "from": "ledger:entry:e1",
135
+ "to": "ledger:account:a",
136
+ "confidence": 1,
137
+ "evidence": "event:ledger:e1"
138
+ },
139
+ "headers": {
140
+ "operation": "insert"
141
+ }
142
+ }
143
+ ]
144
+ }
145
+ ],
146
+ "rows": {
147
+ "ledger_account": [
148
+ {
149
+ "id": "ledger:account:a",
150
+ "name": "Alpha",
151
+ "balance": 5,
152
+ "is_closed": false,
153
+ "opened_at": "2026-11-01T09:00:00.000Z",
154
+ "updated_at": "2026-11-01T09:00:00.000Z"
155
+ }
156
+ ],
157
+ "ledger_entry": [
158
+ {
159
+ "id": "ledger:entry:e1",
160
+ "account_id": "ledger:account:a",
161
+ "amount": 5,
162
+ "rate": 1.25,
163
+ "memo": "Rope",
164
+ "details": {
165
+ "till": "north"
166
+ },
167
+ "posted_at": "2026-11-01T09:00:00.000Z"
168
+ }
169
+ ]
170
+ },
171
+ "links": [
172
+ {
173
+ "type": "entry_of",
174
+ "from_id": "ledger:entry:e1",
175
+ "to_id": "ledger:account:a",
176
+ "scope": "ledger:account:a",
177
+ "origin": "ledger_account",
178
+ "confidence": 1,
179
+ "evidence": "event:ledger:e1"
180
+ }
181
+ ]
182
+ },
183
+ {
184
+ "title": "the same result again changes nothing and gives no change entry",
185
+ "batches": [
186
+ {
187
+ "scopes": [
188
+ {
189
+ "scope": "ledger:account:a",
190
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
191
+ }
192
+ ],
193
+ "changes": [
194
+ {
195
+ "type": "ledger_account",
196
+ "key": "[\"ledger:account:a\"]",
197
+ "value": {
198
+ "id": "ledger:account:a",
199
+ "name": "Alpha",
200
+ "balance": 5,
201
+ "is_closed": false,
202
+ "opened_at": "2026-11-01T09:00:00.000Z",
203
+ "updated_at": "2026-11-01T09:00:00.000Z"
204
+ },
205
+ "headers": {
206
+ "operation": "insert"
207
+ }
208
+ },
209
+ {
210
+ "type": "ledger_entry",
211
+ "key": "[\"ledger:entry:e1\"]",
212
+ "value": {
213
+ "id": "ledger:entry:e1",
214
+ "account_id": "ledger:account:a",
215
+ "amount": 5,
216
+ "rate": 1.25,
217
+ "memo": "Rope",
218
+ "details": {
219
+ "till": "north"
220
+ },
221
+ "posted_at": "2026-11-01T09:00:00.000Z"
222
+ },
223
+ "headers": {
224
+ "operation": "insert"
225
+ }
226
+ },
227
+ {
228
+ "type": "link",
229
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
230
+ "value": {
231
+ "type": "entry_of",
232
+ "from": "ledger:entry:e1",
233
+ "to": "ledger:account:a",
234
+ "confidence": 1,
235
+ "evidence": "event:ledger:e1"
236
+ },
237
+ "headers": {
238
+ "operation": "insert"
239
+ }
240
+ }
241
+ ]
242
+ },
243
+ {
244
+ "scopes": [
245
+ {
246
+ "scope": "ledger:account:a",
247
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
248
+ }
249
+ ],
250
+ "changes": []
251
+ }
252
+ ],
253
+ "rows": {
254
+ "ledger_account": [
255
+ {
256
+ "id": "ledger:account:a",
257
+ "name": "Alpha",
258
+ "balance": 5,
259
+ "is_closed": false,
260
+ "opened_at": "2026-11-01T09:00:00.000Z",
261
+ "updated_at": "2026-11-01T09:00:00.000Z"
262
+ }
263
+ ],
264
+ "ledger_entry": [
265
+ {
266
+ "id": "ledger:entry:e1",
267
+ "account_id": "ledger:account:a",
268
+ "amount": 5,
269
+ "rate": 1.25,
270
+ "memo": "Rope",
271
+ "details": {
272
+ "till": "north"
273
+ },
274
+ "posted_at": "2026-11-01T09:00:00.000Z"
275
+ }
276
+ ]
277
+ },
278
+ "links": [
279
+ {
280
+ "type": "entry_of",
281
+ "from_id": "ledger:entry:e1",
282
+ "to_id": "ledger:account:a",
283
+ "scope": "ledger:account:a",
284
+ "origin": "ledger_account",
285
+ "confidence": 1,
286
+ "evidence": "event:ledger:e1"
287
+ }
288
+ ]
289
+ },
290
+ {
291
+ "title": "a changed row is an update that holds the old value",
292
+ "batches": [
293
+ {
294
+ "scopes": [
295
+ {
296
+ "scope": "ledger:account:a",
297
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
298
+ }
299
+ ],
300
+ "changes": [
301
+ {
302
+ "type": "ledger_account",
303
+ "key": "[\"ledger:account:a\"]",
304
+ "value": {
305
+ "id": "ledger:account:a",
306
+ "name": "Alpha",
307
+ "balance": 5,
308
+ "is_closed": false,
309
+ "opened_at": "2026-11-01T09:00:00.000Z",
310
+ "updated_at": "2026-11-01T09:00:00.000Z"
311
+ },
312
+ "headers": {
313
+ "operation": "insert"
314
+ }
315
+ }
316
+ ]
317
+ },
318
+ {
319
+ "scopes": [
320
+ {
321
+ "scope": "ledger:account:a",
322
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha fund\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
323
+ }
324
+ ],
325
+ "changes": [
326
+ {
327
+ "type": "ledger_account",
328
+ "key": "[\"ledger:account:a\"]",
329
+ "value": {
330
+ "id": "ledger:account:a",
331
+ "name": "Alpha fund",
332
+ "balance": 5,
333
+ "is_closed": false,
334
+ "opened_at": "2026-11-01T09:00:00.000Z",
335
+ "updated_at": "2026-11-01T09:00:00.000Z"
336
+ },
337
+ "old_value": {
338
+ "id": "ledger:account:a",
339
+ "name": "Alpha",
340
+ "balance": 5,
341
+ "is_closed": false,
342
+ "opened_at": "2026-11-01T09:00:00.000Z",
343
+ "updated_at": "2026-11-01T09:00:00.000Z"
344
+ },
345
+ "headers": {
346
+ "operation": "update"
347
+ }
348
+ }
349
+ ]
350
+ }
351
+ ],
352
+ "rows": {
353
+ "ledger_account": [
354
+ {
355
+ "id": "ledger:account:a",
356
+ "name": "Alpha fund",
357
+ "balance": 5,
358
+ "is_closed": false,
359
+ "opened_at": "2026-11-01T09:00:00.000Z",
360
+ "updated_at": "2026-11-01T09:00:00.000Z"
361
+ }
362
+ ],
363
+ "ledger_entry": []
364
+ },
365
+ "links": []
366
+ },
367
+ {
368
+ "title": "a row and a link that the scope no longer returns are deleted, and a deleted link has no old value",
369
+ "batches": [
370
+ {
371
+ "scopes": [
372
+ {
373
+ "scope": "ledger:account:a",
374
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
375
+ }
376
+ ],
377
+ "changes": [
378
+ {
379
+ "type": "ledger_account",
380
+ "key": "[\"ledger:account:a\"]",
381
+ "value": {
382
+ "id": "ledger:account:a",
383
+ "name": "Alpha",
384
+ "balance": 5,
385
+ "is_closed": false,
386
+ "opened_at": "2026-11-01T09:00:00.000Z",
387
+ "updated_at": "2026-11-01T09:00:00.000Z"
388
+ },
389
+ "headers": {
390
+ "operation": "insert"
391
+ }
392
+ },
393
+ {
394
+ "type": "ledger_entry",
395
+ "key": "[\"ledger:entry:e1\"]",
396
+ "value": {
397
+ "id": "ledger:entry:e1",
398
+ "account_id": "ledger:account:a",
399
+ "amount": 5,
400
+ "rate": 1.25,
401
+ "memo": "Rope",
402
+ "details": {
403
+ "till": "north"
404
+ },
405
+ "posted_at": "2026-11-01T09:00:00.000Z"
406
+ },
407
+ "headers": {
408
+ "operation": "insert"
409
+ }
410
+ },
411
+ {
412
+ "type": "link",
413
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
414
+ "value": {
415
+ "type": "entry_of",
416
+ "from": "ledger:entry:e1",
417
+ "to": "ledger:account:a",
418
+ "confidence": 1,
419
+ "evidence": "event:ledger:e1"
420
+ },
421
+ "headers": {
422
+ "operation": "insert"
423
+ }
424
+ }
425
+ ]
426
+ },
427
+ {
428
+ "scopes": [
429
+ {
430
+ "scope": "ledger:account:a",
431
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
432
+ }
433
+ ],
434
+ "changes": [
435
+ {
436
+ "type": "ledger_entry",
437
+ "key": "[\"ledger:entry:e1\"]",
438
+ "old_value": {
439
+ "id": "ledger:entry:e1",
440
+ "account_id": "ledger:account:a",
441
+ "amount": 5,
442
+ "rate": 1.25,
443
+ "memo": "Rope",
444
+ "details": {
445
+ "till": "north"
446
+ },
447
+ "posted_at": "2026-11-01T09:00:00.000Z"
448
+ },
449
+ "headers": {
450
+ "operation": "delete"
451
+ }
452
+ },
453
+ {
454
+ "type": "link",
455
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
456
+ "headers": {
457
+ "operation": "delete"
458
+ }
459
+ }
460
+ ]
461
+ }
462
+ ],
463
+ "rows": {
464
+ "ledger_account": [
465
+ {
466
+ "id": "ledger:account:a",
467
+ "name": "Alpha",
468
+ "balance": 5,
469
+ "is_closed": false,
470
+ "opened_at": "2026-11-01T09:00:00.000Z",
471
+ "updated_at": "2026-11-01T09:00:00.000Z"
472
+ }
473
+ ],
474
+ "ledger_entry": []
475
+ },
476
+ "links": []
477
+ },
478
+ {
479
+ "title": "the change entries of a scope come as rows written, links written, rows deleted, links deleted, each in key order",
480
+ "batches": [
481
+ {
482
+ "scopes": [
483
+ {
484
+ "scope": "ledger:account:a",
485
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e2\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e2\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e2\"}}]"
486
+ }
487
+ ],
488
+ "changes": [
489
+ {
490
+ "type": "ledger_account",
491
+ "key": "[\"ledger:account:a\"]",
492
+ "value": {
493
+ "id": "ledger:account:a",
494
+ "name": "Alpha",
495
+ "balance": 5,
496
+ "is_closed": false,
497
+ "opened_at": "2026-11-01T09:00:00.000Z",
498
+ "updated_at": "2026-11-01T09:00:00.000Z"
499
+ },
500
+ "headers": {
501
+ "operation": "insert"
502
+ }
503
+ },
504
+ {
505
+ "type": "ledger_entry",
506
+ "key": "[\"ledger:entry:e1\"]",
507
+ "value": {
508
+ "id": "ledger:entry:e1",
509
+ "account_id": "ledger:account:a",
510
+ "amount": 5,
511
+ "rate": 1.25,
512
+ "memo": "Rope",
513
+ "details": {
514
+ "till": "north"
515
+ },
516
+ "posted_at": "2026-11-01T09:00:00.000Z"
517
+ },
518
+ "headers": {
519
+ "operation": "insert"
520
+ }
521
+ },
522
+ {
523
+ "type": "ledger_entry",
524
+ "key": "[\"ledger:entry:e2\"]",
525
+ "value": {
526
+ "id": "ledger:entry:e2",
527
+ "account_id": "ledger:account:a",
528
+ "amount": 5,
529
+ "rate": 1.25,
530
+ "memo": "Rope",
531
+ "details": {
532
+ "till": "north"
533
+ },
534
+ "posted_at": "2026-11-01T09:00:00.000Z"
535
+ },
536
+ "headers": {
537
+ "operation": "insert"
538
+ }
539
+ },
540
+ {
541
+ "type": "link",
542
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
543
+ "value": {
544
+ "type": "entry_of",
545
+ "from": "ledger:entry:e1",
546
+ "to": "ledger:account:a",
547
+ "confidence": 1,
548
+ "evidence": "event:ledger:e1"
549
+ },
550
+ "headers": {
551
+ "operation": "insert"
552
+ }
553
+ },
554
+ {
555
+ "type": "link",
556
+ "key": "[\"entry_of\",\"ledger:entry:e2\",\"ledger:account:a\"]",
557
+ "value": {
558
+ "type": "entry_of",
559
+ "from": "ledger:entry:e2",
560
+ "to": "ledger:account:a",
561
+ "confidence": 1,
562
+ "evidence": "event:ledger:e2"
563
+ },
564
+ "headers": {
565
+ "operation": "insert"
566
+ }
567
+ }
568
+ ]
569
+ },
570
+ {
571
+ "scopes": [
572
+ {
573
+ "scope": "ledger:account:a",
574
+ "output": "[{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e3\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e3\"}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e3\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":0.5,\"evidence\":\"event:ledger:e1\"}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":10,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
575
+ }
576
+ ],
577
+ "changes": [
578
+ {
579
+ "type": "ledger_account",
580
+ "key": "[\"ledger:account:a\"]",
581
+ "value": {
582
+ "id": "ledger:account:a",
583
+ "name": "Alpha",
584
+ "balance": 10,
585
+ "is_closed": false,
586
+ "opened_at": "2026-11-01T09:00:00.000Z",
587
+ "updated_at": "2026-11-01T09:00:00.000Z"
588
+ },
589
+ "old_value": {
590
+ "id": "ledger:account:a",
591
+ "name": "Alpha",
592
+ "balance": 5,
593
+ "is_closed": false,
594
+ "opened_at": "2026-11-01T09:00:00.000Z",
595
+ "updated_at": "2026-11-01T09:00:00.000Z"
596
+ },
597
+ "headers": {
598
+ "operation": "update"
599
+ }
600
+ },
601
+ {
602
+ "type": "ledger_entry",
603
+ "key": "[\"ledger:entry:e3\"]",
604
+ "value": {
605
+ "id": "ledger:entry:e3",
606
+ "account_id": "ledger:account:a",
607
+ "amount": 5,
608
+ "rate": 1.25,
609
+ "memo": "Rope",
610
+ "details": {
611
+ "till": "north"
612
+ },
613
+ "posted_at": "2026-11-01T09:00:00.000Z"
614
+ },
615
+ "headers": {
616
+ "operation": "insert"
617
+ }
618
+ },
619
+ {
620
+ "type": "link",
621
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
622
+ "value": {
623
+ "type": "entry_of",
624
+ "from": "ledger:entry:e1",
625
+ "to": "ledger:account:a",
626
+ "confidence": 0.5,
627
+ "evidence": "event:ledger:e1"
628
+ },
629
+ "old_value": {
630
+ "type": "entry_of",
631
+ "from": "ledger:entry:e1",
632
+ "to": "ledger:account:a",
633
+ "confidence": 1,
634
+ "evidence": "event:ledger:e1"
635
+ },
636
+ "headers": {
637
+ "operation": "update"
638
+ }
639
+ },
640
+ {
641
+ "type": "link",
642
+ "key": "[\"entry_of\",\"ledger:entry:e3\",\"ledger:account:a\"]",
643
+ "value": {
644
+ "type": "entry_of",
645
+ "from": "ledger:entry:e3",
646
+ "to": "ledger:account:a",
647
+ "confidence": 1,
648
+ "evidence": "event:ledger:e3"
649
+ },
650
+ "headers": {
651
+ "operation": "insert"
652
+ }
653
+ },
654
+ {
655
+ "type": "ledger_entry",
656
+ "key": "[\"ledger:entry:e2\"]",
657
+ "old_value": {
658
+ "id": "ledger:entry:e2",
659
+ "account_id": "ledger:account:a",
660
+ "amount": 5,
661
+ "rate": 1.25,
662
+ "memo": "Rope",
663
+ "details": {
664
+ "till": "north"
665
+ },
666
+ "posted_at": "2026-11-01T09:00:00.000Z"
667
+ },
668
+ "headers": {
669
+ "operation": "delete"
670
+ }
671
+ },
672
+ {
673
+ "type": "link",
674
+ "key": "[\"entry_of\",\"ledger:entry:e2\",\"ledger:account:a\"]",
675
+ "headers": {
676
+ "operation": "delete"
677
+ }
678
+ }
679
+ ]
680
+ }
681
+ ],
682
+ "rows": {
683
+ "ledger_account": [
684
+ {
685
+ "id": "ledger:account:a",
686
+ "name": "Alpha",
687
+ "balance": 10,
688
+ "is_closed": false,
689
+ "opened_at": "2026-11-01T09:00:00.000Z",
690
+ "updated_at": "2026-11-01T09:00:00.000Z"
691
+ }
692
+ ],
693
+ "ledger_entry": [
694
+ {
695
+ "id": "ledger:entry:e1",
696
+ "account_id": "ledger:account:a",
697
+ "amount": 5,
698
+ "rate": 1.25,
699
+ "memo": "Rope",
700
+ "details": {
701
+ "till": "north"
702
+ },
703
+ "posted_at": "2026-11-01T09:00:00.000Z"
704
+ },
705
+ {
706
+ "id": "ledger:entry:e3",
707
+ "account_id": "ledger:account:a",
708
+ "amount": 5,
709
+ "rate": 1.25,
710
+ "memo": "Rope",
711
+ "details": {
712
+ "till": "north"
713
+ },
714
+ "posted_at": "2026-11-01T09:00:00.000Z"
715
+ }
716
+ ]
717
+ },
718
+ "links": [
719
+ {
720
+ "type": "entry_of",
721
+ "from_id": "ledger:entry:e1",
722
+ "to_id": "ledger:account:a",
723
+ "scope": "ledger:account:a",
724
+ "origin": "ledger_account",
725
+ "confidence": 0.5,
726
+ "evidence": "event:ledger:e1"
727
+ },
728
+ {
729
+ "type": "entry_of",
730
+ "from_id": "ledger:entry:e3",
731
+ "to_id": "ledger:account:a",
732
+ "scope": "ledger:account:a",
733
+ "origin": "ledger_account",
734
+ "confidence": 1,
735
+ "evidence": "event:ledger:e3"
736
+ }
737
+ ]
738
+ },
739
+ {
740
+ "title": "a real column and a confidence compare as floats, so 2 and 2.0 are one value",
741
+ "batches": [
742
+ {
743
+ "scopes": [
744
+ {
745
+ "scope": "ledger:account:a",
746
+ "output": "[{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":2,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
747
+ }
748
+ ],
749
+ "changes": [
750
+ {
751
+ "type": "ledger_entry",
752
+ "key": "[\"ledger:entry:e1\"]",
753
+ "value": {
754
+ "id": "ledger:entry:e1",
755
+ "account_id": "ledger:account:a",
756
+ "amount": 5,
757
+ "rate": 2,
758
+ "memo": "Rope",
759
+ "details": {
760
+ "till": "north"
761
+ },
762
+ "posted_at": "2026-11-01T09:00:00.000Z"
763
+ },
764
+ "headers": {
765
+ "operation": "insert"
766
+ }
767
+ },
768
+ {
769
+ "type": "link",
770
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
771
+ "value": {
772
+ "type": "entry_of",
773
+ "from": "ledger:entry:e1",
774
+ "to": "ledger:account:a",
775
+ "confidence": 1,
776
+ "evidence": "event:ledger:e1"
777
+ },
778
+ "headers": {
779
+ "operation": "insert"
780
+ }
781
+ }
782
+ ]
783
+ },
784
+ {
785
+ "scopes": [
786
+ {
787
+ "scope": "ledger:account:a",
788
+ "output": "[{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":2.0,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1.0,\"evidence\":\"event:ledger:e1\"}}]"
789
+ }
790
+ ],
791
+ "changes": []
792
+ }
793
+ ],
794
+ "rows": {
795
+ "ledger_account": [],
796
+ "ledger_entry": [
797
+ {
798
+ "id": "ledger:entry:e1",
799
+ "account_id": "ledger:account:a",
800
+ "amount": 5,
801
+ "rate": 2,
802
+ "memo": "Rope",
803
+ "details": {
804
+ "till": "north"
805
+ },
806
+ "posted_at": "2026-11-01T09:00:00.000Z"
807
+ }
808
+ ]
809
+ },
810
+ "links": [
811
+ {
812
+ "type": "entry_of",
813
+ "from_id": "ledger:entry:e1",
814
+ "to_id": "ledger:account:a",
815
+ "scope": "ledger:account:a",
816
+ "origin": "ledger_account",
817
+ "confidence": 1,
818
+ "evidence": "event:ledger:e1"
819
+ }
820
+ ]
821
+ },
822
+ {
823
+ "title": "a JSON column compares as a value, whatever the order of its keys",
824
+ "batches": [
825
+ {
826
+ "scopes": [
827
+ {
828
+ "scope": "ledger:account:a",
829
+ "output": "[{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\",\"desk\":\"7\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
830
+ }
831
+ ],
832
+ "changes": [
833
+ {
834
+ "type": "ledger_entry",
835
+ "key": "[\"ledger:entry:e1\"]",
836
+ "value": {
837
+ "id": "ledger:entry:e1",
838
+ "account_id": "ledger:account:a",
839
+ "amount": 5,
840
+ "rate": 1.25,
841
+ "memo": "Rope",
842
+ "details": {
843
+ "till": "north",
844
+ "desk": "7"
845
+ },
846
+ "posted_at": "2026-11-01T09:00:00.000Z"
847
+ },
848
+ "headers": {
849
+ "operation": "insert"
850
+ }
851
+ }
852
+ ]
853
+ },
854
+ {
855
+ "scopes": [
856
+ {
857
+ "scope": "ledger:account:a",
858
+ "output": "[{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"desk\":\"7\",\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
859
+ }
860
+ ],
861
+ "changes": []
862
+ }
863
+ ],
864
+ "rows": {
865
+ "ledger_account": [],
866
+ "ledger_entry": [
867
+ {
868
+ "id": "ledger:entry:e1",
869
+ "account_id": "ledger:account:a",
870
+ "amount": 5,
871
+ "rate": 1.25,
872
+ "memo": "Rope",
873
+ "details": {
874
+ "till": "north",
875
+ "desk": "7"
876
+ },
877
+ "posted_at": "2026-11-01T09:00:00.000Z"
878
+ }
879
+ ]
880
+ },
881
+ "links": []
882
+ },
883
+ {
884
+ "title": "two scopes of one batch give their change entries in the order of the scopes",
885
+ "batches": [
886
+ {
887
+ "scopes": [
888
+ {
889
+ "scope": "ledger:account:a",
890
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
891
+ },
892
+ {
893
+ "scope": "ledger:account:b",
894
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:b\",\"name\":\"Beta\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
895
+ }
896
+ ],
897
+ "changes": [
898
+ {
899
+ "type": "ledger_account",
900
+ "key": "[\"ledger:account:a\"]",
901
+ "value": {
902
+ "id": "ledger:account:a",
903
+ "name": "Alpha",
904
+ "balance": 5,
905
+ "is_closed": false,
906
+ "opened_at": "2026-11-01T09:00:00.000Z",
907
+ "updated_at": "2026-11-01T09:00:00.000Z"
908
+ },
909
+ "headers": {
910
+ "operation": "insert"
911
+ }
912
+ },
913
+ {
914
+ "type": "ledger_account",
915
+ "key": "[\"ledger:account:b\"]",
916
+ "value": {
917
+ "id": "ledger:account:b",
918
+ "name": "Beta",
919
+ "balance": 5,
920
+ "is_closed": false,
921
+ "opened_at": "2026-11-01T09:00:00.000Z",
922
+ "updated_at": "2026-11-01T09:00:00.000Z"
923
+ },
924
+ "headers": {
925
+ "operation": "insert"
926
+ }
927
+ }
928
+ ]
929
+ }
930
+ ],
931
+ "rows": {
932
+ "ledger_account": [
933
+ {
934
+ "id": "ledger:account:a",
935
+ "name": "Alpha",
936
+ "balance": 5,
937
+ "is_closed": false,
938
+ "opened_at": "2026-11-01T09:00:00.000Z",
939
+ "updated_at": "2026-11-01T09:00:00.000Z"
940
+ },
941
+ {
942
+ "id": "ledger:account:b",
943
+ "name": "Beta",
944
+ "balance": 5,
945
+ "is_closed": false,
946
+ "opened_at": "2026-11-01T09:00:00.000Z",
947
+ "updated_at": "2026-11-01T09:00:00.000Z"
948
+ }
949
+ ],
950
+ "ledger_entry": []
951
+ },
952
+ "links": []
953
+ },
954
+ {
955
+ "title": "a row that another scope owns is a scope conflict",
956
+ "batches": [
957
+ {
958
+ "scopes": [
959
+ {
960
+ "scope": "ledger:account:a",
961
+ "output": "[{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
962
+ }
963
+ ],
964
+ "changes": [
965
+ {
966
+ "type": "ledger_entry",
967
+ "key": "[\"ledger:entry:e1\"]",
968
+ "value": {
969
+ "id": "ledger:entry:e1",
970
+ "account_id": "ledger:account:a",
971
+ "amount": 5,
972
+ "rate": 1.25,
973
+ "memo": "Rope",
974
+ "details": {
975
+ "till": "north"
976
+ },
977
+ "posted_at": "2026-11-01T09:00:00.000Z"
978
+ },
979
+ "headers": {
980
+ "operation": "insert"
981
+ }
982
+ }
983
+ ]
984
+ },
985
+ {
986
+ "scopes": [
987
+ {
988
+ "scope": "ledger:account:b",
989
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:b\",\"name\":\"Beta\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
990
+ }
991
+ ],
992
+ "refused": "scope-conflict"
993
+ }
994
+ ],
995
+ "rows": {
996
+ "ledger_account": [],
997
+ "ledger_entry": [
998
+ {
999
+ "id": "ledger:entry:e1",
1000
+ "account_id": "ledger:account:a",
1001
+ "amount": 5,
1002
+ "rate": 1.25,
1003
+ "memo": "Rope",
1004
+ "details": {
1005
+ "till": "north"
1006
+ },
1007
+ "posted_at": "2026-11-01T09:00:00.000Z"
1008
+ }
1009
+ ]
1010
+ },
1011
+ "links": []
1012
+ },
1013
+ {
1014
+ "title": "a link that another scope owns is a scope conflict",
1015
+ "batches": [
1016
+ {
1017
+ "scopes": [
1018
+ {
1019
+ "scope": "ledger:account:a",
1020
+ "output": "[{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
1021
+ }
1022
+ ],
1023
+ "changes": [
1024
+ {
1025
+ "type": "link",
1026
+ "key": "[\"entry_of\",\"ledger:entry:e1\",\"ledger:account:a\"]",
1027
+ "value": {
1028
+ "type": "entry_of",
1029
+ "from": "ledger:entry:e1",
1030
+ "to": "ledger:account:a",
1031
+ "confidence": 1,
1032
+ "evidence": "event:ledger:e1"
1033
+ },
1034
+ "headers": {
1035
+ "operation": "insert"
1036
+ }
1037
+ }
1038
+ ]
1039
+ },
1040
+ {
1041
+ "scopes": [
1042
+ {
1043
+ "scope": "ledger:account:b",
1044
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:b\",\"name\":\"Beta\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
1045
+ }
1046
+ ],
1047
+ "refused": "scope-conflict"
1048
+ }
1049
+ ],
1050
+ "rows": {
1051
+ "ledger_account": [],
1052
+ "ledger_entry": []
1053
+ },
1054
+ "links": [
1055
+ {
1056
+ "type": "entry_of",
1057
+ "from_id": "ledger:entry:e1",
1058
+ "to_id": "ledger:account:a",
1059
+ "scope": "ledger:account:a",
1060
+ "origin": "ledger_account",
1061
+ "confidence": 1,
1062
+ "evidence": "event:ledger:e1"
1063
+ }
1064
+ ]
1065
+ },
1066
+ {
1067
+ "title": "a link that is stored with no scope is a scope conflict",
1068
+ "stored": {
1069
+ "links": [
1070
+ {
1071
+ "type": "entry_of",
1072
+ "from_id": "ledger:entry:e1",
1073
+ "to_id": "ledger:account:a",
1074
+ "scope": null,
1075
+ "origin": "ledger_account",
1076
+ "confidence": 1,
1077
+ "evidence": "event:ledger:e1"
1078
+ }
1079
+ ]
1080
+ },
1081
+ "batches": [
1082
+ {
1083
+ "scopes": [
1084
+ {
1085
+ "scope": "ledger:account:a",
1086
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_entry\":{\"id\":\"ledger:entry:e1\",\"account_id\":\"ledger:account:a\",\"amount\":5,\"rate\":1.25,\"memo\":\"Rope\",\"details\":{\"till\":\"north\"},\"posted_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
1087
+ }
1088
+ ],
1089
+ "refused": "scope-conflict"
1090
+ }
1091
+ ],
1092
+ "rows": {
1093
+ "ledger_account": [],
1094
+ "ledger_entry": []
1095
+ },
1096
+ "links": [
1097
+ {
1098
+ "type": "entry_of",
1099
+ "from_id": "ledger:entry:e1",
1100
+ "to_id": "ledger:account:a",
1101
+ "scope": null,
1102
+ "origin": "ledger_account",
1103
+ "confidence": 1,
1104
+ "evidence": "event:ledger:e1"
1105
+ }
1106
+ ]
1107
+ },
1108
+ {
1109
+ "title": "a row key twice in one scope result is refused",
1110
+ "batches": [
1111
+ {
1112
+ "scopes": [
1113
+ {
1114
+ "scope": "ledger:account:a",
1115
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":6,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
1116
+ }
1117
+ ],
1118
+ "refused": "duplicate-row"
1119
+ }
1120
+ ],
1121
+ "rows": {
1122
+ "ledger_account": [],
1123
+ "ledger_entry": []
1124
+ },
1125
+ "links": []
1126
+ },
1127
+ {
1128
+ "title": "a link key twice in one scope result is refused",
1129
+ "batches": [
1130
+ {
1131
+ "scopes": [
1132
+ {
1133
+ "scope": "ledger:account:a",
1134
+ "output": "[{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":0.5,\"evidence\":\"event:ledger:e1\"}}]"
1135
+ }
1136
+ ],
1137
+ "refused": "duplicate-link"
1138
+ }
1139
+ ],
1140
+ "rows": {
1141
+ "ledger_account": [],
1142
+ "ledger_entry": []
1143
+ },
1144
+ "links": []
1145
+ },
1146
+ {
1147
+ "title": "a refused link stops the whole batch, and the scopes before it write nothing",
1148
+ "batches": [
1149
+ {
1150
+ "scopes": [
1151
+ {
1152
+ "scope": "ledger:account:a",
1153
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
1154
+ },
1155
+ {
1156
+ "scope": "ledger:account:b",
1157
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:b\",\"name\":\"Beta\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}},{\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:entry:e2\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
1158
+ }
1159
+ ],
1160
+ "refused": "bad-link"
1161
+ }
1162
+ ],
1163
+ "rows": {
1164
+ "ledger_account": [],
1165
+ "ledger_entry": []
1166
+ },
1167
+ "links": []
1168
+ },
1169
+ {
1170
+ "title": "an integer column refuses a number written with a fraction, even 5.0",
1171
+ "batches": [
1172
+ {
1173
+ "scopes": [
1174
+ {
1175
+ "scope": "ledger:account:a",
1176
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5.0,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
1177
+ }
1178
+ ],
1179
+ "refused": "bad-row"
1180
+ }
1181
+ ],
1182
+ "rows": {
1183
+ "ledger_account": [],
1184
+ "ledger_entry": []
1185
+ },
1186
+ "links": []
1187
+ },
1188
+ {
1189
+ "title": "a row with a column left out is refused",
1190
+ "batches": [
1191
+ {
1192
+ "scopes": [
1193
+ {
1194
+ "scope": "ledger:account:a",
1195
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
1196
+ }
1197
+ ],
1198
+ "refused": "bad-row"
1199
+ }
1200
+ ],
1201
+ "rows": {
1202
+ "ledger_account": [],
1203
+ "ledger_entry": []
1204
+ },
1205
+ "links": []
1206
+ },
1207
+ {
1208
+ "title": "a row with a key that is no column is refused",
1209
+ "batches": [
1210
+ {
1211
+ "scopes": [
1212
+ {
1213
+ "scope": "ledger:account:a",
1214
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\",\"colour\":\"red\"}}}]"
1215
+ }
1216
+ ],
1217
+ "refused": "bad-row"
1218
+ }
1219
+ ],
1220
+ "rows": {
1221
+ "ledger_account": [],
1222
+ "ledger_entry": []
1223
+ },
1224
+ "links": []
1225
+ },
1226
+ {
1227
+ "title": "a row with null in a column that is not nullable is refused",
1228
+ "batches": [
1229
+ {
1230
+ "scopes": [
1231
+ {
1232
+ "scope": "ledger:account:a",
1233
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":null}}}]"
1234
+ }
1235
+ ],
1236
+ "refused": "bad-row"
1237
+ }
1238
+ ],
1239
+ "rows": {
1240
+ "ledger_account": [],
1241
+ "ledger_entry": []
1242
+ },
1243
+ "links": []
1244
+ },
1245
+ {
1246
+ "title": "a boolean column refuses a number",
1247
+ "batches": [
1248
+ {
1249
+ "scopes": [
1250
+ {
1251
+ "scope": "ledger:account:a",
1252
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":0,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}]"
1253
+ }
1254
+ ],
1255
+ "refused": "bad-row"
1256
+ }
1257
+ ],
1258
+ "rows": {
1259
+ "ledger_account": [],
1260
+ "ledger_entry": []
1261
+ },
1262
+ "links": []
1263
+ },
1264
+ {
1265
+ "title": "a delete in a scope result is refused",
1266
+ "batches": [
1267
+ {
1268
+ "scopes": [
1269
+ {
1270
+ "scope": "ledger:account:a",
1271
+ "output": "[{\"delete\":{\"ledger_account\":{\"id\":\"ledger:account:a\"}}}]"
1272
+ }
1273
+ ],
1274
+ "refused": "bad-operation"
1275
+ }
1276
+ ],
1277
+ "rows": {
1278
+ "ledger_account": [],
1279
+ "ledger_entry": []
1280
+ },
1281
+ "links": []
1282
+ },
1283
+ {
1284
+ "title": "an unlink in a scope result is refused",
1285
+ "batches": [
1286
+ {
1287
+ "scopes": [
1288
+ {
1289
+ "scope": "ledger:account:a",
1290
+ "output": "[{\"unlink\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\"}}]"
1291
+ }
1292
+ ],
1293
+ "refused": "bad-operation"
1294
+ }
1295
+ ],
1296
+ "rows": {
1297
+ "ledger_account": [],
1298
+ "ledger_entry": []
1299
+ },
1300
+ "links": []
1301
+ },
1302
+ {
1303
+ "title": "an operation with two verbs is refused",
1304
+ "batches": [
1305
+ {
1306
+ "scopes": [
1307
+ {
1308
+ "scope": "ledger:account:a",
1309
+ "output": "[{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}},\"link\":{\"type\":\"entry_of\",\"from\":\"ledger:entry:e1\",\"to\":\"ledger:account:a\",\"confidence\":1,\"evidence\":\"event:ledger:e1\"}}]"
1310
+ }
1311
+ ],
1312
+ "refused": "bad-operation"
1313
+ }
1314
+ ],
1315
+ "rows": {
1316
+ "ledger_account": [],
1317
+ "ledger_entry": []
1318
+ },
1319
+ "links": []
1320
+ },
1321
+ {
1322
+ "title": "a result that is no list is refused",
1323
+ "batches": [
1324
+ {
1325
+ "scopes": [
1326
+ {
1327
+ "scope": "ledger:account:a",
1328
+ "output": "{\"upsert\":{\"ledger_account\":{\"id\":\"ledger:account:a\",\"name\":\"Alpha\",\"balance\":5,\"is_closed\":false,\"opened_at\":\"2026-11-01T09:00:00.000Z\",\"updated_at\":\"2026-11-01T09:00:00.000Z\"}}}"
1329
+ }
1330
+ ],
1331
+ "refused": "bad-operation"
1332
+ }
1333
+ ],
1334
+ "rows": {
1335
+ "ledger_account": [],
1336
+ "ledger_entry": []
1337
+ },
1338
+ "links": []
1339
+ },
1340
+ {
1341
+ "title": "an upsert to a table that the consumer does not own is refused",
1342
+ "batches": [
1343
+ {
1344
+ "scopes": [
1345
+ {
1346
+ "scope": "ledger:account:a",
1347
+ "output": "[{\"upsert\":{\"harbour_berth\":{\"id\":\"harbour:berth:1\"}}}]"
1348
+ }
1349
+ ],
1350
+ "refused": "not-owned"
1351
+ }
1352
+ ],
1353
+ "rows": {
1354
+ "ledger_account": [],
1355
+ "ledger_entry": []
1356
+ },
1357
+ "links": []
1358
+ }
1359
+ ]
1360
+ }