Bulk writes are slow: 5,000 Files and 2,000 Money transactions do not settle in 180 s #1393
Closed
opened 2026-10-07 18:18:35 +00:00 by kayg
·
58 comments
No Branch/Tag specified
dev
wip/cards2-1083
wip/cards-1083
wip/canvas-visual
wip/canvasvis2-976
wip/calhdr-1112
wip/blocks-1125
wip/allday-1107
wip/agenda-1086
wip/trayicons2-1095
wip/trayicons-1095
wip/sidebar3-1094
wip/previewcard-1098
wip/palette2-1123
wip/palette-1093
wip/nlpchip-1127
wip/mchrome-1084
wip/mailghost2-1094
wip/mailghost-1094
wip/kbpreview2-1118
wip/kbpreview-1118
wip/kanban-1092
wip/editreg-1132
wip/editorrail3-1113
wip/editorrail2-1113
wip/editorrail-1113
wip/draw4-1101
wip/draw3-1101
wip/draw2-1101
wip/draw-1101
wip/delete-1119
job/txentry-1198
job/rev1586
job/rev2-money-ident
job/rev2-webperf
job/morph-1104
job/previewcard-1098
job/importhang-1121
job/kanban-1092
job/merge-round-7b
job/merge-round-7c
job/calcards-1115
job/collabrev3-1197
job/e2e-b-1071
job/agenda-1086
job/agenda-decks
job/allday-1107
job/browserfix
job/calact9
job/adv7c-1105
wip/collabrev3-1197
wip/calcards-1115
wip/calact9
wip/calact10
wip/browserfix
wip/agenda-decks
wip/adv7c-1105
wip/txentry-1198
wip/rev2-webperf
wip/rev2-money-ident
wip/rev1586
wip/morph-1104
wip/merge-round-7c5
wip/merge-round-7c4
wip/merge-round-7c3
wip/merge-round-7c2
wip/merge-round-7c
wip/importhang-1121
wip/fix1586
wip/e2e-b2-1071
wip/e2e-b-1071
job/noncanon-1618
wip/noncanon-1618
job/edrevert-1616
job/partial-1605
wip/partial-1605
job/calact6
job/cold-1586
job/perfvm-1587
wip/cold-1586
job/cal404-1581
wip/calact6
wip/cal404-1581
job/dname-1584
wip/dname-1584
job/merge41
wip/merge41b
wip/merge41
job/rev1573c
job/hscroll-1579
wip/rev1573c
job/rev1573b
job/rev1573
wip/fix1573
wip/hscroll2
wip/hscroll-1579
job/rev1574
wip/fix1574
wip/rev1574
job/startup-1573c
job/merge40
wip/startup-1573d
wip/startup-1573c
job/calact5
wip/remtext2
wip/merge40c
wip/merge40b
wip/merge40
job/calact4
job/startup-1573
job/calprof
wip/calprof
wip/startup-1573
job/fs1575
job/fix1570
job/calact3
job/rec1540c
wip/fix1570
job/rev1570-review
wip/calact3
job/calact2
wip/calact2
job/calcold-1560
wip/calcold-1560
wip/rec1540c
job/fix1569
wip/fix1569
job/calact-1557
wip/calact-1557
job/remtext-1568
wip/remtext-1568
job/fix1566
wip/fix1566
job/rec1540b
job/reconcile-1540
job/money2rev
wip/money2rev
job/merge39
wip/merge39
wip/reconcile-1540
job/tabswitch2
job/merge38
wip/merge38
job/autherr-1553
wip/autherr-1553
job/money-688
wip/money688rev3
wip/money688rev2
wip/money688rev
wip/money688l
wip/money688k
wip/money688j
wip/money688i
wip/money688h
wip/money688g
wip/money688f
wip/money688e
wip/money688d
wip/money688c
wip/money688b
wip/money-688
wip/tabswitch2
wip/merge37
job/merge37
job/jobnames-1538
wip/jobnames-1538
job/tabcomp-perf
job/calscript-1492
wip/tabcomp-perf
wip/calscript-1492
job/mailbody-1533
wip/mailbody-1533
job/intlfmt-1461
job/merge36
wip/intlfmt2
wip/intlfmt-1461
wip/merge36
job/merge35c
job/tagperf-1186
wip/tagrev2
wip/tagrev
wip/tagperf4
wip/tagperf2-1186
wip/tagperf-1186
wip/tagfix5
wip/tagfix3
wip/tagfast2
wip/tagfast
wip/merge35c
job/notesel-1493
wip/notesel2
wip/notesel-1493
job/merge35b
wip/merge35b
wip/m35btriage
wip/m35bratchet
wip/calsel-1491
job/calsel-1491
job/notifread-1387
job/bulkwrite-1393
wip/notifread2
wip/notifread-1387
job/slowsel-1392
wip/bulkwrite-1393
wip/bulkrev4
wip/bulkrev3
wip/bulkrev2
wip/bulkrev
wip/bulkfix4
wip/bulkfix3
wip/bulkfix2
wip/bulkfix
job/mail7-1340
job/bigscroll-1481
job/searchperf-1425
wip/mailrev4
wip/mailrev3
wip/mailrev2
wip/mailrev
wip/mail7f
wip/mail7e
wip/mail7d
wip/searchperf2
wip/searchperf-1425
wip/mail7c
wip/mail7b
wip/mail7-1340
job/photoopen-1367
job/mailsel-1449
wip/bigscroll-1481
wip/slowsel3
wip/slowsel2
wip/slowsel-1392
wip/selroot
job/journaldup
wip/journaldup8
wip/journaldup7
wip/journaldup6
wip/journaldup5
wip/journaldup4
wip/journaldup3
wip/journaldup2
wip/journaldup
wip/jdrev5
wip/jdrev4
wip/jdrev3
wip/jdrev2
wip/jdrev
wip/jdfix9
job/bignote-1349
wip/mailsel-1449
wip/bignote3
wip/bignote2
wip/bignote-1349
job/statusmark-1457
wip/statusmark-1457
wip/photoopen4
wip/photoopen3
wip/photoopen2
wip/photoopen-1367
job/authflash-1473
job/merge35
wip/authflash-1473
job/phototags-1370
wip/phototags-1370
wip/merge35
wip/m35triage
job/tick-1446
wip/tick-1446
job/restartidx-1450
job/exmove-1333
job/coldnote-1451
wip/restartidx-1450
wip/exmove-1333
wip/coldnote-1451
job/undo412-1430
wip/undo412-1430
job/calwrite-1441
job/xcancel-1443
job/calscroll-1362
wip/xcancel-1443
wip/calwrite-1441
job/xsub-1445
wip/calscroll3
wip/calscroll2
wip/calscroll-1362
wip/xsub-1445
job/linkdup-1440
wip/linkdup2
wip/linkdup-1440
wip/tabswitch4
wip/tabswitch3
wip/tabswitch-1233
wip/navrev
job/tabswitch-1233
job/mdpaste-1431
job/taskundo-1433
job/directory-1199
wip/taskundo-1433
job/remind-1432
job/nfd-1411
wip/remind-1432
wip/dirfix4
wip/dirfix3
wip/dirfix2
wip/dirfix-1199
wip/directory-1199-r
wip/directory-1199
job/readerx-1427
wip/readerx-1427
job/scrollfade-1416
job/tipphone-1439
wip/nfd-1411
job/purge-1420
job/notifrow-1414
wip/purge-1420
wip/notifrow-1414
job/slop-1385
wip/slop2
wip/slop-1385
job/tbover-1409
job/qltitle-1408
job/grantlive-1405
job/taskclick-1434
wip/tbover-1409
wip/taskclick-1434
wip/qltitle-1408
job/scrub-1422
job/hotfix35
job/ticks-1361
wip/scrub-1422
job/taskprev-1401
wip/grantlive-1405
job/ghost-1323
wip/ticks3
wip/ticks2
wip/ticks-1361
wip/taskprev-1401
job/bash3-settings
wip/ghost-1323
job/mailmorph-1368
wip/bash3-settings
job/apiaudit-1374
wip/bash3-filesphotos
job/bash3-filesphotos
job/xuser39-1402
job/bash3-money
job/mailhtml-1372
wip/mailhtml2
wip/mailhtml-1372
job/bash3-interop
job/bash3-calendar
wip/bash3-money
wip/bash3-interop
wip/bash3-calendar
wip/mailmorph2
wip/mailmorph-1368
wip/bash3-tasks
job/outro-1391
job/linkpick-1379
job/linkdel-1384
wip/linkpick2
wip/linkpick-1379
wip/linkdel3
wip/linkdel2
wip/linkdel-1384
job/mailrows-1390
wip/mailrows-1390
wip/apiaudit2
wip/apiaudit-1374
job/bash-core
wip/outro-1391
wip/bash-core2
wip/bash-core
job/bash3-notes
job/bash3-tasks
job/merge34
job/notifdel-1363
wip/merge34
wip/notifdel2
wip/notifdel-1363
job/wklink-1371
wip/wklink-1371
job/searchbar-1360
wip/searchbar2
wip/searchbar-1360
job/merge33-1122
wip/merge33c
wip/merge33b
wip/merge33-1122
job/bash-security
job/mailglass-1373
wip/bash-security2
wip/bash-security
wip/mailglass-1373
job/instaudit-1369
wip/instaudit-1369
job/tabstorm-1338
wip/tabstorm2
wip/tabstorm-1338
wip/bash-files2
wip/bash-files
job/bash-files
job/bash-money
job/editfuzz-1335
wip/bash-money
wip/moneyrev
job/tasksperf-1286
wip/tasksperf3
wip/tasksperf2
wip/tasksperf-1286
wip/editfuzz2
wip/editfuzz-1335
review/dirrev-1199
job/merge32
wip/merge32b
wip/merge32
job/bash-chrome
job/tagdnd-1187
job/bash-notes
wip/bash-notes2
wip/bash-notes
wip/bash-chrome
wip/reconcile-c1
wip/adv-c1
wip/tagdnd2-1187
wip/tagdnd-1187
job/bash-calendar
job/deploy-c1
job/editbreak-1334
job/bash-perf
wip/bash-calendar2
wip/bash-calendar
wip/bash-perf
job/hunt-20261006
job/bash-interop
job/collabloss-1197
wip/collabrev-1197
wip/collabloss5-1197
wip/collabloss4-1197
wip/collabloss3-1197
wip/collabloss2-1197
wip/collabloss-1197
wip/bash-interop
job/bash-mail
wip/bash-mail
job/vis-1293
wip/vis-1293
wip/restyle-search-r
wip/restyle-search-fix2
wip/restyle-search-fix
wip/restyle-search
job/restyle-settings
job/restyle-search
wip/restyle-settings-fix
wip/restyle-settings
job/adv31
wip/advfix-a
wip/adv31
job/bash-tasks
wip/bash-tasks
job/restyle-files
wip/restyle-files-fix
wip/restyle-files
job/fmtbar-1210
wip/fmtbar2-1210
wip/fmtbar-1210
job/perf2-1124
wip/perf2-1124
job/proflog-1204
job/merge31
wip/restyle-notes
wip/merge31
wip/proflog-1204
job/restyle-notes
job/fix-1213
wip/fix-1213
job/hide-1153
job/restyle-mailmoney
job/notestatus-1209
wip/hide-1153
wip/hiderev2-1153
wip/hiderev-1153
wip/hide5-1153
wip/hide4-1153
wip/hide3-1153
wip/hide2-1153
job/sidebarblur-1208
wip/notestatus-1209
wip/sidebarblur-1208
wip/restyle-mailmoney
job/onboard-1141
wip/onboard2-1141
wip/onboard-1141.aborted-early
wip/onboard-1141
job/adv-1202
job/segmented-1200
wip/segmented-1200
job/merge30
job/notifloop-1194
job/toastmorph-1201
wip/notifloop-1194
job/perf-1124
job/tocrail-1191
job/cards-1179
wip/cards2-1179
wip/cards-1179
wip/tocrail-1191
wip/perf-1124
wip/merge30j
job/wizchoices-1140
wip/wizchoices-1140
wip/restyle-1190
job/moneyfmt-1180
wip/moneyfmt2-1180
wip/moneyfmt-1180-r
wip/moneyfmt-1180
job/pillglass-1189
job/flags-1181
wip/flags-1181
job/restyle-1190
job/settingsreg-1195
job/wizard-1140
site/website
wip/wizardrev2-1140
wip/wizardrev-1140
wip/wizard5-1140
wip/wizard4-1140
wip/wizard3-1140
wip/wizard2-1140
wip/wizard-1140
wip/pillglass-1189
wip/settingsreg-1195
job/merge29
job/fu-1171
wip/merge29j
wip/fu-1171
job/fu-1166
job/txresearch-1188
wip/fu-1166
job/merge28
job/search-1066
wip/search-1066
wip/merge28j
job/gateslot-1182
job/bulkimport-1157
job/mailnet-1160
wip/mailnetrev-1160
wip/mailnet-1160
wip/bulkrev-1157
wip/bulkimport-1157
job/startup-1161
wip/startup-1161
job/merge27
job/linkcards-1151
wip/linkcards3-1151
wip/linkcards2-1151
wip/linkcards-1151
job/traydate-1144
wip/traydate3-1144
wip/traydate2-1144
wip/traydate-1144
job/draw-1101
wip/merge27j
job/blockpill-1152
wip/blockpill3-1152
wip/blockpill2-1152
wip/blockpill-1152
job/minihover-1149
wip/minihover2-1149
wip/minihover-1149
job/merge25
wip/merge25-r
wip/merge25b
wip/merge25
job/inspector-1129
job/tags-1110
wip/inspector3-1129
wip/inspector2-1129
wip/inspector-1129
wip/tagsrev-1110
wip/tags2-1110
wip/tags-1110
job/dates-1148
wip/datesrev-1148
wip/dates2-1148
wip/dates-1148
job/licence-1145
wip/licence2-1145
wip/licence-1145
job/selfhost-1156
fixbatch-25
job/merge23
wip/merge23
job/tagfilter-1109
wip/tagfilter2-1109
wip/tagfilter-1109
job/kbd-1134
wip/kbd2-1134
wip/kbd-1134
job/palfoot-1137
wip/selfhost-1156
wip/palfoot2-1137
wip/palfoot-1137
job/toggle-1158
wip/toggle-1158
fixbatch-23
job/kbpreview-1118
job/docratchet-1155
job/perflint-1133
job/devtests-1159
wip/docratchet-1155
wip/devtests-1159
job/segv-1136
wip/toast-1142
wip/segv-1136
job/toast-1142
job/blockreload-1147
wip/blockreload-1147
job/font-1150
wip/font-1150
job/importui-1120
job/minimonth-1149
wip/importui-1120
wip/minimonth-1149
job/depcheck-1146
wip/perflint-1133
wip/depcheck-1146
job/blocks-1125
job/plus-1128
job/shift-1138
wip/plus2-1128
wip/plus-1128
wip/shift-1138
job/moneyfid-1130
job/editorrail-1113
wip/moneyrev-1130
wip/moneyfid-1130
job/noext-851
wip/noext-851
wip/noext3-851
wip/noext2-851
job/week-1135
wip/week-1135
job/editreg-1132
job/smoke-1122
wip/smoke-1122
job/docs-1143
job/palette2-1123
job/calhdr-1112
job/nlpchip-1127
job/mailghost-1094
job/reconnect-1131
wip/reconnect-1131
job/trayicons-1095
job/delete-1119
hotfix-sidebar2
job/cards-1083
job/palette-1093
job/mchrome-1084
job/e2e-a-1071
job/canvas-visual
wip/e2e-a2-1071
wip/e2e-a-1071
backup/canvas-visual-before-reconcile
wip/surfaces-p2
job/merge-round-9
wip/merge-round-9
job/7cfix-small
wip/7cfix-small
job/mailui-1078
job/merge-round-8
wip/merge-round-8
wip/mailui-1078
job/mailround-1038
job/applemail-accept
wip/settitle-1068
wip/mailround2-1038
wip/mailround-1038
wip/e2e-7b
job/crash-1069
wip/crash-1069
job/searchlost-1066
wip/searchlost-1066
job/7b-reconcile
job/flake-1065
wip/flake-1065
wip/merge-round-7b7
wip/merge-round-7b6
wip/merge-round-7b5
wip/merge-round-7b4
wip/7b-reconcile
job/appupdate-1059
job/nfd-1044
wip/appupdate-1059
job/e2e-7b
job/loop-1062
wip/loop-1062
job/pdfprev-1045
job/invtoggle-1053
wip/pdfprev-1045
wip/nfd-1044
wip/invtoggle-1053
job/7bfix-e2e
job/mailstress-b
wip/7bfix-e2e
wip/mailstress-b
job/7bfix-adv
wip/7bfix-adv
job/mailstress-a
job/stack-1054
wip/stack-1054
wip/mailstress-a
job/mailstress-1038
wip/mailstress-1038
job/upload500-1051
wip/upload500-1051
job/share-1034
wip/share-1034
job/syncerr-1037
job/7bfix-photos
wip/7bfix-photos
job/paste-1036
job/setside-1039
wip/setside-1039
wip/paste-1036
job/lease-1042
wip/syncerr-1037
wip/lease-1042
job/7bfix-data
job/passkeybind-1043
wip/apprevoke-1041
job/invite-1035
wip/invite-1035
job/merge-round-7b2
wip/merge-round-7b2
job/mailproxy-486
job/apprevoke-1041
job/rebuild-1033
job/pillborder-1029
wip/pillborder-1029
wip/mailproxy-486
wip/applemail-486
job/headless-998
wip/headless-998
job/groups-1028
wip/groups-1028
job/rebuildwarn-1016
wip/rebuildwarn-1016
job/startup-1011
wip/startup-1011
job/monthpill-1009
job/bgthumb-1025
job/sharetitle-1012
wip/monthpill-1009
wip/bgthumb-1025
wip/sharetitle-1012
job/canvas-cards-977
wip/canvas-cards-977
job/canvas-pencil-978
job/canvas-sketch-990
wip/canvas-sketch-990
wip/canvas-pencil-978
job/canvas-files-989
wip/canvas-files-989
job/canvas-collab-991
wip/canvas-collab-991
job/weekscroll-1018
wip/weekscroll-1018
wip/canvas-core-976
job/canvas-core-976
job/round-drag
wip/round-drag
job/round-settings
wip/oapi-974
job/oapi-974
job/hist2-integrate
job/mailhtml-726
wip/mailhtml-726
wip/hist2-integrate
job/moneyfu-984
job/drag-1015
wip/drag-1015
job/rename-1017
wip/rename-1017
job/hist2-api
wip/hist2-api
job/oneacct-1014
wip/oneacct-1014
wip/moneyfu-984
job/hist2-bench
job/hist2-restore
wip/hist2-bench
job/hist2-write
job/hotfix-724
wip/hotfix-724
wip/hist2-write
wip/hist2-restore
job/hist2-store
job/hist2-ui
wip/hist2-ui
wip/hist2-store
job/searchstarve-965
job/shutdown-963
wip/shutdown-963
wip/pubedit-981
job/pubedit-981
job/analytics-973
wip/searchstarve-965
job/authflash-850
job/weeklane-969
job/pvtitle-1004
job/hist-975
wip/authflash-850
job/voicepill-617
wip/pvtitle-1004
job/headring-1003
wip/weeklane-969
wip/voicepill-617
wip/headring-1003
wip/analytics-973
job/agentscope-980
wip/thumbsandbox-988
job/thumbsandbox-988
wip/hist-975
job/links-856
wip/links-856
job/davetag-966
wip/davetag-966
job/filesstorm-1000
job/hoverpad-725
wip/filesstorm-1000
job/ffmpegblas-993
job/merge-round-7a
wip/hoverpad-725
wip/ffmpegblas-993
job/nowdot-1002
wip/verify-7a
job/noteid-857
wip/nowdot-1002
wip/noteid-857
wip/merge-round-7a
wip/agentscope-980
job/imapedge
job/a11yfix2
wip/imapedge-941
wip/imapedge
wip/a11yfix2
job/notetask-986
job/logheading
wip/logheading-998
job/textthumb-652
job/photolive-987
wip/photolive-987
job/davactive-983
job/savefix-985
job/tabicons-607
wip/davactive-983
wip/tabicons-607
wip/notetask-986
wip/savefix-985
job/dirid-627
job/buildspeed-1007
wip/dirid-627
job/staging-imap
job/perfguards-impl
job/undo-a11y
wip/undo-a11y
job/mailperf
job/wal-824
wip/settings-50
job/settings-50
job/notesfilter-606
wip/notesfilter-606
job/surfaces-p2
wip/wal-824
job/maillayouts
wip/mailperf
wip/maillayouts
job/taskmeta-659
job/money-ident
wip/money-ident
wip/taskmeta-659
job/errstates
wip/perfguards-impl
job/headings-881
wip/headings-881
wip/errstates
job/voice-619
job/gaps-827
job/notesperf
wip/notesperf
wip/voice-619
job/hddsql-549
job/perf-stream-668
wip/perf-stream-668
wip/deeplinks-fix
job/deeplinks-fix
job/authfix
job/docsfix-rust
wip/docsfix-rust
job/webperf
job/docsfix-web
job/datafix2
job/webdav-lock-476
job/copyfix
wip/copyfix
wip/webperf
job/focus-658
wip/protofix
job/mediafix
job/protofix
wip/mediafix
job/agentfix
job/hhmm-724
wip/agentfix
job/undo-722
job/reuse
wip/webdav-lock-476
wip/reuse
job/scopefix
job/datafix
wip/hhmm-724
wip/undo-722
job/surfaces-p1
wip/hddsql-549
job/voicememos-618
wip/datafix2
wip/surfaces-p1
job/fix-940
wip/fix-940
job/blaze-surfaces
wip/datafix
wip/blaze-surfaces
job/taskday-655
job/linknav-639
wip/linknav-639
wip/gaps-827
job/isolation-707
job/audiophotos-720
wip/audiophotos-720
job/advfind-664
wip/voicememos-618
wip/taskday-655
wip/isolation-707
wip/advfind-664
wip/scopefix
wip/focus-658
job/testgaps
wip/testgaps
job/overscroll-718
wip/authfix
job/deps
wip/overscroll-718
job/rev2-agentfix
job/rev2-mailperf
wip/deps
job/hardening-728
wip/hardening-728
job/searchgen-832
wip/searchgen-832
job/photopw-849
job/mailsql-825
wip/photopw-849
job/sharefix
wip/sharefix
job/rev2-mailhtml-726
job/rev2-perfguards
job/copyval-723
job/lightglass-r2
wip/lightglass-r2
wip/docsfix-web
job/copy-audit
job/macinterop-staging-r2
job/design-sync
job/rev2-taskmeta-659
job/docs-audit
job/rev2-advfind-664
job/rev2-mailproxy-486
job/states-audit
job/rev2-datafix
job/design-drift
job/test-gaps
job/rev2-voicememos-618
job/rev2-mediafix
job/rev2-deps
job/rev2-datafix2
job/licence-audit
job/issue-hygiene
job/rev2-protofix
job/rev2-voice-619
job/rev2-isolation-707
job/rev2-surfaces-p1
job/deeplink-audit2
job/rev2-audiophotos-720
wip/test-gaps
job/rev2-overscroll-718
job/rev2-undo-722
wip/states-audit
job/rev2-dropmd-719
job/rev2-linknav-639
job/merge-7b-plan
wip/merge-7b-plan
job/rev2-taskday-655
wip/mailsql-825
job/rev2-webdav-lock-476
job/rev2-browserfix
wip/design-drift
job/rev2-hddsql-549
wip/deeplink-audit2
job/rev2-scopefix
job/rev2-authfix
job/rev2-hardening-728
job/rev2-wal-824
job/rev2-sharefix
job/calsidebar-638
job/chrome-audit
job/ioperf
wip/ioperf
wip/chrome-audit
wip/calsidebar-638
job/dropmd-719
wip/dropmd-719
job/ocr-build
wip/ocr-build
job/blaze-settings
wip/copyval-723
job/toastring-721
wip/toastring-721
job/deployfix-732
wip/deployfix-732
wip/blaze-settings
job/money-import-recheck
job/rev-a11y
job/perf-arch-db
job/rev-7b-data
wip/textthumb-652
wip/perf-arch-db
job/sec-protocols
job/sidehdr-660
job/rev-7b-security
job/research-surfaces
job/rev-design-gaps
job/rev-mcp-api
wip/sidehdr-660
job/perf-arch-memory
wip/sec-protocols
job/perf-arch-bundle
job/snapedge-714
wip/rev-mcp-api
job/sec-supplychain
wip/research-surfaces
job/perf-arch-sync
job/rev-consistency
job/perf-arch-server
wip/perf-arch-server
wip/perf-arch-memory
job/perf-arch-io
job/perf-arch-client
job/sec-fs
job/sec-mcp-scopes
job/sec-sharing
job/perf-guards
job/sec-browser
job/sec-admin-deploy
job/sec-auth
wip/snapedge-714
job/bgpicker-717
wip/perf-arch-bundle
wip/money-import-recheck
job/advsetup-654
wip/bgpicker-717
wip/advsetup-654
job/burst-709
job/kbdcaps-710
job/app-pw-chooser
wip/burst-709
wip/app-pw-chooser
job/imaptest-625
wip/kbdcaps-710
job/fix-499
wip/fix-499
job/perf-mut-667
job/calimg-589
job/perf-snap-666
wip/calimg-589
wip/perf-snap-666
wip/perf-mut-667
job/perf-cache-665
wip/perf-cache-665
job/voicefiles-620
wip/voicefiles-620
job/admin-burst-705
wip/admin-burst-705
job/voicememos-review
wip/voicememos-review
wip/ryw-653
job/ryw-653
job/writeonopen-661
job/instant-663
wip/writeonopen-661
job/money-import-review
wip/money-import-review
wip/importjs-610
review/integrations-407-round6
wip/integrations-review
job/dragghost-612
wip/dragghost-612
job/integrations
wip/integrations
job/decider-656
job/merge-round-6
job/perf-rerun
wip/merge-round-6
job/integrations-review-round5
job/selalign-576
wip/selalign-576
job/mcp-events-491
job/files-631
job/cal-e2e-569
wip/cal-e2e-569
job/reload-423
wip/reload-423
wip/mcp-events-491
wip/files-631
job/notesbridge-644
wip/notesbridge-644
job/editor-series
job/calcard-series
wip/calcard-series
job/mcp-events-review-491
wip/mcp-events-review
wip/editor-series
job/quirks-546
job/integrations-recheck
job/tocrail-636
wip/tocrail-636
wip/quirks-546
wip/reminders-643
job/reminders-643
wip/davscale-573
job/davscale-573
job/integrations-review
wip/ocr-eval-584
job/ocr-eval-584
job/esc-537
wip/esc-537
job/toastname-586
wip/toastname-586
job/submenu-579
wip/submenu-579
job/tasks-mode
wip/tasks-mode
job/agentdocs-630
job/dupwrite-634
wip/agentdocs-630
wip/dupwrite-634
job/lightglass-588
wip/lightglass-588
job/tabswitch-549
job/ghosttask-623
wip/ghosttask-623
job/toaststack-616
job/weekstate-609
job/mailsync-613
wip/mailsync-613
wip/weekstate-609
job/maildup-626
wip/tabswitch-549
wip/maildup-626
wip/toaststack-616
job/motion-611
wip/motion-611
job/tlstest-601
wip/tlstest-601
job/perf-495
job/floating-sheet
wip/floating-sheet
job/remdup-585
wip/remdup-585
job/fix-502
wip/fix-502
job/attachplay-622
job/perf-batch
wip/perf-batch-563
wip/perf-495
hotfix/mail-sync-diag
job/mail-m3
wip/mail-m3
job/attach-poof-603
job/calhover-608
job/editorbar-604
job/mentions-605
job/merge-round-4
job/allday-514
wip/merge-round-4
wip/allday-514
job/merge-round-4a
wip/merge-round-4a
job/sharestack-580
job/fix-501
wip/sharestack-580
wip/fix-501
job/perf-batch-563
job/theme-detail
job/apw-cache-review
wip/apw-cache-review
job/probe-520
wip/probe-520
job/mac-393
wip/mac-393
job/header-571
job/flake-513
wip/flake-513
job/docs-thumb-547
wip/header-571
job/webcal-572
wip/webcal-572
wip/shortcuts-542
job/shortcuts-542
wip/docs-thumb-547
job/caldav-stress
wip/caldav-stress
wip/sweep-478
job/apw-cache-512
wip/apw-cache-512
job/money-empty-540
wip/restart-505
wip/money-empty-540
wip/fix-510
job/restart-505
job/fix-503
job/perf-496
wip/perf-496
job/fix-498
wip/fix-498
job/info-inspector-465
wip/info-inspector-465
job/fix-510
job/fix-507
wip/fix-507
wip/fix-503
job/fix-493
job/money-kinds
wip/money-kinds
job/hygiene-548
job/merge-round-3
wip/fix-493
job/drag-snap-536
wip/merge-round-3
wip/merge-round-0930
wip/drag-snap-536
job/align-538
wip/align-538
job/bg-flash
wip/bg-flash
job/money-import
job/search-count-544
wip/search-count-544
wip/money-import
job/settings-key-541
wip/settings-key-541
job/toast-539
job/preview-421
wip/preview-421
wip/toast-539
job/tasks-500-531
job/title-plain-526
wip/title-plain-526
wip/tasks-500-531
job/notes-bridge
wip/parity-484
job/parity-484
job/files-slow
job/crash-525
wip/notes-bridge
wip/files-slow
wip/crash-525
job/kbd-motion-527
wip/bg-422
job/analytics-504
wip/analytics-504
wip/kbd-motion-527
job/upload-pill-523
wip/upload-pill-523
wip/tray-order
job/tray-order
wip/overflow-mid
wip/merge-round-2
job/perf-494
wip/perf-494
job/merge-round-3-pre-rebuild
wip/mcp-fast-492
wip/motion-477
wip/asr-ab-489
wip/theme-variants-506
wip/overflow-511
wip/week-header-508
wip/attach-427
job/dav-delete-471
job/iso-435
wip/iso-435
wip/files-sel-keys
wip/dav-delete-471
job/align-253
job/siwc-490
wip/siwc-490
job/money-kinds-review
wip/align-253
wip/money-kinds-review
job/small-bugs-3
wip/overlay-title-487
wip/multiget-500
wip/hidden-420
wip/webcal-ui
wip/webcal-431
job/perf-367
job/location
wip/small-bugs-3
wip/location
wip/perf-367
wip/admin-deny-483
job/tag-unicode-473
wip/tag-unicode-473
job/blur-436
wip/photos-470
wip/blur-436
wip/small-bugs-4
wip/hunt-20260930
wip/settings-hdr-482
wip/chips-416
job/dedup-375
wip/dedup-375
job/doc-stack
wip/doc-stack
job/tokens-literals
wip/tokens-literals
job/jobs-leftovers
wip/send-fast
wip/paste-467
wip/money-numbers
job/money-plugin
wip/money-plugin
backup/doc-stack-r3-reset-source
job/break-dav
wip/merge-batch
wip/crossday-469
wip/mac-verify
wip/mail-m2
wip/break-dav
wip/money-review2
job/money-md
job/modes-424
wip/money-md
wip/jobs-leftovers
job/agenda-413
wip/agenda-413
job/dedup-375-clean
job/dedup-375-before-rebase
wip/modes-424
job/recog-417
wip/recog-417
wip/bounce-425
wip/ab-384-luna
backup/doc-stack-round3-before-rebuild
job/webdav-perf
wip/webdav-perf
job/toast-ring
wip/toast-ring
job/money-review
wip/money-review
wip/micro-motion
wip/settings-card
wip/minical
job/notes-imap-428
job/least-priv
wip/ui-small-2
wip/flaky-426
wip/drag-end-418
job/jank
wip/jank
wip/least-priv
wip/docs-site
job/agenda
job/sec-batch
wip/sec-batch
wip/per-user-index
job/area-calendars
wip/area-calendars
job/parity
wip/parity
job/documents-research
wip/documents-research
job/test-infra
job/reminders-sync
wip/small-bugs-2
wip/reminders-sync
wip/gestures
job/google-oauth
wip/tags-merge
wip/tags
job/e2e-theme
wip/e2e-theme
job/icon-align
wip/test-infra
wip/select-align
wip/editor-385
job/voice
wip/webdav
job/webdav
job/app-pw-ui
job/editor-integrity
wip/editor-integrity
wip/voice
wip/quota
wip/cal-followups
wip/icon-align
job/composer-scale
wip/composer-scale
job/jobs-page
wip/jobs-page
job/hig-type
wip/hig-type
wip/app-pw-ui
job/motion-spring
job/mcp
wip/motion-spring
wip/mcp
job/small-bugs
wip/push-hosts
job/profile-sign
wip/touch-369
wip/profile-sign
job/mobile-focus
wip/mobile-focus
wip/ui-polish-354
wip/small-bugs
wip/dup-task
job/toast-polish
job/app-pw-scopes
wip/toast-polish
wip/app-pw-scopes
wip/cli-agent
wip/selection-pills
job/preview-attach
wip/preview-attach
job/dav-proppatch
wip/dav-proppatch
wip/cal-switcher
job/atomic-race
wip/atomic-race
job/photos-shared
wip/photos-shared
wip/cal-grid
wip/note-rewrite
wip/search-rebuild
job/mail-m1
job/paperless-import
wip/paperless-import
wip/mail-m1
wip/hidden-activity
wip/search-d
wip/pricing-research
wip/cursors
wip/auto-scheme
job/single-pills
wip/single-pills
wip/xuser-matrix
wip/money-format
wip/app-pw-setup
wip/purge-dos
wip/vault-health
wip/caldav-apple
wip/xuser-audit
wip/e2e-green
wip/tabbar
wip/adv-harness
wip/maple-mono
job/search-fix
wip/search-fix
wip/search-perf-c
job/adv-harness
wip/sidebar-headers
job/glass
wip/temp-index
job/polish
wip/polish
wip/file-protocols
wip/money-research
wip/glass
wip/voice-models
wip/collab-redo
job/voice-research
wip/hunt-20260928
wip/notes-actions-research
wip/search-pad
wip/search-perf
wip/search-sticky
wip/editor-undo
wip/chrome-rules
wip/motion
wip/appearance-research
wip/appearance
wip/audit-bugs
wip/cal-glass
wip/block-actions
wip/authz-order
wip/event-stripes
wip/chrome-sidebar
wip/auth-flaky
wip/robust-2
wip/gate-fix
wip/menu-blur
wip/import-calternaljs
wip/tray-fix
job/import-calternaljs
wip/index-order
wip/audit-fixes
wip/search-chevrons
research/mail
wip/phone-chrome
wip/dedup-break
wip/csp
wip/ui-audit
wip/select-toast
wip/perf
wip/flat-layout
wip/fonts
wip/event-tint
wip/sync-converge
wip/data-split
wip/glass-audit
wip/robustness
wip/sync-chaos
wip/search-thumbs
wip/fuzz
wip/menu-icons
wip/search-pill
wip/sync-changing
wip/heading-links
wip/date-formats
wip/a11y
wip/break-editor
wip/e2e-fix
wip/settings-sections
wip/sync-root-guard
wip/search-palette
wip/share-edit
job/toasts
wip/toasts
wip/cont-analytics
wip/authz-review
wip/popovers
wip/overlay-glass
wip/change-feed
wip/editor-modes
wip/composer-align
wip/cont-agenda
wip/agenda-merge
job/agent-conventions
wip/agent-conventions
wip/backend-misc
job/route-audit
wip/route-audit
wip/ui-batch
wip/heif-hardening
wip/grid-resize
wip/ask-page
wip/webmcp
job/deeplink-audit
wip/deeplinks
wip/shortcuts
wip/cont-tz-days
main
No results found.
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
kayg/calternal-v0#1393
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Finding (interaction audit #1369)
On a fresh Instance, the audit fixture could not create 5,000 Files or 2,000 Money transactions within 180 s (bounded concurrency 8, through the public API): Files did not settle (0 indexed), Money did not settle (0 counted). 1,000 Tasks took 73 s and 1,000 Notes 55 s; 500 Photos 121 s. The numbers came from the perf VM. Full audit: https://git.kayg.org/kayg/calternal/src/branch/job/instaudit-1369/docs/perf/2026-10-07-1369-interaction-audit.md
This is a User-visible throughput problem (imports, bulk upload, a client syncing a folder), not only a test problem.
Required
Measure write throughput per item type through the real API on the perf VM: Files upload (small files), Money transaction create, Task create, Note create, Photo upload, at concurrency 1 and 8. Profile the slowest (per-request fsync, index transaction per item, projection rebuild, lock contention) and fix the top cause for Files and Money. Target: 5,000 small files and 2,000 transactions settle (visible in the UI and Search) in under 60 s on the perf VM, without weakening durability (DESIGN §2 data safety). Report before/after tables.
Started #1393 on
job/bulkwrite-1393, base/head0419170fe456b58614befcb12d9c9d255118f1c0. Read CLAUDE.md, CONTEXT.md, DESIGN §§2, 48, 59 and audit #1369. Will measure real API writes at concurrency 1 and 8, preserve file sync and authority durability, and check Money balance equivalence. No push or deployment.Static write-path finding (#1393):
files::index::record_implcalledrefresh_sidecar_pairsafter each item. That reads every sibling and begins a writer transaction even when no pair changes. The new path checks four candidate names with the path primary key, plus prior identity links with the Sidecar parent index; full reconciliation is retained. A regression also covers a Sidecar uploaded before its parent and ambiguous parent stems.Money
create_transactionprojected the proposed Budget and then reloaded/projected it for the reply, andview_ofrendered the full Account register to return one row. The change reuses the checked projection and shared single-row renderer. Concurrent non-agent creates share a bounded month save, while each intermediate proposal keeps its existing exact validation. Agent writes retain their calling task for Undo attribution. Generated balance equivalence tests are added. No sync policy or filesystem durability call is removed. Perf VM is currently held by another job; measurement has not started.Files slice committed as
803772ca1. Focused production-route tests (verbatim):Focused API Vitest files (verbatim):
Money batching, exact balance properties and an intermediate-overflow regression are implemented. Initial Money crate tests are running. The perf VM lock is held by another job. This is progress evidence, not a final throughput claim.
Progress at
dacb1930f717a4d478aa2380668b7da3f2007048(mergedorigin/devonce; no push).Money batches adjacent creates for the same Budget and month, up to 32 requests after a 2 ms collection interval. The existing synced compare-and-swap save is unchanged. Every intermediate projection is checked before the final save, and no success or event precedes that save. Agent writes retain their task-local Undo scope.
Initial Money crate result (verbatim):
This includes 24 property cases comparing full Account and month models between individual and concurrent writes, invalid neighbours, and intermediate overflow. Final gates for the merged source are running.
Concurrency review found that an ordinary parent can miss a Sidecar inserted between its first candidate query and its Index write. A second bounded candidate check after that write now preserves pairing; the concurrent sibling regression is being checked. This does not remove or defer filesystem durability.
The perf VM lock is held by another calternal job. Paired five-sample measurements are queued through
perf-run; no throughput claim yet. The first average phase labels its after binaryc1300457f; the full-size phase will use the merged, corrected source and will record its actual SHA.Five paired samples per row now exist for 100 small Files and 100 Money creates on the perf VM, under
perf-run. Before: shared releaseef0aa12b1; after: release sourcec1300457f. Variants were interleaved within each sample. These are average-size measurements, not the final 5,000/2,000-item target.Money at concurrency 8 used 15 saves per 100 creates in every after sample. Median server CPU over writes and the Search wait fell from 5,770 ms to 2,300 ms; median peak RSS was 169.1 MiB before and 176.1 MiB after. Stage tracing measured durable saves at about 50 ms p50, while 100-row batch preparation was about 4–5 ms p50. The existing fsync and durable publication path is unchanged.
The 100-file rows do not prove a throughput fix for Files. The Sidecar query removes folder-wide work as the folder grows, but the small-case write pipeline remains the limiting cost. The full-size run will include Files stage tracing; the 60 s target is not claimed.
Post-merge clippy for Files, Money and server passed (verbatim):
Web check passed; focused Files/Money Vitest output (verbatim):
Full per-crate Rust tests and the merged release build are still running. Branch head:
966c7ea36.HEAD
c51aa915a. The first full-size perf-VM round retained all ten failed samples. Both versions refused a Money create with HTTP 409 after 14.5–15.9 seconds. The before version completed 256–273 rows; the batched version completed 978–1090 rows. No retry hid a refused write.The Notes managed-date migration (#1148) walks all Home Markdown and adds absent date keys. New Money month files lacked those keys. Its independent write can change the month between Money projection and compare-and-swap. The compare-and-swap correctly refuses that race.
The fix adds absent managed date keys to a new month before its first durable publication, with the existing Notes core writer. Budget.md and Accounts.md keep their current creation format. Existing sources and Undo replacements retain their exact bytes. The old exact-Markdown test stays unchanged. A new regression requires the date backfill to be a byte-for-byte no-op on a newly created month. Full-size paired measurements will run on this fix; the failed round remains in the report.
HEAD
2580699b49commits the new-month managed-date fix. The existing exact-Markdown test is unchanged and passes.cargo clippy -p calternal-plugin-money --all-targets -- -D warnings:cargo test -p calternal-plugin-money -- --test-threads=4:The first 5,000-file baseline timed out after 180 s with 1,899 acknowledgements. Its median synced final source write was 45 ms, staging was 30 ms, and the upload-create/finish stages include serialized namespace waits. The after samples are still running. This evidence does not support calling the 60 s Files target complete.
HEAD
959e4f25c3. All branch gates pass on the final Rust code. The full-size Files phase is finishing; fixed-release Money measurements are queued. This is not a claim that either full-size target has passed.cargo fmt --check: exit 0, no output.Files clippy:
Money clippy:
Server clippy:
Files tests:
Money tests:
Server tests:
Web check:
Focused Vitest (
src/lib/files/api.test.ts,src/lib/money/api.test.ts,--maxWorkers=2):Focused Money API probe, on fixed release
2580699b4:HEAD
38a08b464. The full-size root-disk rounds are complete, with five interleaved before/after samples per case. Raw rows are retained in docs/perf/2026-10-08-1393-bulk-writes.jsonl, and the full tables are in docs/perf/2026-10-08-1393-bulk-writes.md.Money meets the under-60-second root-disk target in every sample. Each sample verifies unique row IDs, exact Account/cleared/uncleared balances and Category Activity. The date migration does not cause a refused month write on the fixed release.
Files does not meet the target. Its median Index/change-feed stage falls from 5.434 ms to 1.263 ms (about 77%) and median server CPU from 56.37 s to 41.59 s (about 26%) in these failed runs. The final synced source-write stage remains 46.145/50.125 ms before/after, with a further staging sync and namespace waits. A durable batch publication primitive in calternal-fs is needed to fix that remaining source-writer cost. This branch keeps its writer behavior and durability guarantees unchanged.
Full-size HDD measurements are running under perf-run and the existing 8 ms/200 IOPS emulation. Their rows will be included in the final report. No before/after completed-batch ratio is claimed when the before run failed.
Partial resolution of #1393. Head
67a59b7f12, branch job/bulkwrite-1393. The worktree is clean. The branch merged origin/devd395b6e25once before final gates.Built
Results
Money meets the fresh-Instance root-disk 60 s target in every sample, with exact balances and unique IDs verified. Files does not. Neither full-size case completes on the emulated HDD. All measurements ran under perf-run on the perf VM; load averages are retained. No build ran there. No failed-before completed-batch ratio is claimed.
Full report: docs/perf/2026-10-08-1393-bulk-writes.md. Raw rows: docs/perf/2026-10-08-1393-bulk-writes.jsonl. These sit beside docs/perf/baseline.json, which has no matching bulk-write profile. Notes batch-tail review is filed as #1426; request p95 stayed stable, so this is not an automatic route-p95 violation or a proven code cause.
Files
Gates (verbatim summary output)
cargo fmt --check: exit 0, no output.Files clippy:
Money clippy:
Server clippy:
Files tests:
Money tests:
Server tests:
Web check:
Focused Vitest (
src/lib/files/api.test.ts,src/lib/money/api.test.ts,--maxWorkers=2):Focused Money API probe, on fixed release
2580699b4:Known gaps
The Files target remains unmet. Its final source write is still serialized and synced per item, with a further staging sync and namespace waits. A durable batch publication primitive and a wider upload/Index plan are needed in calternal-fs. That would change another crate's writer behavior/design and is not implemented here. The HDD Money target also remains unmet; median durable batch time is about 998 ms. All failed runs remain in the report.
Legacy Money months without managed dates can still receive the one-time migration and correctly refuse a concurrent edit with 409. Genuine source changes are never overwritten. Four existing Web CSS warnings remain; checks have zero errors and the performance guard has zero violations.
Decisions
DESIGN does not prescribe collection/admission sizes: chose 2 ms, 32 items per batch and 64 accepted creates per User. These bound work and let other operations take the lock between saves. Agent creates use one-item batches. New-month date values use UTC second precision and only absent keys; existing source metadata and Undo bytes stay intact. No dependency or migration was added.
Expected cost: ordinary Files records use two bounded Index probes per item, each with up to four candidate paths and a prior-identity probe. Money source saves cost O(ceil(items/batch size)); validation of growing source text still costs O(items squared). The report measures the requested realistic sizes rather than dropping safety checks to improve timing.
UX gaps closed / left
Closed: concurrent Money create save cost, full-register rendering for one response, and the new-month migration collision. Existing Files and Money production views are used for real-row checks; no visible control was added or restyled.
Left: full Files settlement and both full-size HDD completion targets. Timed-out runs do not prove all-item UI/Search settlement.
For the merge round
These full suites are deferred by the verification policy; crate gates, focused Vitest and the Money API regression probe ran here.
Cleanup: cargo clean removed 29972 files, 19.6GiB total. Local apps/web/build was removed. Review artifacts remain ignored; no screenshots, videos or binaries were committed. No push, deploy, integration merge or issue close was performed.
Related measurement from audit #1374, branch head
95223dde0194a91a9b7783f4e814e1943e3384ce, on the perf VM with the shared release server fromd395b6e25:POST /api/v1/files/movemoving a folder with 1,000 children returned 204 and became visible in 831.21 ms (single sample; p50/p95/max all 831.21 ms), above the 500 ms visibility target. Undo returned 204 and became visible in 280.84 ms. The route iscrates/plugins/files/src/lib.rs::move_pathsintotransfer. The full audit will include its query-plan evidence and load average. This is recorded under the existing bulk-write work rather than as a duplicate issue.Further #1374 bulk evidence, branch head
ace13fe6c(the active audit loaded runner revision199f444d):PUT /api/v1/tags/itemsrequests returned HTTP 200 × 100 and became visible in 23,715.12 ms, against the 500 ms target.POST /api/v1/files/trashrequests received no HTTP response (client status 0 × 2) after 240,018.44 ms. No successful visibility time was available. Restore, re-Trash and Empty Trash were skipped because the 1,000-Item Trash fixture did not settle.These are single-operation samples. The delete call path is
crates/plugins/files/src/lib.rs::trash; the re-tag path iscrates/calternal-tags/src/lib.rs::assign_tags. The hide/unhide rows in the active audit are invalid: that process sent owner paths to an endpoint that accepts shared Items. The runner fixture was corrected in the current branch, but the active process loaded the earlier code. This comment records the bulk-write evidence under #1393; it does not claim that the timeout has the same Index-pool cause as #1383.Further #1374 bulk evidence, branch head
bd078a3c3, on the perf VM with the shared release server fromd395b6e25:Completing 1,000 Tasks through 1,000
POST /api/v1/notes/tasks/tickrequests at concurrency 8 had no visible completed state by 600,021.10 ms. The lower-bound row was written while the client action was still pending; this is not a completion time or p95. The route iscrates/plugins/notes/src/tasks_api.rs::tick; each request takes the User lock, reads and replaces the Markdown source, then callstasks_store::index_source. This adds Task completion to the existing bulk-write investigation. I am not filing a duplicate issue.Rerun result (2026-10-08, perf VM): completing 1,000 Tasks through
POST /api/v1/notes/tasks/ticktook 1,347,186.57 ms to become visible (target 500 ms). All 1,000 requests returned HTTP 200; acknowledgement took 1,346,624.18 ms; 497,000 response bytes; 1,000 rows touched. The state was not visible at the 600,021.10 ms observer checkpoint, then became visible before the action returned. Cause:crates/plugins/notes/src/tasks_api.rs::tickupdates one source Task per request under the User lock. The 50 ms/auth/meprobe had 26,837 samples (p95 36.64 ms; 24,197 returned 429), so its p95 is not a clean successful-request distribution. The audit CSV records the per-operation checkpoints.Additional result from the same perf-VM audit: the 1,000-request Task Undo batch acknowledged after 323,326.81 ms, with 166 HTTP 200 and 834 HTTP 500 responses. Only 166 rows were touched; the full undone state was not visible by 600,374.06 ms. This is a server-error finding in
crates/plugins/notes/src/tasks_api.rs::tick, not only a slow-write result. Per-request error bodies were not retained, so a separate issue (#1446) records the unresolved 5xx reproduction. The immediately preceding completion batch returned 1,000 HTTP 200 and became visible after 1,347,186.57 ms.Independent read-only bulkrev review started on job/bulkwrite-1393 at
67a59b7f12, comparingd395b6e25. I will test Money numerical equivalence and batch crash/restart behavior, plus Files Sidecar identity races. No tracked edits or ref changes. Evidence will be in artifacts/bulkrev-findings.md; I will not read the author report or issue perf notes.Independent bulkrev control finding:
d395b6e25returned 409 (the file changed; reload and try again) on a valid serial transaction create during disposable fixture construction. The review's generated Account/month-model checks passed on reduced control fixtures in Europe/Berlin, Asia/Kolkata and America/Los_Angeles. The head binary is still building; these are baseline observations, not head approval.Contract finding from the diff and control run: mixed valid/invalid independent create requests have per-request validation. Valid neighbours commit and the invalid request is rejected, on the baseline too. This differs from a literal all-or-nothing interpretation of the review brief. I will check atomic publication of the accepted proposals and report the distinction explicitly.
Continuing #1393 on job/bulkwrite-1393, base/head
67a59b7f12. Reading independent number evidence and checking baselined395b6e25. Scope: canonical same-date Money posting order and bounded assignment CAS retries, with existing number tests preserved and generated full-model regressions.Baseline attribution: the calculator at
d395b6e25and67a59b7f1has the same blobf05e8ac338. Replayed the review's seed-1 40 postings in input order and its recorded burst order through that calculator: unequal full models, including October payment reserve 18571 versus 21143 (synthetic minor units). This is inherited file-order sensitivity, not duplicate or lost batch arithmetic.Decisions for #1393: canonical ascending order is calendar date, posting phase (non-positive ordinary postings, transfers, positive ordinary postings), Category ID, Account ID, exact source minor amount, currency, child posting fields, transaction ID. Posting fields precede random API IDs; labels/notes do not affect order. Purchases precede payments and refunds follow payments. The same order governs reserve funding and FIFO payment/refund moves. Source files remain lossless. Historical oracle vectors that depended on file FIFO must change under this explicitly requested behavior; all vector inputs and number tests are retained, with Python independently generating expected models.
Assignments retain the existing per-User exclusive Money writer lock and rebuild from latest source bytes after a CAS conflict, for at most four attempts. Successful retries preserve unrelated Assignments and unknown metadata. Exhaustion remains a conflict, with no partial write.
Exact baseline attribution completed: the reference calculator read from
d395b6e25, with the independent review's exact 40-row seed, opening rows, Assignments and recorded burst file order, reproduces October Card payment Available 18571 (input order) versus 21143 (burst file order), and final Card debt not covered 33340 versus 30768. All numbers are synthetic minor units. The current calculator and projection at the starting head have the same source blobs asd395b6e25. With the canonical order, the independent Python reference returns 18571 and 33340 for both orders, and every month field matches. Evidence: artifacts/bulkfix-baseline-exact.py and artifacts/bulkfix-baseline-exact.log.All 625 original recorded vector inputs remain. Eleven outputs from the same-day payment/refund FIFO case change under #1393's explicit replacement of file ordering. No Rust assertion, status expectation or historical vector input is removed or weakened. The Python oracle suite passes 100 tests. Rust clippy for both Money crates passed; Rust route tests are now running after the first dependency build.
#1393 Money correctness continuation
Final head:
f3b57c58a0063f9e0aacad45671ef27b07a5c855onjob/bulkwrite-1393. The Money corrections are ready for merge; the original Files/HDD throughput scope remains partial. No push, deploy or issue closure.Built
Files
crates/calternal-money/src/{budget,ledger}.rs,tests/{properties,review_984}.rs,tests/properties.proptest-regressions.crates/plugins/money/src/{routes,store,tests}.rs,proptest-regressions/tests.txt.docs/DESIGN.md§48,docs/perf/2026-10-08-1393-money-number-fixes.md,bench/money-pair-review-984.sh.spikes/money-format/budget_math.py,spikes/money-markdown/test_money_adversarial.py,contracts/vectors/money/money.v1.json.Decisions
The canonical key is date, phase, Category ID, Account ID, exact source amount, currency, child posting fields, then transaction ID. Non-positive ordinary rows precede transfers; positive ordinary rows follow transfers. Posting fields precede generated IDs so recreated equivalent sets also agree. Source bytes keep their written order. This explicitly replaces the old same-date file-order decision. All 625 original vector inputs remain; eleven expected outputs change under this rule, independently computed by the Python oracle. No Rust assertion or divergence rule was weakened.
Assignment saves make at most four attempts under the existing Money writer lock. They reload and validate exact targets on each conflict. Persistent conflicts still return 409 with no write. No new dependencies or UI controls.
UX gaps closed
Arrival order no longer changes Card numbers. Transient metadata conflicts no longer refuse otherwise valid Assignment targets. No UI change requires screenshots.
Known gaps
The previous Files/emulated-HDD #1393 throughput target remains incomplete; see
docs/perf/2026-10-08-1393-bulk-writes.md. This continuation fixes the two Money findings, not that publication primitive. Four continuous external source conflicts still return 409 without losing data.Expected replay cost: n transaction references × sort O(n log n) and posting work, no added per-row IO. Assignment writes have at most four loads/validations/CAS attempts. Locked perf-VM debug diagnostic (five samples, each two full replays): 2,000 rows p50 41.627 ms / p95 46.318 ms; 100,000 rows p50 2341.024 ms / p95 2996.602 ms. Process user/system CPU 41.26/0.50 s, peak RSS 279944 KiB. No matching entry in
docs/perf/baseline.json; these are not release or throughput claims. No builds ran on the VM.For the merge round
cargo clippy -p calternal-server --all-targets -- -D warningsandcargo test -p calternal-server -- --test-threads=4: combined server integration.node tests/adversarial/money_api.mjs: full-server authentication, malformed and oversized payloads, mixed valid/invalid batch isolation.MONEY_REVIEW_MODES=canonical MONEY_REVIEW_BIN=<release-review_984-binary> bash bench/money-pair-review-984.sh, throughperf-run calternalon the perf VM: release timings and reversed-row equality.Verification notes
Merged fetched
origin/devataf202e151once. The new TCP property initially exceeded the old 10-second check deadline; there was no model mismatch. The saved seed and eight cases passed with the new property's 60-second deadline in 1192.18 s. Existing checks retain their 10-second deadline. Final gate results follow below. Raw gate logs remain in ignoredartifacts/bulkfix-final-*.log. All changed doc comments were re-read.Final gates
Code head:
dd43c951c362bff5f342de0ee2669a49d19fd8cf. The one fetch andmerge used
origin/devataf202e151. All required commands exited zero.cargo fmt --checkproduced no output. The final plugin library suite took1481.47 seconds. It includes the generated TCP property.
cargo clippy -p calternal-money -p calternal-plugin-money --all-targets -- -D warnings:cargo test -p calternal-money -- --test-threads=4:cargo test -p calternal-plugin-money -- --test-threads=4:cd apps/web && bun run check:cd apps/web && bun run test --maxWorkers=2:Cleanup
cargo cleanoutput:Removed
apps/web/build. The worktree is clean.Independent bulkrev2 number review started on job/bulkwrite-1393.
Head:
f3b57c58a0.The review will inspect the diff and source, without the author's report or result files. Product code will not change. Scratch checks will cover all 11 vector changes, same-date order, Assignment races and totals.
Independent bulkrev2 review: a blocking totals counterexample remains at
f3b57c58a.Synthetic cents: cash income 20,000; assign 10,000 each to Groceries and Dining. Card Groceries purchase -10,000 on March 28; Dining purchase -10,000 on March 29. March 31 cash-to-Card payment 10,000 and Groceries refund +10,000. Card balance ends at 0 and cash at 10,000. The calculator returns Groceries Available 10,000 plus payment Available 10,000, Ready to Assign 0. The extra 10,000 persists into April. Expected payment Available is 0. The scratch regression uses the production core. Changing only the refund date to March 30 produces the correct total.
All 11 new vector reserves match the current §48 canonical Category-ID FIFO formula. Their old values follow the former file-order FIFO rule. This does not cover refunds of purchases already consumed by a payment.
Full findings and independent scratch evidence are in artifacts/bulkrev2-findings.md. Assignment race checks and requested gates are in progress. Product code is unchanged.
Independent bulkrev2 follow-up:
The refund counterexample is now cross-checked against the actual origin/dev budget calculator (
af202e151), compiled as a scratch module. The same source bytes put the March 31 refund before the March 31 payment. OLD: reserve 0, total Available 10,000 in March and April. NEW: reserve 10,000, total Available 20,000. Cash is 10,000 and Card is 0 in both. This is a demonstrated regression of a previously correct source order.A separate medium boundary finding: M=9,007,199,254,740,991 synthetic cents. Income and Groceries Assignment M-1; same-day Checking Groceries -M, a Checking Split +1 with Groceries -1 and Dining +2, Checking Groceries +M refund, Card Dining -1. File order expense/split/refund/card rejects as outside exact range; expense/refund/split/card succeeds with Checking M, Card -1, Groceries Available M-2, Dining Available 1, reserve 1, Ready to Assign 0. The unsorted cash pre-pass overflows before cancellation. This is a supported-bound acceptance inconsistency, not stored corruption.
The 256 fixed-seed histories have identical complete month snapshots under Europe/Berlin, America/New_York and Pacific/Kiritimati. Review base:
af202e1514. Required origin/dev merge reported Already up to date. First review-only commit:4695540c3. Product code remains unchanged.Final independent bulkrev2 report. Head:
c8d278b00bIndependent bulkrev2 review of #1393
Reviewed head
f3b57c58a0. Did not read author reports or result files. All amounts below are synthetic fixture cents.Changed reference vectors: manual calculations
Each fixture assigns S = 1,000,000,000,000 cents to Food and to Dining. Let A be the Food purchase and full Food refund, B the Dining purchase, and P the payment. Canonical order is Dining purchase, Food purchase, payment, Food refund.
After the purchases the reserve is A+B. The payment first consumes min(B,P) from Dining and then min(A,max(P-B,0)) from Food. The full Food refund releases all remaining unpaid Food reserve. Thus the new reserve is max(B-P,0). Food Available is S, Dining Available is S-B, payment Available is the reserve, and Ready to Assign is -2S. Cash is -P, Card is P-B, Savings is 0. Uncovered debt is 0. These equations check every output number, not only the changed field.
The old outputs use Food-first file-order FIFO. They follow the old tie rule, but leave excess reserve after the full Food refund. For these fully funded fixtures, category Available plus Ready to Assign must equal cash plus any positive Card balance. The new reserve max(B-P,0) meets that equation; the old reserve exceeds it by OLD minus NEW. Thus all 11 NEW outputs are right and the OLD outputs are wrong under the envelope conservation rule as well as current FIFO. This is a visible tie-policy change that fixes these 11 fixtures, not a general fix for paid-purchase refunds. Labels and memos cannot change the new result; stable Category IDs can.
Totals equation
Severity: specification mismatch, not a merge blocker by itself. Input: cash income 10,000; assign 10,000 to Groceries; card purchase 10,000. Expected under the requested literal equation: category Available + Ready to Assign = signed on-budget balances. Actual: lhs 10,000 (payment reserve), rhs 0 (cash 10,000 + card -10,000). The funded-card invariant compares lhs with cash balances, excluding negative Card debt. Unfunded current-month credit deficits also need an explicit correction; imported Ready to Assign adjustments are another exception. A universal signed-account equation would contradict §48's payment-reserve model.
B1: paid-purchase refund duplicates Category money (BLOCK)
Severity: blocking number corruption. Source:
budget.rsCard refund restores the full expense amount and releases only unpaid reserve. Canonical payment-before-refund does not make this safe when the payment already consumed the refunded purchase.Inputs (synthetic cents): cash income 20,000 on March 1; assign Groceries 10,000 and Dining 10,000. Card Groceries purchase -10,000 on March 28, Dining purchase -10,000 on March 29. March 31 payment from cash to Card 10,000; March 31 Card Groceries refund +10,000. The older Groceries purchase is paid first.
Expected: cash 10,000, Card 0, total real money 10,000. Groceries Available 10,000 after refund; Dining Available 0; payment reserve 0. Ready to Assign 0. April carries only 10,000.
Actual: cash 10,000, Card 0, Groceries Available 10,000, Dining Available 0, payment reserve 10,000, Ready to Assign 0. March and April both show 20,000 Available against 10,000 cash.
refund_payment_calendar_choice_is_visible_next_monthreproduces this. Moving only the refund date to March 30 gives reserve 0 and correct totals. This counterexample works with zero Card debt, so the signed-account invariant caveat does not explain it.The same-day phase is a visible policy choice: a refund credited before the payment on March 31 is still applied after it. No instant or time zone is available to recover intraday chronology. Calendar-date-only inputs need deterministic handling that also preserves total money. Existing unpaid-reserve-only refund rules leave this case unresolved. The policy does not shift transaction activity into another month, but the excess reserve rolls into April.
B1 baseline comparison: the same-day scratch writes the refund before the payment. The old stable date sort keeps that order, releases the unpaid Groceries reserve first, then consumes Dining reserve: correct 10,000 total Available. The new phase sort moves the payment before that refund and returns 20,000. Thus the new policy makes this previously correct source order wrong. The underlying paid-refund defect also exists in the old version when the payment is written before the refund.
N2: boundary acceptance still depends on same-date file order
Severity: medium; rejected input at an extreme supported value, no stored corruption. This alone does not block under the owner's odd-input rule, but the requested order-independence check fails at the bound.
Let M = 9,007,199,254,740,991 cents, the supported bound (not i64::MAX). March 1 income M-1 in Checking; assign Groceries M-1. All remaining rows are March 29: a Checking Groceries expense -M; a Checking Split parent +1 with Groceries -1 and Dining +2; a Checking Groceries refund +M; a Card Dining purchase -1.
Expected: both file orders return the same valid model. Canonical order keeps every posting value and total within the bound. Checking M, Card -1, Groceries Available M-2, Dining Available 1, payment Available 1, Ready to Assign 0.
Actual: expense, split, refund, card returns
amount is outside the exact range; expense, refund, split, card returns the expected model. The unsorted monthly cash-activity pre-pass sums Groceries -M then -1 before +M and rejects. Sorting only the later posting pass does not fix acceptance. Reproduced byboundary_shuffle_reports_acceptance_difference.The first exploratory boundary fixture failed in both orders due to canonical intermediate overflow. It was discarded as evidence. The mixed-sign split fixture above has one valid replay and one rejected replay.
Generated histories and time zones
The seeded property uses 256 histories, at most 60 rows each, four months per history, six posting forms (ordinary Card purchases/refunds, Card payments, cash transfers, Card splits, cash spending), and US/EU DST and month-edge dates: March 8, March 29, March 31, April 1, October 25 and November 1. It compares the original and shuffled complete month models with an explicitly sorted §48 baseline. Seed: 1393.
Berlin, New York and Kiritimati process time zones each passed all five scratch probes. The 256 complete-history snapshot digests and totals checks were identical across all three runs. Dates remain calendar dates. Each run checked all 1,024 generated month totals: 964 failed the literal signed-account equation and 716 differed from cash totals. These generated histories include underfunded payments, credit overspending and refunds, so those counts are observations, not 716 proven defects. The zero-debt, fully funded B1 fixture isolates a real conservation defect without those exceptions. A fully funded purchase-only fixture proves the requested signed-account equation cannot hold even in a correct Card model.
i64::MIN, i64::MAX and the two values just beyond ±MAX_EXACT are rejected both by the numeric constructor and by Markdown projection/replay. Supported ±MAX_EXACT round-trip exactly; N2 covers acceptance at the supported boundary.
The scratch probes record known failures as observations and return success when the witness is reproduced. Green probe results do not resolve B1 or N2.
Full totals for all 11 vector calculations
All amounts are synthetic cents. Savings and uncovered debt are 0 in each row. Food activity is 0; Dining activity is -B; payment activity is -P. Other marker Categories have 0 Available. Food and Dining each have Assignment 1,000,000,000,000; payment Assignment is 0. The payment Available appears in the reserve table above.
Dynamic baseline proof: the scratch compiles the origin/dev budget module, with its imports pointed at the current codec/ledger (the ledger diff only adds ordering derives). For the refund-before-payment fixture, OLD reserve/total Available is 0/10,000; NEW is 10,000/20,000, in both March and April. Cash 10,000, Card 0. See artifacts/bulkrev2-baseline.log.
Assignment retry checks
Two and five requests start behind a barrier and use the production Assignment handler, real calternal-fs conditional writes and one shared Money writer handle. Each case also injects three initial conflicts before a successful fourth attempt. The saved October Assignment rows are checked against the final month model. September's seeded data stays in the file.
Same-Category Assignments are absolute PUT targets, not increments. The final target is checked against the successful response with the largest revision; request start order does not define the winner. The sums above are the observed winners, not a required scheduling order. No target is added twice and no neighbour is lost.
Four injected source conflicts return exactly HTTP 409. Budget.md bytes and report revision stay unchanged. A queued fifth non-conflicting write is left for the next request, which successfully saves 1,234. The existing real stale-source retry test also runs in the requested full package gate; that check preserves another Category and unknown frontmatter.
Built and files
This review adds test probes and a report. It does not change product code, dependencies, lockfiles or existing test expectations. The author report and result files were not read.
crates/calternal-money/tests/review_bulkrev2/core.rs: seeded order property, raw monthly sums, supported-bound witness and paid-refund witness.crates/calternal-money/tests/properties.rs: test-only module link and module comment. The probes reuse the existing fixture builders.crates/plugins/money/src/review_bulkrev2.rs: two/five Assignment writers and exact four-conflict rejection.crates/plugins/money/src/tests.rs: test-only module link and module comment. The probes reuse the real route fixtures and fault injector.tests/money/bulkrev2-review.md: this independent report.Reviewed product head:
f3b57c58a0. Base:af202e1514. The required fetch and merge of origin/dev reportedAlready up to date.Test head:a1a2d6b290. The final issue comment gives the report commit SHA.Known gaps
B1 remains in product code and blocks this review. N2 remains as a medium boundary-input inconsistency. Product fixes are outside this read-mostly job. The two defect witnesses intentionally record current results; they are review evidence, not assertions that the incorrect numbers are desired behavior.
The requested gate passed. Six pre-existing ignored tests remain unchanged: four performance/growth profiles and two isolated child tests. The parents execute the child checks. No web code changed, so no web build or UI screenshots were required. No web build output was created. No performance profile was added for this review-only work.
Initial exploratory checks did not supply valid evidence: one boundary fixture rejected in both orders; a probe used unwrap_err on a success type without Debug; and a queued Cargo invocation retained a scratch file name after it moved. The fixture, probe and test layout were corrected. The completed final gates below use the final source layout. No existing expectation was changed.
Decisions
No product design decision was made. Same-Category Assignment calls were reviewed as absolute PUT targets, as the API declares; serial application means the final committed target wins, not the sum of targets. The largest successful response revision identifies that final write. Seed 1393 makes the three time-zone runs comparable. The literal signed-account equation was checked and reported, without changing the payment-reserve rules to force it to hold.
Verification
Environment for Cargo commands: CARGO_PROFILE_DEV_DEBUG=line-tables-only, CARGO_INCREMENTAL=0, CARGO_BUILD_JOBS=4, TMPDIR set to this worktree's target/tmp. CARGO_TARGET_DIR was left at its preset value.
cargo fmt --check: exit 0, no output.cargo clippy -p calternal-money --all-targets -- -D warnings: exit 0. Final output line, verbatim:cargo clippy -p calternal-plugin-money --all-targets -- -D warnings: exit 0. Final output line, verbatim:cargo test -p calternal-money -p calternal-plugin-money -- --test-threads=4: exit 0. Complete output, verbatim:Focused Assignment output and all 1,024 generated month sums are kept in artifacts/bulkrev2-assignment-final.log and artifacts/bulkrev2-month-totals.jsonl. Time-zone snapshot evidence is in artifacts/bulkrev2-core-{berlin,new-york,kiritimati}.log. The dynamic OLD/NEW comparison is in artifacts/bulkrev2-baseline.log. Review artifacts are not committed.
BLOCK — B1: paid-purchase refunds duplicate payment reserve and carry excess Available into the next month.
bulkfix2 started on job/bulkwrite-1393, base
c8d278b00b. Scope: correct paid Card refunds (B1), canonical bound acceptance (N2), and add shuffle/conservation regressions. All amounts used are synthetic test fixtures.bulkfix2 finding: replaying origin/dev's budget module confirms payment-first B1 leaves reserve 10,000 and total Available 20,000 in both March and April; refund-first leaves reserve 0 and Available 10,000. Evidence: artifacts/origin-dev-b1.log. Fixed replay now produces reserve 0 and Available 10,000 for both orders. N2 now uses canonical order in both the pre-pass and posting pass. The new 256-case funded shuffle property checks conservation in every March/April/May model, including paid and unpaid refunds, splits and transfers.
All 11 reviewed vectors remain unchanged and pass. One additional legacy vector (9f1c6b453f4864f3) encoded duplicate reserve after an oversized refund clears the Card balance; its Python assertion and recorded output are updated for the explicitly requested new refund behavior. Decision: refund releases its own unpaid reserve first, then other funded reserve on the same Card, capped at positive reserve. DESIGN §48 records this rule and the fully funded conservation equation, including its deficit/reconciliation limits. Core tests and clippy pass; final merged gates remain.
bulkfix2 follow-up: refunds must also be capped by debt before the inflow. If a refund crosses zero Card balance, its excess is positive Card money; releasing reserve for that excess would remove real money from a manually overfunded payment Category. Extended the property with payment assignments and added a split-refund regression that crosses zero between child legs. Split children use a bounded integer balance cursor while the parent Account balance still posts once. The Python oracle has the same rule. No new dependencies or routes. Final clippy completed successfully; final combined crate tests are building.
bulkfix2 (#1393)
Head:
3e5fe2ae1cBase:
c8d278b00bBranch: job/bulkwrite-1393
Merged origin/dev once before final gates (
b93a497e7). No push, deployment or merge into dev/main.Built
Origin/dev comparison
The frozen origin/dev budget module returns reserve 10,000 and total Available 20,000 for payment-first B1, in both March and April. Refund-first returns reserve 0 and Available 10,000. The fixed code returns the correct complete model for both file orders and for the earlier refund date. All amounts are synthetic fixture cents. Evidence: artifacts/origin-dev-b1.log.
Files changed by bulkfix2
Decisions
For fully funded expense histories, without import reconciliation adjustments:
expense Category Available + payment Category Available + Ready to Assign = Cash balances + positive Card balances, at every month end. Payment reserves are already in Category Available; do not count them twice. Negative Card balances are debt, not budget money. Current-month credit deficits and imported Ready to Assign adjustments need separate corrections. DESIGN §48 and the property comment now state this domain and equation. Existing cash-only and mixed-deficit conservation tests remain unchanged.
Refund queue choice: release the refunded Category's unpaid reserve first, then consume the Card's oldest other funded purchases. Cap debt per split child, so crossing zero cannot consume payment overfunding.
Expected cost
For T month rows and L child legs: one O(T log T) canonical sort, then two O(T + L) passes and amortized queue consumption. The existing sort is reused; no extra sort, file scan, DB query or IO is added. This number-fix job did not run a performance measurement, per the current verification policy. The existing #1393 profile remains available for the merge round.
Known gaps
No identified B1/N2 gap remains. The funded conservation property does not claim a universal signed-account equation for unfunded credit or source reconciliation adjustments. No UI code changed; screenshots and UX-control review do not apply.
For the merge round
CALTERNAL_SERVER_BIN=<merged prebuilt binary> bun tests/adversarial/money_api.mjs: verify real-server Money input rejection, write isolation and exact balances on the combined branch.bench/bulk-write-1393.mjsthroughperf-run calternal <job> <minutes> -- ..., with Money, 2,000 rows, concurrency 1/8 and five samples. Compare with docs/perf/2026-10-08-1393-bulk-writes.md. Build here and copy the binary; do not build on the perf VM.Gate environment
CARGO_PROFILE_DEV_DEBUG=line-tables-only
CARGO_INCREMENTAL=0
CARGO_BUILD_JOBS=4
TMPDIR=/target/tmp
CARGO_TARGET_DIR retained as preset.
Status: READY FOR MERGE. All requested Rust gates passed.
Gate output (verbatim)
cargo fmt --check: exit 0, no output.cargo clippy -p calternal-money -p calternal-plugin-money --all-targets -- -D warningscargo test -p calternal-money -p calternal-plugin-money -- --test-threads=4Focused Python refund/split oracle testsCleanup: cargo clean completed; worktree web build outputs and test temporary files were removed. Build symlinks were unlinked without following their targets. Git status is clean.
Head
3e5fe2ae1c; B1 FIXED; N2 FIXED.Independent number review bulkrev3 started on job/bulkwrite-1393, reviewed head/base
3e5fe2ae1c. Product code will stay unchanged. Author report and result files will not be read. Tests will cover B1/N2 permutations, underfunded and cross-month refunds, mixed cash deficits, two Cards, split returns, hidden/missing Categories, generated histories and the eleven reference vectors. Findings are recorded in artifacts/bulkrev3-findings.md.Independent bulkrev3 findings — #1393
Reviewed product head:
3e5fe2ae1c.All amounts are synthetic fixture cents. Product code is unchanged. Author report and result files were not read.
Previous witnesses
B1: both same-date permutations return 10,000 total Available plus Ready to Assign, against 10,000 real money, in March and April. Reserve is 0. Correct.
N2: all 24 permutations accepted with Cash MAX_EXACT, Card -1, Groceries Available MAX_EXACT-2, Dining 1, reserve 1 and Ready to Assign 0. Correct.
R3-1 — underfunded partial refund removes money (BLOCK)
March 1 Cash income 200; Food Assignment 50. March 10 Card Food purchase -100; March 20 Card Food refund +50.
After the purchase Food is -50 and payment reserve is 50. The refund cancels the unfunded half of the purchase. Expected after refund: Food 0, reserve 50, Ready to Assign 150; Cash 200, Card -50. Total allocations 200. Actual: Food 0, reserve 0, Ready to Assign 150; allocations 150 against real 200. Missing 50 persists in April and May.
DESIGN §48: credit overspending becomes debt; it does not spend real Cash. Here the returned debt was not funded. Releasing 50 Cash reserve while only healing negative credit Available removes money. The new unconditional refund release follows the new refund paragraph literally but conflicts with envelope conservation. The specification also needs to distinguish restoration of a current credit deficit from restoration of spendable Available.
R3-2 — later-month refund of unfunded debt creates money (BLOCK)
March 1 Cash income 200; no Assignment. March 10 Card Food purchase -100. March end Food -100, reserve 0, Ready to Assign 200. Credit deficit resets without reducing Ready to Assign under DESIGN §48. April 1 Card Food refund +100.
Expected April: Cash 200, Card 0, Food 0, reserve 0, Ready to Assign 200; total 200. No real money enters: only unfunded debt is cancelled. Actual: Food 100, reserve 0, Ready to Assign 200; total 300 against real 200. Excess 100 carries into May.
R3-3 — mixed cash/Card refund loses cash deficit on rollover (BLOCK)
March 1 Cash income 200; Food Assignment 50. March 10 Cash Food spend -100; March 11 Card Food purchase -100; March 20 Card Food refund +100.
Expected March: Cash 100, Card 0, Food -50, reserve 0, Ready to Assign 150. April must deduct the cash deficit: Food 0 and Ready to Assign 100; total 100.
Actual March matches the expected totals. April resets Food to 0 but keeps Ready to Assign 150; excess 50 persists in May. The monthly mixed pool includes the Card refund, treats the final cash deficit as credit and skips the Ready to Assign deduction. This contradicts DESIGN §48's cash-overspending rollover rule.
R3-4 — two-Card transfer into positive credit omits real money (BLOCK)
March 1 Cash income 200. March 11 general transfer 100 from zero-balance Card to zero-balance Other Card.
Accounts are Cash 200, Card -100, Other +100. DESIGN §48 states positive Card balances are real budget money. Expected total allocations 300 (the additional 100 must get a Category or Ready to Assign). Actual total remains 200 in March, April and May. The general-transfer rule says Category totals are unchanged; preserving that rule requires a Ready to Assign correction when a transfer crosses the Card credit boundary. No such correction occurs.
A transfer of existing debt between Cards preserves total money but leaves the reserve attached to the old Card, as the literal general-transfer rule specifies. This distribution is not counted as another conservation blocker.
Evidence
crates/calternal-money/tests/review_bulkrev3.rs: independent public codec/project/replay probes.artifacts/bulkrev3-probes.log: exact replay outputs. Probe tests collect incorrect outcomes without asserting that those outcomes are desired behavior.Independent bulkrev3 findings — #1393
Reviewed product head:
3e5fe2ae1c.All amounts are synthetic fixture cents. Product code is unchanged. Author report and result files were not read.
Previous witnesses
B1: both same-date permutations return 10,000 total Available plus Ready to Assign, against 10,000 real money, in March and April. Reserve is 0. Correct.
N2: all 24 permutations accepted with Cash MAX_EXACT, Card -1, Groceries Available MAX_EXACT-2, Dining 1, reserve 1 and Ready to Assign 0. Correct.
R3-1 — underfunded partial refund removes money (BLOCK)
March 1 Cash income 200; Food Assignment 50. March 10 Card Food purchase -100; March 20 Card Food refund +50.
After the purchase Food is -50 and payment reserve is 50. The refund cancels the unfunded half of the purchase. Expected after refund: Food 0, reserve 50, Ready to Assign 150; Cash 200, Card -50. Total allocations 200. Actual: Food 0, reserve 0, Ready to Assign 150; allocations 150 against real 200. Missing 50 persists in April and May.
DESIGN §48: credit overspending becomes debt; it does not spend real Cash. Here the returned debt was not funded. Releasing 50 Cash reserve while only healing negative credit Available removes money. The new unconditional refund release follows the new refund paragraph literally but conflicts with envelope conservation. The specification also needs to distinguish restoration of a current credit deficit from restoration of spendable Available.
R3-2 — later-month refund of unfunded debt creates money (BLOCK)
March 1 Cash income 200; no Assignment. March 10 Card Food purchase -100. March end Food -100, reserve 0, Ready to Assign 200. Credit deficit resets without reducing Ready to Assign under DESIGN §48. April 1 Card Food refund +100.
Expected April: Cash 200, Card 0, Food 0, reserve 0, Ready to Assign 200; total 200. No real money enters: only unfunded debt is cancelled. Actual: Food 100, reserve 0, Ready to Assign 200; total 300 against real 200. Excess 100 carries into May.
R3-3 — mixed cash/Card refund loses cash deficit on rollover (BLOCK)
March 1 Cash income 200; Food Assignment 50. March 10 Cash Food spend -100; March 11 Card Food purchase -100; March 20 Card Food refund +100.
Expected March: Cash 100, Card 0, Food -50, reserve 0, Ready to Assign 150. April must deduct the cash deficit: Food 0 and Ready to Assign 100; total 100.
Actual March matches the expected totals. April resets Food to 0 but keeps Ready to Assign 150; excess 50 persists in May. The monthly mixed pool includes the Card refund, treats the final cash deficit as credit and skips the Ready to Assign deduction. This contradicts DESIGN §48's cash-overspending rollover rule.
R3-4 — two-Card transfer into positive credit omits real money (BLOCK)
March 1 Cash income 200. March 11 general transfer 100 from zero-balance Card to zero-balance Other Card.
Accounts are Cash 200, Card -100, Other +100. DESIGN §48 states positive Card balances are real budget money. Expected total allocations 300 (the additional 100 must get a Category or Ready to Assign). Actual total remains 200 in March, April and May. The general-transfer rule says Category totals are unchanged; preserving that rule requires a Ready to Assign correction when a transfer crosses the Card credit boundary. No such correction occurs.
A transfer of existing debt between Cards preserves total money but leaves the reserve attached to the old Card, as the literal general-transfer rule specifies. This distribution is not counted as another conservation blocker.
Evidence
crates/calternal-money/tests/review_bulkrev3.rs: independent public codec/project/replay probes.artifacts/bulkrev3-probes.log: exact replay outputs. Probe tests collect incorrect outcomes without asserting that those outcomes are desired behavior.Eleven reference vectors
Independent integer/Decimal calculations from the input rows match every stored output field in all eleven vectors, including Category activity, Assignment, Available, funding state, Account balances, Card debt, reserve, uncovered debt and Ready to Assign. All use reserve=max(Dining purchase-payment,0), Food Available=S, Dining Available=S-B and Ready to Assign=-2S. See artifacts/bulkrev3-vectors.log. Production equality is also checked in the requested vectors gate.
Generated histories (first pass)
Seed 139303, 256 histories, six months per history (three active months followed by three separate empty rollover months), up to 60 rows. Two Cards, underfunded Assignments, purchases, refunds, payments, cash spends, split purchases/refunds and Card transfers. Every full-model same-date shuffle comparison passed. Raw account-leg sums match production balances. Of 768 empty-month allocation checks, 102 equal real budget money, 127 exceed it, and 539 fall short; delta ranges from -2783 to +929. These broad cases include overpayments and positive Card balances. The isolated witnesses above establish defects without treating every random discrepancy as a separate blocker.
R3-5 — spending positive Card credit creates reserve (BLOCK)
March 1 Cash income 200. March 10 categorized Card Food refund +50 with no prior debt; March 11 Card Food purchase -50. The refund creates real positive Card credit and Food Available 50. The purchase spends that credit, so expected final Cash 200, Card 0, Food 0, reserve 0, Ready to Assign 200; total 200. Actual reserve is 50, with Ready to Assign 200 and Food 0; total 250. Excess 50 persists in April and May. The purchase path funds a payment reserve even when the Card credit already paid for the purchase. This is another positive-credit boundary missed by the fully-funded property.
Final independent probe: 4 passed, 0 failed; its per-month raw account-sum check now covers all 1,536 monthly models (rather than final balances only). Same generated allocation counts as the first pass. The standalone probe used already-built test dependencies while the full Cargo gate was compiling. An initial direct compilation selected incompatible Serde feature builds; this diagnostic was corrected by selecting the matching previously-built dependencies. The final Cargo gate remains the authoritative build/test verification.
Final independent review head:
bafe854ce8fcef89d3f498a9e2f2ed2ea763e95a.Independent Money number review — bulkrev3, #1393
BLOCK: R3-1, R3-2, R3-3, R3-4 and R3-5. They remove or duplicate budget money.
The previous B1 and N2 cases now pass. All eleven changed reference vectors
remain correct.
Reviewed product head:
3e5fe2ae1c82f69ee26c8f9d56000db7b9cd26db.Branch:
job/bulkwrite-1393. The required fetch and merge oforigin/devreported
Already up to date.The author report and result files were notread. The previous review was read for its B1 and N2 inputs and independent
vector equations. All values in this report are synthetic fixture cents.
Method and invariant
The probes use the public Markdown codec, projection and replay. Exact
integer input values go through the existing Minor formatter and Assignment
editor. No product code, dependencies, lockfiles or existing test expectations
changed. No web code changed.
DESIGN §48 defines the envelope method: Assignments move
money from Ready to Assign to a Category; funded Card spending moves money to
the payment Category; a payment spends its reserve; cash overspending reduces
next month's Ready to Assign; credit overspending becomes uncovered debt.
Positive Card balances are real budget money. Negative Card balances are debt.
Hidden Categories retain their history and count in totals.
The universal account invariant is exact balance conservation: at each month
end, each Account balance equals its opening balance plus all its ordinary
legs and transfer legs through that month. Transfers sum to zero. This holds
regardless of Category funding. The generated test checks all 1,536 month
models against independent raw input sums.
For allocation conservation, define L as signed Category Available plus Ready
to Assign. Define M as Cash balances plus the positive part of each Card
balance. Import reconciliation adjustments must be added to M when present;
these fixtures have none. At a transaction-free next month after rollover,
there is no new unfunded credit deficit. The strongest allocation invariant
for these Cash/Card histories is L = M. It must hold for underfunded
histories too: virtual credit deficits have reset and cash deficits have
reduced Ready to Assign. No debt is counted as negative budget money and no
payment reserve is counted twice. This test avoids the incorrect comparison
with the signed sum of all Account balances. At a month with current credit
overspending, L needs a credit-deficit correction; that temporary discrepancy
alone is not a defect.
Same-date shuffle independence holds for both acceptance and the complete
MonthTotals model. It is independent of money conservation: a model can be
consistently wrong in every file order.
Previous cases and reference vectors
B1 uses the previous amounts, dates and posting types: Cash income 20,000; Food and Dining
Assignments 10,000 each; older Food purchase -10,000; Dining purchase -10,000;
same-date payment 10,000 and Food refund 10,000. Both same-date permutations
return Food 10,000, Dining 0, reserve 0, Ready to Assign 0, Cash 10,000 and
Card 0. March and April each contain exactly 10,000 real and allocated money.
N2 uses the exact previous values and Category IDs. M is MAX_EXACT,
9,007,199,254,740,991. Opening income and Food Assignment are M-1; the four
same-date rows are Cash Food -M, a Cash Split parent +1 with Food -1 and Dining
+2, Cash Food +M, and Card Dining -1. All 24 permutations are accepted and
return Cash M, Card -1, Food M-2, Dining 1, reserve 1 and Ready to Assign 0.
For each of the eleven vectors, S is the Food and Dining Assignment,
1,000,000,000,000. A is the Food purchase and full refund, B the Dining
purchase, P the payment. Dining precedes Food in canonical order. The final
reserve is max(B-P,0); Food Available is S; Dining Available is S-B; Ready to
Assign is -2S; Cash is -P; Card is P-B; Savings and uncovered debt are 0.
Food activity is 0, Dining activity is -B, and payment activity is -P.
The independent calculation checked every stored output field, including
funding state, empty targets and marker Categories, not just the reserve.
Production replay equality is checked by the crate's vectors gate.
Blocking cases
Every case starts with March 1 Cash income 200. No import adjustment is used.
Unless stated otherwise, other Categories and the second Card have zero
balances. The evidence also replays empty following months.
R3-1: a partial refund of an underfunded purchase removes money. Food
Assignment 50; March 10 Card Food purchase -100; March 20 Card Food refund
+50. Before the refund Food is -50 and reserve is 50. The refund cancels the
unfunded half of the purchase. Expected Food 0, reserve 50, Ready to Assign
150, Cash 200 and Card -50: allocations 200. Actual Food 0, reserve 0 and
Ready to Assign 150: allocations 150. The missing 50 persists in April and
May. The refund releases real reserve while it only heals a virtual credit
deficit. The new refund paragraph, applied literally, conflicts with the
envelope rule here; both code and specification need this distinction.
R3-2: a later-month refund of unfunded debt creates money. No Assignment;
March 10 Card Food purchase -100; April 1 Card Food refund +100. March Food
-100 is a virtual credit deficit and resets without reducing Ready to Assign.
Expected April Food 0, reserve 0 and Ready to Assign 200, Cash 200 and Card 0:
allocations 200. Actual Food 100 and Ready to Assign 200: allocations 300.
Excess 100 persists in May. Cancelling unfunded debt did not bring money into
an Account or release any funded reserve.
R3-3: a mixed cash/Card refund loses the cash deficit at rollover. Food
Assignment 50; March 10 Cash Food spend -100; March 11 Card Food purchase -100;
March 20 Card Food refund +100. March correctly ends with Cash 100, Card 0,
Food -50, reserve 0 and Ready to Assign 150. §48 requires the cash deficit to
reduce April Ready to Assign to 100 as Food resets to 0. Actual April and May
Ready to Assign remain 150: excess 50. The mixed monthly pool includes the
Card refund and classifies the remaining cash deficit as credit.
R3-4: a two-Card transfer into positive credit omits budget money. No
Assignment; March 11 general transfer 100 from the zero-balance Card to the
zero-balance Other Card. Cash is 200, Card -100 and Other +100. Under §48's
explicit positive-Card rule, real budget money is 300. Allocations remain 200
in March, April and May: missing 100. The general-transfer rule keeps Category
totals unchanged, so a Ready to Assign correction is required to satisfy both
rules when a Card transfer crosses zero. The code applies neither correction.
The owner should confirm how these two §48 rules combine; this review applies
the stated positive-Card definition consistently.
R3-5: spending positive Card credit creates a payment reserve. No
Assignment; March 10 Card Food refund +50 with no prior debt; March 11 Card
Food purchase -50. The refund creates Food Available 50 and positive Card
credit 50. The purchase spends that credit. Expected final Cash 200, Card 0,
Food 0, reserve 0 and Ready to Assign 200: allocations 200. Actual reserve 50:
allocations 250. Excess 50 persists in April and May. No debt remains for this
reserve to pay. This funded purchase is a further counterexample to an
unqualified fully-funded conservation claim when positive Card credit exists.
Other requested cases
unknown category block id: food; no totals are returned. No deletion route was changed or tested.Generated histories
Seed 139303; 256 histories; up to 60 rows each; six months per history.
January, March and May contain activity; the following empty months check
rollover. Each history includes zero or small Assignments and draws from Card
purchases/refunds, payments, Cash spending, split Card purchases/refunds,
second-Card purchases and two-Card transfers. Broad accepted inputs include
payments larger than debt and positive Card balances.
All 256 complete-model shuffle comparisons pass. All 1,536 monthly raw Account
balance checks pass. Of 768 empty-month allocation checks, 102 satisfy L=M,
127 have excess allocations and 539 have missing allocations. The delta is
between -2,783 and +929. These counts are not separate defect diagnoses. The
five small cases above establish the blockers without relying on a correction
for current-month credit overspending.
Built, files and known gaps
This review adds four independent Rust review probes and this report:
crates/calternal-money/tests/review_bulkrev3.rs.tests/money/bulkrev3-review.md.Findings were appended during the review in
artifacts/bulkrev3-findings.md. Probe outputs, vector calculations and gatelogs remain in
artifacts/; they are not committed. Product code is unchanged.The five blocking cases are not fixed because this job is read-mostly.
Probe tests collect discrepancies as evidence; green probe results do not
approve the incorrect numbers. Existing expectations remain unchanged.
No new UI, route or background job was built, so no UI screenshots or new
performance profile applies. No secrets were displayed.
Decisions
No product design was changed. The review uses §48's Cash-plus-positive-Card
rule, rather than signed net Accounts, for real budget money. Empty next months
remove the current-credit-deficit ambiguity without excluding underfunded
histories. Missing Category identities are reviewed as invalid source files;
history deletion is forbidden. R3-1 and R3-4 expose specification combinations
that the owner must clarify; they do not justify changing product rules in a
review job.
Verification
Cargo used CARGO_PROFILE_DEV_DEBUG=line-tables-only, CARGO_INCREMENTAL=0,
CARGO_BUILD_JOBS=4 and TMPDIR=/target/tmp. The preset CARGO_TARGET_DIR
was not changed. No crate/package version was introduced or changed.
Review test head:
8ca3a9f95. The final issue comment records the report commit SHA.cargo fmt --check: exit 0, no output.cargo clippy -p calternal-money --all-targets -- -D warnings: exit 0.Output, verbatim:
cargo test -p calternal-money --test review_bulkrev3 -- --nocapture --test-threads=4: exit 0. Final result, verbatim:cargo test -p calternal-money -p calternal-plugin-money -- --test-threads=4: exit 0. Complete output, verbatim:Six existing ignored tests remain unchanged. Four are performance/growth profiles. The other two are child checks executed by their parents. No suite was restarted.
cargo clean: exit 0. Output, verbatim:The temporary standalone probe binary was deleted. No web build output was created. The source doc comments were read again before this report.
BLOCK — R3-1 underfunded partial refunds remove money; R3-2 later-month unfunded refunds duplicate money; R3-3 mixed cash/Card refunds lose the cash deficit at rollover; R3-4 transfers into positive Card credit omit budget money; R3-5 spending positive Card credit creates payment reserve.
bulkfix3 started on
job/bulkwrite-1393, headbafe854ce8fcef89d3f498a9e2f2ed2ea763e95a. Step 0 runs the independent R3 Markdown inputs against detachedorigin/devatcc1af3c12intarget/tmp/devcheck. No product code has changed. Cargo uses the preset target directory and four build jobs.bulkfix3 Step 0 — before product changes
Branch
job/bulkwrite-1393, initial headbafe854ce8fcef89d3f498a9e2f2ed2ea763e95a. Tested detachedorigin/devatcc1af3c12b68ad26c8403adde1be39c096b9ddea. Copied the independentreview_bulkrev3.rsinto that checkout and rancargo test --manifest-path target/tmp/devcheck/Cargo.toml -p calternal-money --test review_bulkrev3 envelope_edge_cases -- --nocapture. All inputs used the public codec, projection and calculator. Values below are synthetic test minor units.All five reproduce on dev and on the reviewed branch. None was introduced by this branch. Full baseline output remains in
artifacts/dev-r3.log. All five will be fixed, including the §48 refund and transfer distinctions needed for conservation.bulkfix3 arithmetic finding
The initial focused R3 run passes all four tests, including all five corrected witnesses, B1 (2 permutations), N2 (24 permutations), and seed 139303 with 256 histories / 1,536 Account models / 768 post-rollover conservation checks. Output:
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.88s.The defect source is the same in dev: refunds restore their full amount and release reserve even when they only cancel unfunded debt; the mixed cash pool counts every Card inflow as cash funding; positive Card spending moves money into reserve as if it created debt; transfers lack a positive-credit budget correction.
Decisions in §48: current-month unfunded refunds heal virtual deficits first; retired unfunded debt refunds add no Available except funded reserve release and new positive credit; positive Card credit spending uses cash-like money; general zero-crossing transfers add the change in Cash plus positive Cards to Ready to Assign. Payment transfers keep their payment Category move and put the remaining real-money change in Ready to Assign. Conservation is
L + U = Mduring the month andL = Mafter rollover, with cumulative import Ready to Assign adjustments added to M. Payment Categories are included once in L.The property is being extended to random dates, both Card transfer directions, Cash refunds and later Assignments. Full touched-crate gates are in progress. No API, dependency or UI changes.
bulkfix3 existing expectation review
The 11 independently reviewed refund vectors pass unchanged. The R3 policy changes also affect two pre-existing source records and two numeric pins in the 40-row shuffle witness. No input, status code or shuffle assertion changed. No divergence exemption was added.
properties.rs, 40-row witness: March Card refunds 9,472 cancel older unfunded debt, yielding no new Category money. March Food = 15,000 + 432 - 1,768 - 2,813 = 10,851. April Food = 10,851 + 6,775 - 9,046 + 2,572 = 11,152 and reserve = 9,046 - 2,572 = 6,474. October cash activity = -22,049, so cash-first Card funding = 11,152 + 15,000 - 22,049 = 4,103. Its refund 7,133 heals current unfunded spending without reserve release. Reserve = 6,474 + 4,103 = 10,577 (old pin 18,571). Raw final debt is 51,911, so uncovered = 41,334 (old pin 33,340). The test comment carries this calculation.901a5dc38f13b647: February refunds cancel retired unfunded debt (R3-2). No Assignment or positive reserve exists. Fees Available = -15,151; tax = -(2,727 + 182 + 400) = -3,309; retail and reserve = 0. Raw debt 366,524 is fully uncovered. Category Activity remains the full source amounts.e6d77a36b9ded73d: payment 20,000 against Amex debt 9,853 creates positive credit 10,147. Ready to Assign must retain this money: 590,000 + 10,147 = 600,147. Other values remain unchanged. This is the positive-Card-credit transfer rule (R3-4).These are explicit behavior changes required by the R3 fixes, rather than weakening tests to pass. A new focused test
reviewed_r3_vectors_assert_retired_debt_and_positive_creditchecks the two source records against the hand calculations. All unrelated labels and stored expectations are retained.bulkfix3 progress: main conservation fix committed as
9d8bd5fb73914265bf9127f6a7cf537880aab6b2; Tracking rollover guard committed as1e3e418ba. The guard preserves the prior off-budget deficit reset and has a two-month regression,tracking_deficit_rollover_does_not_reduce_ready_to_assign, which passes. No existing Tracking expectation changed.Final fetch and merge of origin/dev (
cc1af3c12) reportedAlready up to date.Both requested crates passed Clippy:The combined test gate is compiling the plugin test profile. The Money-only full suite, all five R3 assertions, B1/N2, all 625 stored records, the unchanged 11-vector check and the expanded conservation property already passed before the final two-crate gate.
Money conservation fixes — bulkfix3, #1393
Fix R3-1 through R3-5 in the shared full-Ledger and streamed-import replay.
All amounts in review evidence are synthetic fixture minor units.
Baseline
Step 0 ran the five independent Markdown inputs against detached origin/dev
cc1af3c12b68ad26c8403adde1be39c096b9ddeabefore product changes. All fiveare PRE-EXISTING. The table was posted on #1393 before editing.
Built and files
crates/calternal-money/src/budget.rs: track current virtual deficits byCard and Category. Refunds heal those deficits before releasing reserve.
Retired unfunded debt refunds do not create Available. Positive Card credit
is spent directly. On-budget transfers account for zero crossings. Rollover
deducts only cash deficits and keeps the existing Tracking contract.
crates/calternal-money/tests/review_bulkrev3.rs: turn all five witnessesinto exact correct-result assertions. One seeded general property,
random_histories_conserve_money_after_every_rollover, checks 256 histories,1,536 Account models, 768 post-rollover allocation sums and complete-model
shuffle independence. Random histories include underfunding, overspending,
later refunds, positive credit, both Card transfer directions, splits, Cash
refunds, later Assignments and payments to both Cards.
crates/calternal-money/tests/properties.rs: keep the 40-row shufflewitness and update only its two R3-dependent amount pins, with a hand
calculation in its doc comment.
crates/calternal-money/tests/vectors.rsandcontracts/vectors/money/money.v1.json: correct two other records affectedby the explicit R3 rules and assert their hand-computed results. All 11
previously reviewed refund vectors remain unchanged.
crates/calternal-money/tests/account_kinds.rs: guard the existing Trackingdeficit rollover rule with a two-month replay.
docs/DESIGN.md§48: state the exact refund, positive-credit, transfer andconservation rules.
Decisions
§48 was incomplete for the R3 cases. Current-month unfunded refunds heal
virtual deficits without releasing funded money. Canceling retired unfunded
debt adds no Available, except for funded reserve released or new positive
credit. Positive Card credit spending uses real budget money. General
on-budget transfers leave Categories unchanged and put their real-money
change in Ready to Assign. Payment transfers keep their payment Category
move and put the remaining real-money change in Ready to Assign.
For these Cash-income and on-budget expense/refund/payment/transfer histories,
let L be Ready to Assign plus all signed Category Available, including payment
Categories once. Let M be Cash balances plus positive Card balances. Let U be
remaining current-month unfunded Card spending. Before rollover, L + U = M.
After rollover, L = M. Add cumulative import reconciliation adjustments to M
when present. Tracking import rules remain unchanged.
Expected replay cost is the existing O(T log T) canonical sort plus bounded
passes over rows, Categories and touched Card/Category pairs. The new deficit
state uses O(K) memory for K touched pairs and resets each month. A transfer
reads only its two Accounts. No new per-request Home scan, IO, dependency,
route, background job or UI operation was added. No benchmark or UI evidence
is required by the current verification policy for this correctness slice.
Expectation changes
No input, status code, blanket divergence exemption or shuffle assertion
changed. Two amount pins and two stored records changed because this issue
explicitly changes their refund or positive-credit behavior:
March carry is 10,851; April carry is 11,152 with reserve 6,474; October
cash-first funding is 4,103. Its refund heals virtual credit spending.
Reserve = 6,474 + 4,103; uncovered = raw debt 51,911 minus that reserve.
901a5dc38f13b647: later reversals cancel retired unfunded debt. Februaryreserve and retail Available stay zero; Fees are -15,151 and Tax is -3,309.
Raw debt 366,524 is fully uncovered.
e6d77a36b9ded73d: payment 20,000 against debt 9,853 creates credit 10,147.Ready to Assign = 590,000 + 10,147 = 600,147. Other fields stay unchanged.
The calculations are stated in tests and in the issue comment.
Known gaps and merge round
No requested R3 gap remains. Existing ignored performance/child tests stay
ignored; their counts are quoted in the gate output. No API or web code changed.
The full real-server adversarial matrix, release/staging checks and the
original throughput work stay with the merge round under the current verification policy.
Use the merged server and matching web build. For the merge round:
bun tests/adversarial/money_api.mjsmust confirm thereal API still enforces authorization, exact amounts and durable batch
isolation with the corrected replay.
tests/adversarial/run.shcovers thefull cross-User and protocol matrix. This job runs the focused regressions
and the finite seeded conservation campaign instead. No push, deploy or
promotion was made. The origin/dev merge reported
Already up to date.Verification
Cargo used CARGO_PROFILE_DEV_DEBUG=line-tables-only, CARGO_INCREMENTAL=0,
CARGO_BUILD_JOBS=4 and TMPDIR=/target/tmp. The preset CARGO_TARGET_DIR
was not changed. No dependency version changed. Build output is cleaned at
job end. Module and function comments were read before the final report.
The main fix is
9d8bd5fb73914265bf9127f6a7cf537880aab6b2. The Trackingregression is
1e3e418ba19494f67ce7882d3df8f545491a60ed. The final reportcommit also clarifies doc comments for positive Card credit spending; it
changes no Rust test or implementation body.
The final combined gates passed. B1, N2, all 625 stored cases, the unchanged
11-vector check, raw Account checks and both core and real-TCP API shuffle
checks pass. The API property covers serial, shuffled, eight-client and burst
writes in three client zones. Six existing ignored tests remain ignored.
cargo fmt --check: exit 0; no output.cargo clippy -p calternal-money -p calternal-plugin-money --all-targets -- -D warnings:exit 0. Complete output, verbatim:
cargo test -p calternal-money -p calternal-plugin-money -- --test-threads=4:exit 0. Complete output, verbatim:
cargo clean: exit 0. Output, verbatim:The scratch origin/dev checkout was removed. Both
apps/web/buildandapps/web/.svelte-kit/outputare absent. Text logs stay in ignoredartifacts/; no screenshots, videos or build output were committed.Final head:
f39bf92bf3; R3-1 FIXED; R3-2 FIXED; R3-3 FIXED; R3-4 FIXED; R3-5 FIXED; property: random_histories_conserve_money_after_every_rolloverIndependent Money correctness review round 4 started. Branch job/bulkwrite-1393; base/reviewed head
f39bf92bf3. Read binding docs, DESIGN §48, issue body and prior independent reviews; author reports and result files excluded. Product code stays unchanged. Testing raw-account balance conservation, corrected Category conservation, same-date shuffles, four changed pins/records, and wider randomized histories.Independent bulkrev4 review: BLOCK on two accepted-file conservation gaps.
R4-1 — Positive Card opening credit disappears (BLOCK)
Smallest input: no Assignments; one Opening balances row, Card +1 cent on 2026-01-01; empty February. The Opening balances Category has kind:: balance. §48 defines positive Card balances as real money. Correct: Card +1, Ready to Assign +1, all Available 0, L = M = 1 in both months. Actual: Card +1, Ready to Assign 0, all Available 0, L = 0 and M = 1. The one cent remains absent after rollover. Replay::post handles Balance only for Cash, and ignores positive Card credit. This is accepted through the Markdown codec/projection, with a linked valid payment Category. It is an adjacent pre-existing gap, not a demonstrated regression in the R3 fix.
R4-2 — Card without a payment Category removes funded money (BLOCK)
Smallest funded witness: Cash income +1; Food Assignment +1; Card Food purchase -1; empty next month. Omit the optional Card payment category:: property. Core projection accepts the Card. Correct: either reject this metadata before replay or retain the 1 real cent in a payment Category. Actual: Cash 1, Card -1, Food 0, Ready to Assign 0, no Card reserve. L = 0 and M = 1, also after rollover. Replay::post computes funded = 1 and U = 0, but only adds funded to Available inside Some(payment Category). Thus money disappears. This accepted metadata is not sampled by either generator. This is an adjacent pre-existing gap, not a demonstrated regression in the R3 fix.
Wider histories — first result
Seed 139304, 512 histories: two Cards; both Card transfer directions; 20 posting forms; four consecutive active months; same-date shuffle; mixed-sign Card and Cash splits; repeated Category children; real visibility:: hidden; arbitrary debt-relative refunds; expense and payment Assignments. Raw Account sums, full shuffled models, and all 2,048 empty-prefix rollovers passed. The earlier round 3 hidden probe uses hidden:: true, which is an opaque property, not the real visibility:: hidden field. The new probe asserts the parsed visibility before comparing totals. Missing Category identities reject.
Independent bulkrev4 progress, head
86a16b87d6. Product head remainsf39bf92bf3.Evidence: 512 wider histories passed 2,560 month models and 2,048 prefix rollovers, complete shuffle equality, raw Account sums and active-month L <= M. Thirteen explicit envelope edge cases passed full hand-computed fields. All eleven original vector outputs passed the independent integer script; pins 10,577 and 41,334 and revised vector records 901a5dc38f13b647 / e6d77a36b9ded73d are correct. The Money core gate passed, including B1 2/2 and N2 24/24 permutations. Plugin gate remains in its Card model test; bulk_write_balances_equal_one_by_one has passed.
R4-1 and R4-2 remain BLOCK: accepted positive Card opening credit and accepted unlinked Card purchases each lose one cent in their smallest witness. Both generated initialization/metadata extensions shrink to n = 1. No product code or old test expectation changed. Independent probe/numeric-check slices are committed. Final report will include both complete gate outputs verbatim.
Independent bulkrev4 final report. Head
82087a0163a30f66a9bd190ad2e67db412f760c7. Reviewed product headf39bf92bf37a7538c97f6ed2fd593af388e7e482. Verdict: BLOCK R4-1, R4-2.Independent Money number review — bulkrev4, #1393
BLOCK: R4-1 and R4-2. Both accepted inputs remove real budget money.
Reviewed product head:
f39bf92bf37a7538c97f6ed2fd593af388e7e482.Branch:
job/bulkwrite-1393. Required fetch/merge oforigin/dev:Already up to date.The author reports and result files were not read. Only prior independent reviews were used. All amounts are synthetic test cents. No product code, dependency, lockfile or existing expectation changed. The probe/evidence commit is86a16b87d60725175842363c29112c91d7f05103; the final issue comment identifies the report commit.Built and files
tests/money/bulkrev4-probe.rs: standalone exact-number probes, 512 wider generated histories, 13 hand-computed edge cases, bounds, real hidden visibility and two known-failing shrink properties.tests/money/bulkrev4-probe.sh: compile and run the probe against crate-gate artifacts. Run after the Money gate. Known failures need--ignored.tests/money/bulkrev4-numbers.py: independent integer checks of every stored field in the eleven original vectors, both burst pins and both revised vector records.tests/money/bulkrev4-review.md: this review and verbatim gate evidence.artifacts/bulkrev4-findings.md: findings appended during the review. It stays gitignored.Invariant and scope
DESIGN §48 defines M as Cash balances plus positive Card balances. L is Ready to Assign plus all signed Category Available, including payment Categories once. U is current-month unfunded Card spending that refunds or real Category inflows have not canceled. For Cash income and the on-budget expense, refund, payment and transfer forms under review, L + U = M. Add cumulative import Ready to Assign adjustments to M when present. At rollover U resets. Only the cash part of a negative Available reduces Ready to Assign. With no new transactions, L = M, even after underfunded histories. Negative Card balances are debt, not negative budget money.
Each Account also equals the exact raw sum of its ordinary and transfer legs through each month. These checks do not use derived reserves to predict Account balances. Full same-date models and acceptance must be independent of file order.
The widened generator checks raw Account sums and
L <= Mat every month, plus exactL = Mafter every prefix rollover. The hand cases also checkL + U = Mwith independently known U. U is private replay state, so the random probe does not independently assert its exact current-month value. That is a test limit, not a claim that arbitrary current-month differences are defects.BLOCK R4-1: positive Card opening credit disappears
Minimal accepted input: one active Card with a valid payment Category; no Assignments; one Opening balances row
+0.01 USDon January 1; empty February. The Category haskind:: balance.Correct under §48: Card +1; Ready to Assign +1; all Available 0. L = M = 1 in January and February. Actual: Card +1; Ready to Assign 0; all Available 0. L = 0, M = 1. The loss survives rollover. The
create_accountroute accepts a positive Cardopening_balance, creates its payment Category and writes this Opening balances row. Thus the route source permits the same failing input. This reachability statement is an inference from source, not a separate live HTTP probe.Replay::postreturns from the Balance branch without crediting positive Card balances. The propertygenerated_opening_credit_conservesshrank ton = 1. This is an adjacent pre-existing gap. This review does not claim the R3 patch introduced it. It still meets the requested BLOCK rule: accepted input loses real budget money.BLOCK R4-2: missing payment link removes funded money
Minimal funded input: Cash income +1; Food Assignment +1; Card Food purchase -1; empty February. Omit the Card's optional
payment category::property. The public codec/projection accepts that metadata.Correct: reject the unusable Card metadata before calculating, or retain the real cent in its payment Category. Actual: Cash 1; Card -1; Food 0; Ready to Assign 0; no Card reserve. L = 0, M = 1 in both months.
Replay::postcomputes one funded cent and no virtual deficit, but adds reserve only when a payment Category exists. The cent is removed from Food without a destination.generated_unlinked_card_conservesshrank ton = 1. This is also an adjacent pre-existing gap, not a demonstrated regression of the R3 patch.Previous B1, N2 and changed numbers
B1 passes both same-date permutations. Cash income 20,000; Food and Dining Assignments 10,000 each; Food purchase -10,000 on March 28; Dining -10,000 on March 29; payment 10,000 and Food refund 10,000 on March 31. Correct final Food 10,000; Dining 0; reserve 0; Ready to Assign 0; Cash 10,000; Card 0. March and April each allocate exactly 10,000.
N2 passes all 24 same-date permutations. Let E = MAX_EXACT = 9,007,199,254,740,991. Cash opening income and Food Assignment are E-1. Same-date Cash Food -E; Cash split parent +1 with Food -1 and Dining +2; Cash Food +E; Card Dining -1. Correct Cash E; Card -1; Food E-2; Dining 1; reserve 1; Ready to Assign 0. Canonical pre-pass order accepts all orders.
Both changed burst pins are correct. March Card refunds = 361 + 1,648 + 7,463 = 9,472 cancel old debt. March Food = 15,000 + 432 - 1,768 - 2,813 = 10,851. April Food = 10,851 + 6,775 - 9,046 + 2,572 = 11,152; reserve = 9,046 - 2,572 = 6,474. October Cash activity = -22,049, leaving 11,152 + 15,000 - 22,049 = 4,103 for Card funding. Card purchases total 19,230. The 7,133 refund cancels current virtual deficit and does not release reserve. Correct reserve = 6,474 + 4,103 = 10,577. November raw debt = 51,911; correct uncovered = 51,911 - 10,577 = 41,334.
Changed record
901a5dc38f13b647is correct. February returns 50,505 retail, 1,768 fees and 318 tax reduce retired unfunded debt. No positive reserve exists. February Available: retail 0; fees -15,151; tax -(2,727 + 182 + 400) = -3,309; interest -(1,010 + 2,222) = -3,232; payment 0. Raw debt and uncovered are 366,524. Correct reserve is 0.Changed record
e6d77a36b9ded73dis correct. Cash openings and income = 1,150,500; Assignments = 560,500; Amex purchases = 9,853; payment = 20,000; new positive credit = 10,147. Correct Ready to Assign = 1,150,500 - 560,500 + 10,147 = 600,147.All eleven earlier reference vectors remain correct, including every stored output field. S = 1,000,000,000,000 is assigned to both Food and Dining. A is Food purchase/full refund, B is Dining purchase, P is payment. Canonical order puts Dining before Food. Reserve = max(B-P,0); Food = S; Dining = S-B; Ready to Assign = -2S; Cash = -P; Card = P-B; Savings and uncovered are 0. Food activity = 0; Dining activity = -B; payment activity = -P. The Python check derives these numbers from input fields and constructs the full output. The Rust vectors gate independently checks production replay equality.
Requested envelope cases
Each case starts with Cash income 200. Unless stated otherwise, Dining and the Other Card stay at zero. All rows below match production in March, April and May.
visibility:: hiddenparses as hidden and retains the same totals as visible.Generator coverage
The existing seed-139303 property has two Cards, both Card transfer directions, refunds unconstrained by debt, later refunds, Cash refunds, Card splits and three active months. It inserts an empty month after every active month. It does not exercise consecutive active months, mixed-sign splits, Cash splits, repeated children, real hidden visibility, payment Assignments, opening Card credit or a missing payment link. The earlier hidden check uses opaque
hidden:: true; the new probe verifies actual parsedvisibility:: hidden.The new seed-139304 property ran 512 histories with up to 64 random rows, four consecutive active months, two Cards, 20 posting forms, Cash and Card purchases/refunds, split purchases/refunds, mixed-sign Cash/Card splits, repeated Category children, both Card transfer directions, overpayments, arbitrary debt-relative refunds, hidden Categories and positive/negative expense/payment Assignments. It checked 2,560 month models, 2,048 prefix rollovers, raw Account sums and complete shuffled models. All passed in the linked-Card, zero-opening-balance domain. No generated money excess or loss was found in that domain.
Separate initialization/metadata extensions found R4-1 and R4-2. Both shrink to one cent. The failures survive rollover. They are outside the passing property's fixed metadata.
Exact bounds: i64::MIN, i64::MAX and values just outside ±MAX_EXACT reject. Supported ±MAX_EXACT round-trip and conserve in Cash histories. N2 checks acceptance at the supported bound in every same-date order. A zero-parent Card split with an internal cursor of 2 * MAX_EXACT also conserves. This is not exhaustive testing near every intermediate bound.
Decisions, known gaps and merge round
No product design decision was made. This review applies §48's positive-Card and allocation equations to every accepted input. The design does not state how a Card without a payment link must behave. R4-2 therefore accepts either an early validation error or correct reserve accounting as a remedy; silently losing money is not a valid result.
Known gaps: R4-1 and R4-2 remain unfixed because this job forbids product changes. The probes use the real codec/projection/replay, not new HTTP checks. Random U is not exposed, so exact current-month U is checked only in the hand cases. Tracking transfers, Card income, import reconciliation and every integer intermediate are not covered by the new random property. Existing gates cover their existing contracts; this review makes no new universal claim for them. No UI, performance route or background job was changed; no screenshots or new bench profile were needed. No dependencies were added.
For the merge round: do not merge until both accepted-file conservation gaps have a fix or an explicit owner disposition. Turn the two standalone shrink properties into normal regressions with the fixes. Rerun
tests/money/bulkrev4-probe.sh --ignored --nocapture --test-threads=1after the Money crate gate; both must conserve or reject unusable metadata. No requested correctness scenario was deferred.Gates and cleanup
Both required crates were tested once, separately, with
CARGO_BUILD_JOBS=4,CARGO_PROFILE_DEV_DEBUG=line-tables-only,CARGO_INCREMENTAL=0andTMPDIR=<worktree>/target/tmp. The configured CARGO_TARGET_DIR was not changed.calternal-money: 68 passed, 1 ignored.calternal-plugin-money: 162 passed, 5 ignored, plus the parent-invoked child check. Existing ignored tests were not changed. No suite was restarted. No crate code changed, so no new crate clippy gate was needed.cargo fmt --check: exit 0, no output. The standalone Rust probe was formatted with rustfmt. Shell syntax was checked and the independent Python arithmetic script ran successfully.Gate output below is verbatim. These green existing gates do not cover the two accepted-input defects.
cargo test -p calternal-moneycargo test -p calternal-plugin-moneypython3 tests/money/bulkrev4-numbers.pytests/money/bulkrev4-probe.sh --nocapture --test-threads=1artifacts/bulkrev4-probe --ignored --nocapture --test-threads=1 (exit 101, expected BLOCK witnesses)cargo cleanNo web output was created. The standalone probe binary was deleted. All changed module and function comments were read again before this report. No secrets were displayed. No push, deployment or merge into dev was performed.
BLOCK — R4-1 positive Card opening credit disappears; R4-2 accepted unlinked Card purchases remove funded money.
bulkfix4 starts on branch job/bulkwrite-1393 at
82087a0163. Baseline origin/dev iscc1af3c12b. I will classify R4-1/R4-2 on that baseline, then fix opening Card credit and missing payment-link conservation, with focused and generated regressions. No push or deploy.bulkfix4 baseline classification
Ran the unchanged round-4 Markdown observation probe as a temporary integration test on detached origin/dev
cc1af3c12b68ad26c8403adde1be39c096b9ddea, before product edits. All amounts are synthetic fixture minor units.Command:
cargo test --manifest-path artifacts/bulkfix4-dev/Cargo.toml -p calternal-money --test bulkfix4_baseline opening_credit_and_missing_payment_observations -- --nocapture. Result:test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in 0.02s. This observation test prints defects; it does not assert the incorrect totals. Both defects survive rollover, and neither was introduced by the R3 fixes.bulkfix4 R4-2 vector finding and decision
The full Money test run found exactly one changed record out of 625:
de7c249363a57c0c. Its Card omits its payment link, so the requested R4-2 rule (every accepted Card has a distinct payment Category) changes its report shape. The new Category iscard-payment-visa, Available 0; reserve 0; raw Card debt -4,950; uncovered debt 4,950. Food remains -4,950 and Ready to Assign remains 0. All amounts are synthetic fixture minor units. This is the explicit R4-2 behavior change, not a blanket oracle exception: update only those metadata/report fields and add an independent exact assertion. All prior B1/N2/R3 and eleven refund records remain unchanged.Decision for the unspecified legacy case: deterministic, idempotent repair in the parsed calculation/edit view; normal edits save repaired metadata, with Category-first publication before a new Account link. Reads never write. A payment-for marker keyed by stable Account ID lets repair reuse a Category after a partial save. Same-named Cards remain distinct; ID collisions use the existing suffix rule. §48 states this rule.
bulkfix4 core fix committed
Core commit
e2ce0a08d: conserve opening Card credit and repair missing payment Categories.cargo clippy -p calternal-money --all-targets -- -D warningspasses.cargo test -p calternal-moneypasses: 72 passed, 1 ignored. The 256-history property now generates signed openings on both Cards, missing links on either Card, raw Account sums, post-rollover conservation, full shuffle equality and a separate current-month Ready to Assign check from raw ordered legs and Assignments. The latter uses prior positive Category carry as a boundary input; it does not compute Ready to Assign from the current allocation sum. Both one-cent and 12,345-cent witnesses assert the correct values.The required single fetch/merge of origin/dev returned
Already up to date.Server editable-view and Category-first persistence regression tests are the remaining feature slice; the two-crate gates are running.bulkfix4 independent verification
The unchanged round-4 probe now passes with all formerly ignored conservation properties enabled:
tests/money/bulkrev4-probe.sh --include-ignored --nocapture --test-threads=1. Verbatim result:test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 26.18s. This includes the 512 wider histories and both one-cent shrink properties. January and February opening-credit allocations and real money are both 1; January and February unlinked-Card funded allocations and real money are both 1. The Python independent number script also passes every stored field in the eleven refund vectors and both prior corrected burst/vector results.Required two-crate Clippy gate passed. Plugin test binaries are building for the combined test gate; server document-view persistence is the remaining verification.
bulkfix4 gate finding
The combined plugin test gate aborted at the existing
tests::concurrent_store_edits_keep_exactly_one_winner:thread ... has overflowed its stack,fatal runtime error: stack overflow, aborting, SIGABRT. No expectation was changed. The new Category-first publication branch enlarged the shared async write future, including for ordinary Budget edits that do not take that branch. Move the cold repair/publication path to a private boxed async helper so the normal write future stays small. Run the exact failing test first, then resume the affected crate gate. This is a real blocker found and fixed in the job, not a SLOW finding.bulkfix4 stack and size-cap regressions fixed
The exact existing concurrent-write regression passes after boxing the cold Account repair publisher. The new multi-step legacy fixture then exposed another stack overflow before shared setup returned. Box the nested filesystem writer and the new fixture setup/write futures; do not change stack-size settings or remove assertions. The filesystem writers carry fixed IO buffers, so their async future sizes matter even before the guarded path runs.
Focused result, verbatim:
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 136 filtered out; finished in 6.43s. Both legacy regressions pass on the normal test stack. They prove that reads leave source bytes unchanged, repaired Categories remain editable, Category-first publication resolves new links, a partial save reuses its identity, retries leave equal Budget bytes untouched, original source hashes remain CAS authority, and a Budget at the 8 MiB reader cap refuses the repair before changing either file. No existing expectation changed.The final two-crate gate is running after these fixes. Earlier aborted outputs are retained in artifacts and will be disclosed in the final report.
bulkfix4 publication preflight
Resolving a linked ID is not sufficient: a stale Account edit could resolve its old payment link to an Expense Category after a Budget edit. The Category-first publisher now reuses replay metadata validation with an empty month list before writing either source. It rejects wrong kinds and shared payment links without replaying transactions. The focused legacy regression sends a known Expense target and proves both files remain unchanged, then verifies the normal valid publication, reload and editing flow. Verbatim result:
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 136 filtered out; finished in 13.59s. No existing expectation changed. The in-progress gate was superseded for this last publication check; the final gate is now running with it included.bulkfix4 server slice committed
Commit
bda6bc7eccdc2a4b6fe017831e67002e91a013c0makes repaired Category documents editable with original source CAS hashes, publishes new Categories before Account links, skips equal Budget bytes on retry, enforces the reader cap before publishing, and rejects stale Expense links before either file changes. It keeps the write futures small on the default test stack. Both focused legacy regressions pass, the positive-opening route passes in the full run, and the final two-crate Clippy gate passes. No existing status-code or fixture expectation changed.The final combined test gate has passed the new tests, the concurrent-edit regression, import, authorization, identity, recovery and Undo checks so far. Only the two existing bulk comparison properties remain in the plugin unit run. There are no failures in this final run. The source worktree is clean; this comment is progress, not a READY report. I will post full gate output and the final report after those properties settle.
bulkfix4 complete
HEAD:
cd622b94e212d88dd2e7292a796f2ad5f82df2ccMoney conservation fixes — bulkfix4, #1393
All amounts are synthetic test minor units. Both R4 defects are FIXED.
Code commits:
e2ce0a08db2a73ba748360766716e019bfef3697andbda6bc7eccdc2a4b6fe017831e67002e91a013c0. The final issuecomment gives the report commit HEAD. No push, deploy or promotion was made.
Baseline classification
Before product changes, the unchanged observation probe ran on detached
origin/dev
cc1af3c12b68ad26c8403adde1be39c096b9ddea. It used the samemethod as bulkfix3: real Markdown codec, projection and replay on the
baseline. The table was posted on #1393 before the fixes.
The baseline observation test passed because it prints the defects. It does
not assert the wrong amounts. Both defects survive rollover on origin/dev.
The required single fetch and merge of origin/dev gave
Already up to date.Built and files
crates/calternal-money/src/budget.rs: put the exact positive-creditchange from a Card opening in Ready to Assign. Debt adds no budget money.
Reject a hand-built Ledger with missing or invalid payment links before
replay can discard funded money.
crates/calternal-money/src/edit.rs: reuse the existing Category editor,ID collision rule, Markdown property writer and reference writer. Repair
missing Card payment links in stable Account ID order. A
payment formarker lets a partial save reuse its Category. Same-named Cards stay distinct.
crates/calternal-money/src/ledger.rs: apply that shared repair beforecalculation, including direct codec and streamed import callers.
crates/plugins/money/src/store.rs: expose repaired editable documentswith the original source hashes for CAS. Keep the shared parse cache
unchanged. Before an Account edit saves a repaired link, save its Category
first. Skip equal Budget bytes on retry. Reject a repair above the reader
size cap before any write. Validate a link's payment kind and uniqueness
before publication, not only whether its target exists.
crates/calternal-money/tests/review_bulkrev3.rs: normal regressions forone-cent and 12,345-cent R4 witnesses, signed and repeated openings,
refunds after rollover, repair collisions, duplicate display names and
partial-save reuse. Extend the 256-history property with signed openings
on both Cards and missing links on either Card. Keep raw Account sums,
full model shuffle checks and conservation after each rollover. Check
current-month Ready to Assign separately from raw ordered Account legs
and Assignments, with prior positive Category carry as the boundary input.
crates/plugins/money/src/tests.rs: verify the positive-opening route,editable repaired Categories, Category-first persistence, restart reuse,
source hash authority, unchanged source bytes on read and size-cap refusal.
crates/calternal-money/tests/vectors.rsandcontracts/vectors/money/money.v1.json: change only the R4-2 report fieldsin
de7c249363a57c0cand add exact assertions. Its unlinked Card now has azero-balance payment Category and uncovered debt 4,950. Food stays -4,950;
Ready to Assign stays 0. No vector input or divergence rule changed. All
eleven refund vectors and the previous R3 records and burst pins stay unchanged.
docs/DESIGN.md§48: state opening-credit and legacy payment repair rules.Decisions
Every accepted Card has one distinct payment Category. Use deterministic
repair in the parsed calculation and edit view for older files. Reads do not
write. Normal metadata edits save the repair with source hash checks. Save
new Categories before new Account links. A failure can leave an unused
Category, which its Account marker lets a retry reuse. Do not match by name.
This fills the R4-2 gap in §48 without a new route or background migration.
A Card opening adds only its positive-balance change to Ready to Assign.
This works across zero in both directions and with repeated adjustments.
Negative opening debt creates no Category money or Ready to Assign.
Verification and limits
The unchanged round-4 Rust probe passes with both formerly ignored shrink
properties enabled: six tests, including 512 wider histories. The independent
Python script still verifies every stored field in the eleven refund records
and the two previous corrected burst and vector results. B1, N2, R3-1 through
R3-5, shuffle independence and the 625-record vector test pass.
No requested conservation gap remains in the tested domain. The generated
current-month Ready to Assign check uses prior positive Category Available
as a boundary input; it is not a second independent Category funding engine.
Exact random current-month U remains private. The hand witnesses have known U.
Card income, Tracking and import reconciliation keep their existing coverage;
this report makes no new universal conservation claim for those forms.
There is no new UI control, route, background job or dependency. No UI
screenshot set or new throughput profile is part of this correctness slice.
Under the current verification policy, the original bulk throughput
measurements remain with the performance and merge rounds. Expected cost:
Card opening arithmetic is O(1) per leg. Healthy metadata repair scans Account
sections only. For K missing links it sorts K stable IDs and uses the existing
pure editors on the two metadata documents. It adds no Home scan or per-row IO.
For metadata of B bytes and K missing Cards, the existing editors do
O(K × B) byte work. The healthy path avoids copies of Budget.md.
Retained repair data is bounded by those documents and their Category markers.
UX gaps closed: the repaired Category is assignable and receives funded
purchases through the existing API. No new UI controls were built. UX gaps
left: none in this backend scope.
For the merge round
Run
bun tests/adversarial/money_api.mjsagainst the merged local server tocheck authorization, exact amounts and durable batch isolation with the
corrected replay. Run
tests/adversarial/run.shfor the full cross-User andprotocol matrices. Full web suites, release/staging checks, Mac interop and
the original bulk throughput acceptance stay with the merge round. This job
ran the focused route regressions and one finite round-4 campaign.
Gates
Final results: calternal-money 72 passed, 1 ignored; calternal-plugin-money
165 passed, 5 ignored, plus its parent-invoked memory guard child. The two
bulk comparison properties passed. Existing ignored tests stay unchanged.
Use CARGO_BUILD_JOBS=4, CARGO_PROFILE_DEV_DEBUG=line-tables-only,
CARGO_INCREMENTAL=0 and TMPDIR=/target/tmp. Keep the configured
CARGO_TARGET_DIR unchanged. The output below is verbatim.
The first combined plugin gate aborted in the existing concurrent-edit test
with a stack overflow. The new Account publication branch enlarged the
shared write future. Move that cold branch to a boxed private async helper.
The exact
concurrent_store_edits_keep_exactly_one_winnertest then passedon the normal test stack. A later gate reached a stack overflow in the new
multi-step legacy fixture, before shared setup returned. Box the nested
filesystem writer and the fixture's setup and write futures. Both legacy
regressions then passed on the normal stack (2 passed in 6.43s). No assertion
or input was removed. Restart the affected gates after these fixes.
A final publication check also rejects a stale link to an Expense Category
before either source file changes. Reuse replay's metadata validation with no
months; do not duplicate its link-kind or uniqueness rules. The focused legacy
tests pass with this check (2 passed in 13.59s). One in-progress gate was
superseded when this preflight was added; keep it in
artifacts/bulkfix4-tests-superseded.log. It is not a passing result.No stack-size setting or existing test expectation changed. Keep both aborted
runs in
artifacts/bulkfix4-tests-stack-abort.logandartifacts/bulkfix4-tests-legacy-stack-abort.log; neither is a passing result.cargo fmt --checkExit 0. No output.
cargo clippy -p calternal-money -p calternal-plugin-money --all-targets -- -D warningscargo test -p calternal-money -p calternal-plugin-money -- --test-threads=4Independent probe and arithmetic
tests/money/bulkrev4-probe.sh --include-ignored --nocapture --test-threads=1:python3 tests/money/bulkrev4-numbers.py:Cleanup
cargo clean: exit 0. Output, verbatim:The detached baseline worktree was removed. Both web build output directories
and this job's test temporary directory are absent. The standalone probe
executable was removed after verification. Text logs stay in ignored artifacts.
No screenshots, videos or build output were committed. The source worktree
was clean at code HEAD. This report is the final documentation commit.
R4-1/R4-2 FIXED; both PRE-EXISTING on origin/dev.
Final HEAD:
cd622b94e2; R4-1/R4-2 FIXED; both PRE-EXISTING.Independent bulkrev5 final number review started on job/bulkwrite-1393, base/head
cd622b94e2. Required origin/dev merge: Already up to date. Product code remains unchanged. Checking signed Card openings, legacy metadata repair, independent reference arithmetic and 2,000 generated histories with a new seed.bulkrev5 independent review
Start: job/bulkwrite-1393 at
cd622b94e2. Required origin/dev merge: Already up to date. No author reports or result files read.DESIGN §48 defines L + U = M; rollover with no new rows gives L = M. Repair is in-memory on reads; durable publication must save Categories before links.
Reference arithmetic: PASS. All 11 complete original vectors and earlier revised records remain correct. Since round 4, only de7c249363a57c0c changes: repaired card-payment-visa has zero Assignment/activity/Available and Funded state; Visa reserve map now contains 0 and uncovered map contains 4950. The exact -49.50 USD FX total is -4950 budget units; no Assignment means U=4950, L=-4950, M=0. All other fields and inputs are unchanged.
bulkrev5 independent review
Start: job/bulkwrite-1393 at
cd622b94e2. Required origin/dev merge: Already up to date. No author reports or result files read.DESIGN §48 defines L + U = M; rollover with no new rows gives L = M. Repair is in-memory on reads; durable publication must save Categories before links.
Reference arithmetic: PASS. All 11 complete original vectors and earlier revised records remain correct. Since round 4, only de7c249363a57c0c changes: repaired card-payment-visa has zero Assignment/activity/Available and Funded state; Visa reserve map now contains 0 and uncovered map contains 4950. The exact -49.50 USD FX total is -4950 budget units; no Assignment means U=4950, L=-4950, M=0. All other fields and inputs are unchanged.
Signed openings and metadata generator: seed 139305, 2,000 histories PASS (12,000 month models and 6,000 empty rollovers). Both Card opening signs and omitted links on either Card are generated. Independent Ready to Assign uses raw ordered account legs and positive Category carry, not the replay Ready to Assign. B1 2/2 and N2 24/24 same-date orders PASS.
Real-file repair PASS: 16 reads preserve source bytes and fingerprints in two Homes with identical IDs. A deliberately wrong Accounts.md CAS hash causes failure after the real Category publisher. A fresh handle calculates the valid partial Budget; retry reuses its payment-for marker and does not rewrite Budget.md. The other User remains unchanged. In-memory metadata repair does run during reads, as §48 requires; durable repair does not.
Gate progress: public positive Card opening route, editable Category-first legacy repair, source-cap refusal and vectors all pass in the required combined crate test. No conservation, wrong-number, read-write or cross-User finding has appeared. Final gate status and second 2,000-history generator remain pending.
Final bulkrev5 head:
74c58b72f590e3663b84751724f3119161c86689. READY — no blocking items.Independent Money number review — bulkrev5, #1393
READY. No conservation defect, wrong number, file change on read or cross-User effect was found.
Reviewed product head:
cd622b94e212d88dd2e7292a796f2ad5f82df2cc.Branch:
job/bulkwrite-1393. The fetched dev base iscc1af3c12b68ad26c8403adde1be39c096b9ddea(also the merge base). Requiredgit fetch origin && git merge origin/dev:Already up to date.Review code head:83bc8d71ee567608a15f27f4436c07f6e4230d4c. The final issue comment records the final report commit. Product code, dependencies, lockfiles and existing expectations were unchanged. Author reports and result files were not read. Prior independent reviews and their probes were reused. All amounts below are synthetic fixture minor units.Built and files
tests/money/bulkrev5-numbers.py: independently checks all eleven original vectors and every changed field in this round.tests/money/bulkrev5-probe.py: compiles prior probes against the real gate-built crates, with seed 139305 and 2,000 histories per generator. It changes only standalone copies' generator configuration. Rustc checks dependency metadata compatibility because cargo builds feature-distinct library artifacts.tests/money/bulkrev5-store.rs: real-file probe for reads, interrupted Category-first publication, retry and User isolation.tests/money/bulkrev5-review.md: this review, with verbatim gate evidence.artifacts/bulkrev5-findings.md: findings appended during review; stays gitignored.Invariant and comparison
DESIGN §48 defines M as Cash balances plus each positive Card balance, L as Ready to Assign plus all signed Category Available (payment Categories once), and U as current-month unfunded Card spending not canceled or covered. For the documented Cash-income/on-budget expense/refund/payment/transfer histories, L + U = M. Import reconciliation adjustments add to M. At rollover, negative Available resets; only its Cash part reduces Ready to Assign. With no new rows, L = M, including after underfunded histories. Every Account also equals its exact raw ordinary and transfer leg sum. Same-date input order must not change acceptance or the complete model.
The hand cases check exact Category distribution and known U. The random probes check raw Account sums, complete shuffled models and exact rollover conservation. The consecutive-month probe also checks L <= M in every month. The opening/legacy probe independently calculates current-month Ready to Assign from ordered raw Account legs, Assignments and positive Category carry. Private replay U is not independently exposed by random tests; an exact random current-month U oracle was not added. These checks cover excess allocations and losses that survive rollover, without pretending that every active-month L < M is a defect.
B1 passes both same-date orders: Cash income 20,000, Food and Dining Assignments 10,000 each, purchases 10,000 each, then payment 10,000 and Food refund 10,000 on March 31. Correct Food 10,000, Dining 0, reserve 0, Ready to Assign 0, Cash 10,000 and Card 0. March and April each allocate 10,000.
N2 passes all 24 orders. E = MAX_EXACT. Cash opening and Food Assignment E-1; same-date Cash Food -E, Cash split Food -1/Dining +2, Cash Food +E, Card Dining -1. Correct Cash E, Card -1, Food E-2, Dining 1, reserve 1 and Ready to Assign 0. The pre-pass accepts every permutation.
Requested envelope cases
Each case starts with Cash income 200. The following §48 values match the real replay. Other Category/Card values are zero unless stated.
visibility:: hiddenparses as hidden and retains the same totals as visible.Final-round openings and repair
Positive Card openings enter Ready to Assign by the exact change in positive Card balance; debt openings add zero. One-cent and larger positive/negative fixtures pass. Repeated adjustments +n, -2n, +3n cross zero twice and give final Card credit and Ready to Assign 2n, with zero reserve. The fresh-seed generator includes signed openings on both Cards and omitted links on either Card. The public opening route also passes.
Repair is deterministic and idempotent. Existing independent collision tests pass with two same-named Cards and a taken generated ID. Fully repaired metadata needs no further repair. Budget-only persistence reuses the
payment formarker. Source order does not change allocated Category identities.The independent real-file probe uses two Homes with identical Budget and Account IDs. Sixteen reads preserve all source bytes and kernel fingerprints. Each funded one-cent purchase retains Cash 1, Card -1, Food 0, Ready to Assign 0 and reserve 1 before and after rollover. A deliberately wrong Accounts.md hash causes the real store to save the new Category and then fail its Account CAS. A fresh handle loads this partial save as a valid Budget and reuses the same payment Category. Retrying the Account edit leaves Budget.md bytes and fingerprint unchanged. The other User's source bytes and fingerprints remain unchanged.
The durable save order is Categories first, links second. calternal-fs gives each replacement the normal atomic/fsync contract. The failed-CAS probe reaches the same source state as interruption between those saves. This round did not add a process-kill campaign. Existing route tests also pass for editable repair, Category-first persistence and refusal before exceeding the source-file cap.
In-memory repair runs on reads, as §48 explicitly requires. Reads never save the repaired files. The brief's wording that repair never runs on read is interpreted as no durable repair on read, consistent with the stated design and requested file-change check.
Reference numbers
All eleven earlier vectors remain correct, including every stored field. With S assigned to both Food and Dining, Food purchase/full refund A, Dining purchase B and payment P: Food S, Dining S-B, Ready to Assign -2S, Cash -P, Card P-B and reserve max(B-P,0). The earlier revised burst pins and vector records also remain correct.
Only
de7c249363a57c0cchanged since round 4. Its source FX total is -49.50 USD = -4,950 budget units, with no Assignment. Correct Visa debt -4,950, uncovered 4,950, reserve 0, Food Available -4,950 and U 4,950. The newcard-payment-visaCategory has Assignment, activity and Available 0, Funded state and no target fields. L+U=M=0. The number probe compares the entire record against the prior record with exactly these independently computed additions. Every other field and input is unchanged. The Rust vectors gate checks the production replay against the records.Generated histories
Seed 139305, distinct from earlier reviews:
Total: 4,000 histories, 22,000 month models and 14,000 rollover checks. All pass. The standalone source warning about proptest persistence does not affect the fixed seed or case count; there was no failed/shrunk generated input.
Known gaps and decisions
No product gap was found within this round. No product design decision was made. Review choices: reuse the existing independent generators with a fresh seed; simulate the Category/link interruption through a real failed Account CAS; interpret the read rule as no durable source mutation. These choices do not change product behavior.
Random exact U, Tracking transfers, Card Income Categories, import reconciliation and exhaustive integer intermediates are outside the expanded generators. Existing crate gates cover their existing contracts. This review does not claim exhaustive coverage of them. No UI or feature was built, so no screenshots or performance profile was added. No benchmark was run. No secrets were displayed.
For the merge round
On the combined branch, run
cargo clippy --all-targets -- -D warningsandcargo testfor workspace integration;cd apps/web && bun run check && bun run testfor web integration; andtests/adversarial/run.shfor the live-server robustness, authorization and User-isolation matrix. These are the merge round's standard checks, not unfinished focused review work. The focused arithmetic, shuffle, opening, repair, interruption and isolation checks requested here were completed in this job; none were deferred. No push, deployment or merge into dev was performed.Gate output (verbatim)
cargo fmt --checkexited 0 with no stdout or stderr. The standalone Rust probe also passedrustfmt --edition 2024 --check tests/money/bulkrev5-store.rs.cargo test -p calternal-money -p calternal-plugin-money (CARGO_BUILD_JOBS=4)python3 tests/money/bulkrev5-numbers.pypython3 tests/money/bulkrev5-probe.pypython3 tests/money/bulkrev5-probe.py --store-only (strengthened partial/final replay assertions)cargo cleanNo web build output was created. The three standalone probe binaries were removed. All changed module and function doc comments were read again. The worktree contains only committed review files and ignored evidence.
READY — no blocking items.
Deployed to calternal.cloud in merge round 35c (build
5a2c02c13, live 2026-10-09 ~16:35 IST). Closing.