Startup stays in notes-upgrade-backfills for over 25 minutes on a medium Home #1573
Closed
opened 2026-10-11 06:21:21 +00:00 by kayg
·
129 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#1573
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?
Problem
Server startup stays in
state=migrating,phase=notes-upgrade-backfillsfor more than 25 minutes on a medium Home. The app is not ready during this time.Evidence (job calcold-1560, perf VM, dev
4ebeba0b8plus Calendar changes):bench/tab-switch-seed.pydefault size (5,000 Notes, 2,000 Tasks, 20,000 Money transactions, 3 years of Calendar)./healthz(or the readiness probe) returnedstatus=200 state=migrating phase=notes-upgrade-backfillsuntil the harness limit of 1,500,000 ms.--smokesize data, startup completed.Related observations (job tabswitch2, #1233): restart readiness p50 48 s on a 100-file snapshot; a 10,000-file restart not ready within 60 s on a loaded host. #1540 covers reconcile time on 100,000-file Homes; this issue covers the readiness gate and the Notes upgrade backfills.
Goal
notes-upgrade-backfillstakes this long on this fixture (profile: which backfill, rows/s, DB time, lock waits, per-file reads). Reproduce with the default seed on the perf VM.Done when
Starting #1573 from branch
job/startup-1573, based onorigin/devat0c712b98c164b6734d54f63485012bd1a11005b4. I will reproduce the default seed on the perf VM and profile the Notes upgrade backfills before changing code. I will not edit Files reconcile code owned by #1540.Measurement decision rule (set before the runs): accept only if the default-seed cold startup-to-ready p50 improves by at least 50%, and no restart p50 regresses by more than 10%. Keep cold and warm results separate and retain every sample. The perf VM was checked at 2026-10-11 08:41 CEST and was held by
rec1540c-searchfix; I will start measurement after that lock releases.Code finding before profiling:
migrate_user_note_identities_for_userscans every Markdown Note and then executesSELECT id FROM note_items WHERE user_id=? AND path=?once per file (crates/plugins/notes/src/lib.rs, identity migration). The date migration also scans the Home, checks and writes each Note under the User lock, then executes oneUPDATE note_itemsper file. Both run in sequence insidenotes-upgrade-backfills, before Search and Files reconciliation. I will measure the per-migration time and filesystem/SQLite activity on the perf VM before changing this path.Default seed setup completed on the perf VM under
perf-runon 2026-10-11. The generated fixture reports 5,000 regular Notes, 1,097 Daily notes, 100,000 Files, 50,000 Photos, 2,000 Tasks and 20,000 transactions (seed 549, anchor 2026-10-11). No server code has changed. The unchanged server's cold-cache startup profile is now running; it will record state/phase and Note migration markers, process CPU/RSS/I/O/syscall samples, and load.Metric clarification from the current server contract:
crates/calternal-server/src/wire.rs::readyzchecks the Index and system-directory write probe and can return HTTP 200 while/healthzremainsstate=migrating. The live-startup test aroundnotes-upgrade-backfillsasserts this behavior. I therefore use/healthz's transition tostate=ok(startup_ok_ms) as the issue's startup-to-ready metric;readyz_msis retained only as a live-router check and is not used for the acceptance decision.Profiler finding and correction: the first default cold run reached its timeout but failed while building the JSON record because the embedded sampler lacked
cache_modeandbinary. The initial shell also usedset -earound the sampler, which could skip cleanup and leave the benchmark server holding the perf lock. I passed both values explicitly, made cleanup run on sampler errors, validated a five-second remote smoke, and stopped only the confirmed orphan server from this benchmark.perf-run statusthen reportedfree, and a new full cold baseline is running. This was a profiler defect; the first run has no usable timing result.The corrected default cold run reproduced the readiness stall on the unchanged
4ebeba0b8server: it timed out at 1,500,449 ms, with no/healthzstate=ok; onlynotes-stable-identity-v1completed, first observed at 157,452 ms. Load before the run was 2.39 / 3.43 / 3.61. This run’s sampler used loopback although the server binds to the perf VM’s NetBird address, so health and process samples were empty; the migration-marker timing is usable, but the CPU/RSS/state series is not. I am correcting that endpoint and will collect the full phase profile before changing server code.Log profile for the unchanged cold run:
notes-stable-identity-v1scanned 6,099 Markdown Notes in 157,452 ms (about 38.7 Notes/s); 0 were missing IDs, 0 were assigned, and 0 Index rows were repaired. The following date migration emitted no completion summary before the 1,500-second timeout. The log contains 2,076 SQLx slow-statement warnings at the configured 10 ms threshold; I am aggregating their durations separately. Code inspection explains the read/query pressure: identity performs oneSELECT id ... WHERE user_id=? AND path=?for each scanned Note. Date migration first reads every eligible Note during its scan, then the per-Note helper reads.calternal/settings.json, fingerprints/stats/reads/stats the Note, and the final unresolved check scans it again. These are source-based operation counts; direct per-file syscall samples were unavailable because the first run used the wrong health endpoint and did not capture process samples.The server readiness slice is committed as
6f8da1a90. The complete server crate test run passed, includingwire::tests::live_apps_run_in_separate_processes, which runsstartup_serves_http_while_upgrade_backfills_waitand confirms/healthzreportsstate=okwhile the Notes backfill waits. Exact summary:277 passed; 0 failed; 12 ignored; perf guards and private index permissions also passed. The live test took 170.10 s. Files reconciliation implementation was not changed; only its startup progress is now reported as background work.I aggregated the baseline SQLx slow-statement log without printing query text. It contains 2,072 slow statements; their reported execution times sum to 665,552 ms (p50 162.75 ms, p95 814.4 ms, max 3,615.65 ms). This is aggregate SQL execution time, not wall-clock startup time. It confirms that the baseline startup spent substantial work in SQLite; the Notes path now batches identity lookup into one query and resolves the User Zone once per date-backfill pass.
The seeded default Home occupies 9.6 GB on the perf VM's ext2 filesystem. A planned
cp --reflink=autoclone therefore became a full copy; I stopped it after 41 MB and removed the partial clone. No profile was run on that partial data. I will reuse one seed root and reseed it between profiles so the VM keeps one large fixture at a time.Corrected sampler baseline (
4ebeba0b8, default seed, cold cache) now reaches/healthzafter 1,025.89 ms and/readyzafter 99,353.13 ms. It remainsstate=migratingthrough the 300,000 ms cutoff; onlynotes-stable-identity-v1completes, at 266,173 ms. This distinguishes the database liveness probe from startup readiness and confirms startup readiness exceeds five minutes on this run. The procfs sampler returned no samples, so I added content-free exception-type counters and will correct it before reporting CPU/RSS.First default cold comparison from fresh seeds: unchanged baseline
4ebeba0b8did not reachstate=okin 300,000 ms; candidate57ea69a1reachedstate=okat 113,870.35 ms. Against the baseline's five-minute lower bound, this run shows >62% lower startup-ready time and clears the >=50% decision threshold for this pair. Both runs reached/readyzat about 99 s, which is the database liveness probe; the improved metric is/healthz state=ok. This is one pair only, so it is not the required three-pair p50 yet. CPU/RSS sampling is still empty; I added attempt counters and will use them in the next run.The 113.87 s candidate run still missed the issue's <30 s cold goal. The corrected health timeline had a 92.2 s gap between
prepare-data-directoryandconnect-index, before Notes backfills. Source inspection found thatRoot::recover()replays journals and then walks every directory in the whole Home to unlink abandoned.calternal-tmp-*files. This is the remaining pre-bind cost; the cleanup scan is the likely cause based on the 9.6 GB fixture and timing gap. Decision: keep journal replay before serving, expose the existing idempotent temp sweep as a smallcalternal-fshelper, and run it after readiness. The shared active-temp registry protects in-flight temporary files; Files paths already filter these reserved names. No Files reconcile implementation is changed.Correction to the prior comment: the measured gap is 93.18 s (
connect-indexat 94,206.68 ms minusprepare-data-directoryat 1,022.91 ms), not 92.2 s. The cleanup scan remains a source-based attribution pending named phase timings in the next profile.Progress update: optimized the Notes identity backfill to query existing path/ID mappings in one batch and read the User Zone once per User. Startup now serves requests after required journal recovery, then runs semantic indexing, restartable Plugin backfills, and the abandoned temp sweep in background phases. Full populated 687-Note upgrade coverage checks identity mappings, preserved source properties/body/mtime and byte-stable restart. Evidence:
cargo test -p calternal-plugin-notespassed (326 unit, 2 integration);cargo test -p calternal-serverpassed (277 unit, perf guard, private permission; 12 ignored); server clippy andcargo fmt --checkpassed. Commits:8f1588105,6f8da1a90,afdd85bfb,c5e285b70,c2ec2b628. Files reconcile implementation was not changed. The required perf lock is currently held by another job; measurements will resume when available.New default-seed cold baseline (
pair1-base-cold, pre-change binary SHA58ca0cef…84e99, seed 549): health at 1,027.70 ms; readyz at 85,315.85 ms; nohealthz state=okby the 300 s cap;notes-stable-identity-v1completed at 226,750.89 ms. Perf-VM load before the locked run was 4.78. This reproduces the readiness delay. The sample collector still returnedprocess_sample_attempts=0, so CPU/RSS figures are not usable; I am fixing the harness before using them.Pair 1 default cold candidate (
pair1-candidate-cold, SHA7057aa8a…381bb922):/readyz96,743.14 ms; firsthealthz state=ok112,030.33 ms; load before 6.41; 23 process samples, no sample errors. Compared with the same-pair pre-change run, which had nostate=okby 300 s, the health-state readiness result improves by at least 62.7%. It still misses the issue's <30 s target./readyzis 13.4% slower than the 85,315.85 ms baseline, so I am reporting the two endpoints separately and continuing interleaved pairs.Pair 2 default cold pre-change run (
pair2-base-cold, same baseline SHA):/readyz79,259.82 ms; nohealthz state=okby 300 s; Notes stable-identity marker 226,751.46 ms; perf-VM load 5.49 before the run. The corrected sampler captured 53 samples (process CPU p50/p95 7.16/59.54 s; RSS p50/p95 140.8/178.7 MiB). Pair 1 did not have valid samples because it used the prior sampler version.Pair 2 default cold candidate (
pair2-candidate-cold, candidate SHA7057aa8a…381bb922):/readyz100,820.25 ms; firsthealthz state=ok115,644.09 ms; load before 3.08; 23 samples with no errors. Compared with the paired baseline, which had nostate=okby 300 s, health-state readiness improved by at least 61.5%. The absolute <30 s goal remains unmet on the perf VM. The two cold pairs show a consistent health-state improvement, but/readyzremains slower and final acceptance still needs the third pair and other restart cases.Supplementary local candidate run on the full default seed (
/load 3.78):/readyz7,026.83 ms andhealthz state=ok21,664.37 ms. The page cache was warm from local fixture extraction and a prior startup attempt, so this is not a cold comparison and is reported separately from the perf-VM samples. It meets the <30 s health-state target only in this local warmed case.Finished #1573 on branch
job/startup-1573; head3d4ed037b.Built: batched the Notes identity Index lookup and cached Zone per User; preserved per-Note checked writes and restart markers; replayed required namespace journals before serving and deferred abandoned-temp cleanup, semantic search, and restartable background work; expanded populated-Home repeat-upgrade coverage; added
bench/startup-1573.pyfor locked startup profiles and bounded process samples. Files reconcile implementation was not edited. No ratchet was changed.Files:
bench/startup-1573.py;crates/calternal-fs/src/journal.rs;crates/calternal-fs/src/lib.rs;crates/calternal-server/src/main.rs;crates/calternal-server/src/wire.rs;crates/plugins/notes/src/lib.rs.Gates, output copied verbatim:
cargo fmt --check: exited 0 with no output.Finished \dev` profile [unoptimized + debuginfo] target(s) in 12.36s`Finished \dev` profile [unoptimized + debuginfo] target(s) in 25.81s`Finished \dev` profile [unoptimized + debuginfo] target(s) in 4m 25s`test result: ok. 109 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 11.98s;test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s;test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.68s;test result: ok. 54 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 191.81s;test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.04s; doctests:test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s.test result: ok. 326 passed; 0 failed; 5 ignored; 0 measured; 0 filtered out; finished in 180.11s;test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 16.06s; doctests:test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s.test result: ok. 277 passed; 0 failed; 12 ignored; 0 measured; 0 filtered out; finished in 133.87s; perf guard:test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 48.64s; private permissions:test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.16s.Removed 28847 files, 18.3GiB total;apps/web/buildwas deleted.Performance: two interleaved default-seed cold perf-VM pairs completed. Both baseline runs had no
healthz state=okby 300s; candidate runs reached it at 112.03s and 115.64s (at least 61.5% faster against the cap). The paired/readyzp50 went from 82.29s baseline to 98.78s candidate (20.0% slower). The candidate therefore misses the <30s cold goal, and the result does not meet the no-regression rule if/readyzis the required readiness metric. The third default pair and default warm matrix did not run: another job repeatedly held the perf lock through the four-hour limit. A perf-VM 100-file baseline also timed out at 300s; its candidate reachedstate=okat 55.74s. Local post-seed runs reachedstate=okat 21.66s on the default seed, 19.66s with 100 Files, and 19.66s with 10,000 Files; page cache was warm, so these are supplementary local readings. A local full Notes upgrade ran for 900s and completed the identity, date, and daily bridge-tag markers, but not all five markers.Decisions: required journal replay stays before serving because it protects durable writes. The full temp sweep and derived/restartable work run after readiness. I used
healthz state=okfor the agreed readiness calculation and reported/readyzseparately because it disagreed. No UI changed; UX gaps are not applicable. Known gaps are the incomplete third/default-warm pair matrix, no perf-VM 10,000-file pair, and no completed full-default upgrade timing.#1573 continuation on branch job/startup-1573 at
3d4ed037b; branch base is origin/dev0c712b98c.Decision rule (recorded before measurements): accept the startup change only if the User-visible readiness metric improves for both default-seed cold startup and the 100-file restart, and no measured baseline/candidate cell regresses by more than 10%. I will also report the 10,000-file restart result and the separate /healthz state=ok and /readyz timings.
User-visible readiness metric: elapsed time from process start until an authenticated request for real Calendar range data and a real Note body both return HTTP 200 with expected fixture content. This measures when a User can use the app, rather than when the listener or process health endpoint responds.
I will trace what the app exposes between /readyz and healthz state=ok, verify startup phase accounting, then run the requested perf-run measurements if the shared perf VM is available.
Code timing finding:
release_startup_after_api_idlehas a 15 s fallback when no User API call arrives./healthzand/readyzare outside the tracked/api/paths, so they do not release that gate. This accounts for the candidate's roughly 16 s between/readyzandhealthz state=ok; it is not pre-bind work.I changed the transition to
state=okto happen immediately after installing the live router. Background startup work still waits for API idle, while/readyzcontinues to check the Index and system-directory write probe. The profile now probes the authenticated Calendar range for the seeded Log and reads the seeded Note body, and it records partial/404 responses plus named pre-listener phase timings without writing response content to its report.Correction to my prior timing comment: the roughly 16 s issue is the candidate's slower
/readyztime versus baseline (98.8 s vs 82.3 s p50). The 15 s no-API fallback explains a separate lag between/readyzand/healthz state=ok; it does not explain the baseline/candidate/readyzregression.The live-router health transition fix addresses only that health-state lag. I will use the named startup phase timings to locate the pre-ready candidate regression and report both deltas separately.
Timing finding from the prior candidate logs (
7057aa8a…381bb922): on the 100-file Home,/readyzwas 40,134 ms and the old/healthz state=oktransition was 55,737 ms. Named phase timings sum to 3,719 ms. The health phase timeline stays atprepare-data-directoryfrom 1,021 ms untilindex-migrationsat 37,107 ms, then reachesstart-search-indexersat 39,121 ms andpost-bind-startup-workat 40,134 ms. This leaves about 36 s in setup outside the recorded named timings; it is not attributed yet. The old 15 s API-idle fallback accounts for the separate 15.6 s after/readyz; the latest code now marks health ready on live-router installation, with a passing focused test.On the prior default-seed candidate sample,
search.indexer_startalone took 60,960 ms and named phases total 63,818 ms, while/readyzwas 100,820 ms. The ~37 s setup gap is also present there. These old samples do not satisfy the new User-data metric or the 3-pair requirement. I will include phase timings and correct Calendar/Note reads in the fresh locked matrix.Measurement setup decision for this continuation: use the shared baseline release built from
4ebeba0b8(its local SHA-256 matches the perf VMstartup-1573-bins/basebinary). The default-seed samples drop Linux page cache before each profile; 100-file and 10,000-file restart samples use a warm page cache because they model a restart of an already-upgraded Home. For a common auth fixture, the baseline initializes each freshly seeded Index and stops at its first live-router/readyz; it does not wait for its older health state to finish optional upgrades. I then insert one private benchmark session and clone the same stopped source for each paired sample. The 100-file and 10,000-file sources are upgraded and reconciled with the candidate before the restart pairs. These setup runs are not measured cells.The fresh default #549 seed was generated on the perf VM under
perf-runat anchor date 2026-10-11: 5,000 Notes, 100,000 Files, 50,000 Photos, 2,000 Tasks, 20,000 Money transactions, 32,910 Logs, and 1,097 daily Notes. The Calendar/Note probe fixture is present. Baseline Index initialization at first/readyzis queued behind the currentcalact4-1557perf-run holder; no measured cell has run yet.Default cold pair 1 baseline (
4ebeba0b8, SHA58ca0cefe0ffe215a31f0ec2844a4c2979806fd2cc4574925959c61be0d84e99) reached/readyzat 22.76 s, but the User metric was right-censored at the 600 s limit: nohealthz state=ok; the authenticated Note read was 404 on first probe and first returned the expected 200 body at 90.82 s; Calendar returned 200 from the first probe but did not contain the seeded Log through timeout. There were 97 probes, no auth failures, and no incorrect Note 200. This is direct evidence that/readyzcan precede both correct page content and complete Calendar data.Named pre-serving phases totaled 15.95 s (
root.open_prepare_recover14.78 s;search.indexer_start0.58 s). The remaining time is post-bind background work, so phase duration alone does not explain the censored User metric. Process samples showed average CPU 79.6%, p50/p95 sampled CPU 74.0/83.9%, RSS p50/p95 306.4/313.8 MiB; host load before was 0.91/3.47/3.81 and the last sample was 6.10/5.11/4.45. The sample exited by timeout with server exit 0.Default cold pair 1 candidate (
a7e8cf4a96631b311fe1e011c4ac14b50af87ce57eacb1f41698bfd2c5f81e1e) also reached the 600 s timeout without the User metric./readyzandhealthz state=okwere both 83.53 s; Calendar returned 200 partial from the first probe through timeout, while the Note first returned the expected 200 body at 161.07 s. The first statuses were Calendar 200 / Note 404. There were 83 probes, no auth failure, and no wrong Note 200.Compared with pair 1 baseline, candidate
/readyzwas 83.53 s vs 22.76 s (+267%), so this pair fails the pre-recorded 10% no-regression rule; both User metrics are right-censored above 600 s. Candidate named phases account for 81.13 s of the 83.53 s:frontend.assets_retain19.27 s andsearch.indexer_start59.55 s dominate, withroot.open_prepare_recover1.18 s. Baseline hadroot.open_prepare_recover14.78 s andsearch.indexer_start0.58 s. This candidate's added readiness time is therefore localized to asset retention and Search Index startup, even though journal/temp recovery became shorter. I will complete the remaining pairs to check repeatability.Candidate process samples: average CPU 65.2%, p50/p95 sampled CPU 79.8/107.5%, RSS p50/p95 292.5/308.0 MiB; load before was 2.98/3.13/3.63 and the last sample was 6.51/6.26/5.10.
Found and corrected a User-metric probe bug before continuing the matrix. The seeded Calendar Log title includes a linked Note, so matching its display title exactly could never accept the real row. The probe now matches its stable
linknav-639-attachmentblock ID and the visible title prefix. The first default pair remains valid for startup timing, but its Calendar/User-metric outcome is invalid and will be rerun. Probe syntax andgit diff --checkpass.Corrected default cold pair 1 baseline sample (binary
4ebeba0b8, SHA256 58ca0cfe…84e99; probe now matches the stable Calendar Log block ID):/readyz22.712 s;/healthz200 at 1.525 s butstate=okwas not observed. The authenticated Note read returned the expected ID, title and body at 108.433 s. The authenticated Calendar range returned 200 from 22.712 s but did not contain the seeded Log through 600 s, so the combined User metric is censored (>600 s). There were no incorrect Note 200 responses or auth failures; the server exited 0. Named startup phases total 15.426 s, includingroot.open_prepare_recover14.210 s andsearch.indexer_start0.534 s. Process CPU average 70.0% (sample p50 77.9%, p95 111.8%); RSS p50 306.6 MiB, p95 322.1 MiB. Perf VM load before was 1.33/2.25/3.16 and last sampled was 5.50/4.93/4.13. This baseline cell cannot establish the acceptance improvement until its paired candidate is measured.Candidate timing showed
frontend.assets_retaintook 19.267 s before/readyz, while deferred Search initialization took 59.553 s. I kept asset retention as a pre-listener gate because it preserves immutable chunks for already-open tabs (#423), and changed it to run onspawn_blockingalongside Search Index initialization. The server waits for both results, and errors still fail startup. Commit7b3557408passescargo fmt --check,cargo clippy -p calternal-server --all-targets -- -D warnings, andcargo test -p calternal-server(278 passed, 12 ignored; perf guard 1 passed; private permissions 1 passed). The new release binary is built; perf VM results are pending. No latency improvement is claimed before measurement.Metric clarification from the literal request:
user_visible_msis the first correct authenticated response from either sampled real page (Calendar range or Note read). Requiring both would measure time-to-full-route-coverage, not the first successful page GET. The profile still records each route’s correct-content time separately and flags incorrect Note 200s/auth failures. On the first corrected baseline sample, the metric is therefore the Note’s correct 200 at 108.433 s; Calendar remained a 200 partial response with the seeded Log absent through 600 s. The earlier comments describing the combined metric are superseded by this definition. The current in-flight pair-1 candidate was already generated with the prior stop condition, but it records both per-route timestamps and will be reported using this same first-correct-response rule; later profiles use the corrected stop condition.py_compileandgit diff --checkpass.Corrected default cold pair 1 candidate sample (release SHA256 3bee171a…72c0227a):
/readyz65.374 s;healthz state=ok65.374 s. The first correct response from either sampled page was not observed by 600 s, so the User metric is >600 s. Calendar returned 200 but lacked the seeded Log from 65.374 s through the cutoff; Note returned 404 through the cutoff. There were no incorrect Note 200s or auth failures; server exit was 0. Named phases:root.open_prepare_recover1.136 s,search.indexer_start62.129 s,frontend.assets_retain62.129 s; the last two ran concurrently, so their durations must not be summed. Their shared timing shows the overlap reduced the prior candidate/readyzby about 18 s, but it did not improve the User metric; this pair fails the stated acceptance rule (baseline first correct page 108.433 s, candidate >600 s). CPU average 67.5% (sample p50 79.8%, p95 108.7%); RSS p50 301.9 MiB, p95 314.7 MiB. Perf VM load before was 2.40/3.30/3.64 and last sampled was 7.27/5.96/4.83. Later pairs will test repeatability.Default cold pair 2 candidate ran first with the first-correct-response metric: User metric 91.371 s (expected Note ID/title/body).
/readyzandhealthz state=okwere both 18.652 s. Calendar returned 200 but the seeded Log was absent; Note was 404 on the first probe and then correct. No incorrect Note 200 or auth failure; server exit 0. Phases:root.open_prepare_recover1.171 s,search.indexer_start14.069 s,frontend.assets_retain15.509 s (concurrent). CPU average 23.0% (p50 10.6%, p95 70.9%); RSS p50 136.9 MiB, p95 178.5 MiB. Perf VM load before 2.08/4.67/4.50 and last 4.60/4.62/4.48. The paired baseline is in progress. This differs sharply from pair 1 candidate (>600 s User metric;/readyz65.374 s), so startup variance remains under investigation by the remaining pairs.Default cold pair 2 baseline ran after the candidate. User metric (correct Note GET) was 134.869 s.
/readyz69.306 s;healthz state=okwas not observed. Calendar returned 200 but remained partial without the seeded Log; Note was 404 first and correct by 134.869 s. No incorrect Note 200 or auth failure; server exit 0. Named phases:root.open_prepare_recover61.082 s,search.indexer_start0.705 s; total 62.426 s. CPU average 10.8% (p50 6.4%, p95 40.3%); RSS p50 61.5 MiB, p95 148.6 MiB. Load before 2.62/3.24/3.79 and last 3.58/3.20/3.68. The paired candidate User metric was 91.371 s, a 32.2% improvement. This pair passes the improvement check, but pair 1 candidate regressed beyond the 10% limit.Default cold pair 3 baseline: User metric 89.545 s (correct Note ID/title/body).
/readyz9.619 s;healthz state=okwas not observed. Calendar returned 200 but stayed partial without the seeded Log; Note returned 404 first and was correct at 89.545 s. No incorrect Note 200/auth failure; server exit 0. Named startup phases total 3.364 s (root.open_prepare_recover2.044 s;search.indexer_start0.646 s). CPU average 20.1% (p50 5.5%, p95 88.0%); RSS p50 136.5 MiB, p95 216.5 MiB. Perf VM load before 1.78/2.71/3.45 and last 4.43/3.11/3.50. Pair 3 candidate is queued.Default cold pair 3 candidate: User metric 140.474 s (expected Note ID/title/body), versus baseline pair 3 at 89.545 s, a 56.9% regression.
/readyzandhealthz state=okwere both 66.248 s. Calendar returned 200 but lacked the seeded Log; Note was 404 first and then correct. No incorrect Note 200/auth failure; server exit 0. Phases:root.open_prepare_recover1.275 s;search.indexer_startandfrontend.assets_retaineach 62.361 s and ran concurrently. CPU average 15.1% (p50 4.9%, p95 76.0%); RSS p50 132.0 MiB, p95 181.0 MiB. Load before 3.62/3.02/3.46 and last 5.34/3.58/3.58. The 3-pair default cold User metrics are baseline [108.433, 134.869, 89.545] s and candidate [>600, 91.371, 140.474] s. Median is 108.433 s baseline vs 140.474 s candidate; two of three candidate cells regress by more than 10%, so the posted decision rule rejects this candidate.Readiness behavior observed with the corrected metric: before the live router is installed,
/healthzreturns HTTP 200 withstate=migrating, and the startup fallback returns 503 for application routes. In the baseline binary,/readyzcan return 200 while/healthzremains migrating: pair 1/readyzwas 22.712 s, the Note was 404 until 108.433 s, Calendar was 200 but partial, and nostate=okwas observed through 600 s. In the candidate,/readyzandstate=okcoincide when the live router is installed; after that point, the sampled Calendar route returns partial 200 data and the Note route returns 404 until its content exists. Across all measured probes, no incorrect Note 200 or auth failure occurred. Calendar’s seeded Log was absent in every cold sample, so the first-correct page metric was the Note when it became available. This describes the measured routes; the full route surface was not exhaustively probed.#1573 final report — decision: REJECT
The posted rule was: accept only if the User metric improves on the default-seed cold run and the 100-file restart, with no measured cell regressing by more than 10%.
User metric and readiness
The User metric is the time from process start to the first correct authenticated GET from either real page sample: the seeded Note body with matching stable ID/title, or the Calendar range with the seeded Log block ID and title prefix. Each route is also reported separately. The Calendar probe uses stable block ID linknav-639-attachment because the display title contains a linked Note.
Default-seed cold pairs (perf VM, candidate and baseline runs interleaved):
The candidate median is 29.5% slower. Pair 1 and pair 3 regress by more than 10%; pair 2 improves by 32.2%. The default-seed condition fails, so the candidate is rejected. The 100-file and 10,000-file restart matrices were not completed. The existing 100-file directory had no bearer, Note migration markers, or Files Index rows; no 10,000-file seed directory existed. Setup remained behind another perf VM holder when the time box ended.
Across all six cold samples, Calendar returned 200 but lacked the seeded Log. Note returned 404 until its expected content became available; no incorrect Note 200 or authentication failure occurred. Before live-router install, /healthz returns 200 with state=migrating and the startup fallback returns 503 for application routes. In the baseline, /readyz could succeed while health remained migrating; the sampled Calendar route was partial and the Note route was 404. In the candidate, /readyz and state=ok coincide at live-router install; afterwards Calendar was partial and Note was 404 until correct. These probes cover Calendar and Note only, not every route.
Startup phases
The earlier 100-file report had /readyz at 40.134 s, health state=ok at 55.737 s, and only 3.719 s in the named phase totals. About 36 s was still hidden inside prepare-data-directory; the subsequent split of required journal replay from restartable abandoned-temp cleanup moved that optional sweep after readiness. Required journal replay remains before serving.
For the earlier candidate /readyz regression (98.8 s vs 82.3 s baseline), the newly named frontend.assets_retain phase took 19.267 s; search.indexer_start took 59.553 s in the same run. The candidate now runs asset retention on spawn_blocking concurrently with Search Index initialization, and still waits for both before opening the live router. This preserves old immutable assets for open tabs. In cold pair 2 the concurrent phases took 15.509 s and 14.069 s, with /readyz at 18.652 s. In cold pairs 1 and 3 they each took about 62.1 s, with /readyz around 65 s. The phase total sums both concurrent timers and must not be added. Startup remains highly variable.
Perf VM measurements
All runs used perf-run. CPU is average / sample p50 / p95; RSS is sample p50 / p95 MiB. Load average is before → last sample.
Built and decisions
Gates
cargo fmt --check: exit 0, no output.
cargo clippy -p calternal-server --all-targets -- -D warnings:
Finished
devprofile [unoptimized + debuginfo] target(s) in 20.57scargo test -p calternal-server:
test result: ok. 278 passed; 0 failed; 12 ignored; 0 measured; 0 filtered out; finished in 147.52s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 39.12s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s
python3 -m py_compile bench/startup-1573.py: passed.
cargo clean: Removed 28846 files, 18.3GiB total.
Head SHA:
ddca41a0b7.Remote seed/binary cleanup: local build output and apps/web/build are removed. The final perf-run cleanup is queued but has not acquired the lock as of this report; perf-run status shows calact5-1557 holding it. The remaining remote roots are /srv/hdd-emu/startup-1573-default, /srv/hdd-emu/startup-1573-100, /srv/hdd-emu/startup-1573-bins, and the default pair clone directories. No server process was active in the inspected 100-file root. No push, deploy or merge was performed.
Correction to the final report: the perf-run cleanup completed. It removed all nine /srv/hdd-emu/startup-1573-* roots, including the seed and binary directories. Verification output: remaining_startup1573_roots=0; filesystem /srv/hdd-emu 40G total, 5.3G used, 32G available. The remote cleanup gap noted in the previous comment is closed.
Starting #1573 escalation on branch
job/startup-1573c, inherited head/baseddca41a0b70213926e7a98c7ee100d8c8ccf25cefromjob/startup-1573. I read CLAUDE.md, CONTEXT.md, DESIGN §59 and all 42 issue comments, including both rejected reports and the cleanup correction.Decision rule, before any new measurement: accept only if first correct authenticated page data improves for default-seed cold and 100-file restart, no cell regresses by more than 10%, and no run hangs. Use three interleaved pairs per cell against origin/dev; retain failed and censored samples.
The brief has an explicit stop condition: “If DESIGN has no rule, stop and list it as a question in the report instead of inventing one.” DESIGN §59 specifies Index reads and background derived work but does not define the incomplete-projection response contract for Calendar range or Notes reads. I requested the missing decision and will not invent that contract.
#1573 escalation report: STOPPED AT THE REQUIRED DESIGN QUESTION
Question: While the Calendar or Notes startup projection is incomplete, should authenticated reads return 503 with Retry-After, or 200 with an explicit incomplete state? Which completion boundary applies to each route?
The job brief explicitly says: “If DESIGN has no rule, stop and list it as a question in the report instead of inventing one.” DESIGN §59 rules 1 and 8 specify Index reads, read priority and background derived work. They do not define either response contract or its completion boundary. The 503 + Retry-After rule elsewhere in DESIGN concerns refused streams, not incomplete projections. I did not extend it to these routes without a decision.
Built/files: no production changes, no new commits. Local investigation notes are in gitignored
artifacts/startup-1573c/. Head remainsddca41a0b70213926e7a98c7ee100d8c8ccf25ceonjob/startup-1573c.Read-only findings, not reproduced root causes:
Indexer::open_innercallsstart_watchersynchronously, and that function registers a recursive watcher before returning. This can block the runtime polling both startup timers. Thus the identical ~62 s concurrent timers do not establish 62 s of asset work. This needs an isolated watcher-registration timer and a live reproduction.Root::retain_web_asset_generationreads every existing incoming asset and compares its full contents, then prunes and collects store statistics. It does not walk the Home, but it does reread asset payloads on restart.Known gaps: all requested fixes, hang reproduction and lock-wait evidence, populated upgrade verification, and all nine new baseline/candidate pairs remain outstanding. No performance improvement or acceptance is claimed. The existing candidate remains rejected by the round-2 evidence.
Decision rule posted before measurements: first correct authenticated page data must improve on default-seed cold and 100-file restart; no cell may regress >10%; zero hangs. No new measured cell ran. The perf VM was held by
calact5-1557during inspection. Previous #1573 roots were already cleaned; inspection found no/srv/hdd-emu/startup-1573-*roots. I created no remote seeds or processes.Gates: not run; no code changed. No gate success is claimed. No dependencies, perf ratchet, migrations or test expectations changed. No pushes, merges or deploys.
Decisions: none invented. The incomplete-projection contract is the open question above. No UI changed; UX gaps closed: none; UX gaps left: incomplete projection reads remain unresolved.
Resuming #1573 escalation as startup-1573d on job/startup-1573c, base/head
ddca41a0b7. Read the contract, DESIGN §59, glossary and all 44 report comments. The new brief explicitly defers Calendar/Notes response-contract decisions; I will continue without changing those response shapes.Decision rule before new measurements: accept only if first correct authenticated page data improves on default-seed cold and 100-file restart, no cell regresses by more than 10%, and zero runs hang. Three interleaved pairs per cell versus origin/dev. Keep each route result and censored sample. First priority is reproducing and diagnosing the >600-second candidate failure, including background phases and lock waits. No pushes or deploys. The required one-time origin/dev integration is authorized by the specific final-gates instruction.
Reproduced fixture invariant failure with the inherited release calternal-fs library, not a modified writer:
bench/startup-1573.py::clone_fixtureusescp -alon every Home file.calternal-fs/src/write.rsrefuses a checked mtime-preserving metadata write when the source has unknown hard links (DESIGN §2, #1148). The #549 seed lacks managed date keys, so its date backfill needs that checked write. A paired fixture can therefore be permanently ineligible for the upgrade even though bytes and mtime look correct. Commit6e224522fadds an opt-in clone that detaches Markdown inodes while preserving bytes and nanosecond mtime; its regression passes. The legacy clone remains available to reproduce the >600 s scenario on the perf VM. This is an isolated failure proof, not yet the full stall root cause. Perf lock remains held by calact5-1557.FS regression evidence: the first process-age test exposed coarse filesystem timestamps: a newly created temp with restored old mtime could compare older than a nanosecond process cutoff. The corrected helper preserves the entire process-start second and checks ctime as well as mtime. It retains the active-name registry across candidate stat/unlink; ordinary directory entries no longer hold that registry while stat runs. The FS release unit suite passed:
test result: ok. 111 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 10.69s. Commitseec9d7c0dand30f306f05add the cutoff and restart inventory helper.Asset finding: retention reads deployment manifests, then reads and compares every current asset payload, prunes the flat private store and collects stats. It does not walk a Home. The restart helper now validates one durable build inventory and stats its references; normal retention still installs and verifies a new build. It does not refresh retention age. The manifest is published only after payload durability, so it is the restart publication proof (#423).
Search design invariant found during implementation: #505's existing constructors must keep waiting for a live Search writer. Their test expectation remains unchanged. The server gets a separate background constructor and an independent mandatory filesystem writer lease before journal replay; it cannot serve mutations while an older server owns the Data directory. The slow Search writer and recursive watcher setup can then run independently, with the existing Search
indexingresponse state. Warm Search retains its persisted reader. The perf VM has repeatedly moved directly between calact5-1557 phases; the new profiler queues through perf-run's own bounded flock rather than polling gaps.Live reproduction and measurement details (#1573): the inherited candidate from
ddca41a0bon a legacy hard-link clone reached the first correct authenticated Note GET at 91.82469 s, with the expected stable ID/title/body. The first Calendar range was a partial 200 at 9.17477 s and remained partial. The server exited 0 without a forced shutdown. This run does not reproduce or explain the old >600 s pair; I do not claim that blocker closed. Kernel waits were mainly futex and disk inode/page/journal waits. The hard-link checked-write rejection has separate deterministic evidence, but it is not sufficient to explain the old missing Note.The real 100-source-file restart fixture passed stopped-Index verification: 90 ordinary Files rows, all 5 Notes migration markers, 1 target Note projection and a private bearer. The 10,000-source-file fixture is preparing through the same real upgrade path.
Measurement details posted before the new matrix: use three interleaved pairs for default cold, 100-file restart and 10,000-file restart; reverse baseline/candidate order for pair 2; drop caches for every measured start, including restart cells. Clone stopped Index state and detach every Markdown source inode, preserving bytes and ns mtime. Keep censored samples. One perf-run lease covers both runs of each pair, clone and shutdown. Acceptance remains improvement in default cold and 100 restart, no cell >10% regression, zero hangs; route shapes remain unchanged. Baseline is exact origin/dev
0c712b98c1. The single requested fetch/merge returnedAlready up to date.The first verified matrix reproduced the blocker: default cold candidate pair 1 exceeded the 600 s correct authenticated data deadline. The listener and readyz were available at 62.1 s. There were 85 authenticated probe attempts: initial Note 404 responses followed by client timeouts; Calendar returned partial 200 responses. An offline Index/source check during that run found the expected Note row (hidden=0), matching calternal-id/title/body and a private source inode (nlink=1). Opt-in Notes diagnostics recorded a 463.8 s User writer-lock wait. This is therefore not explained by the old hard-link fixture. Background whole-Home reconciliation and Daily note metadata migrations retain this same writer across thousands of sources; I am replacing those scopes with per-source admission and a FIFO foreground-read regression test. No Calendar/Notes response shape changes. The first matrix is diagnostic and fails the posted zero-hang acceptance rule; a complete new matrix is required after the fix.
The Notes whole-Home guard fix has a passing focused regression:
background_home_reconcile_admits_foreground_between_sources(1 passed). A queued foreground reader gets the User writer before the refresh ends; each source is read again under its own guard, Task targets are staged first, and indexed orphan candidates are checked against current bytes. Atomic Files mutations still use the locked entry point. Daily metadata repairs also release the writer per source; navigation detects a removed planned neighbour and retains the migration for retry.The previous candidate also had 58.9 s recursive Search watcher registration and an unnamed recursive Home watcher on a Tokio worker. Home registration now runs via spawn_blocking and is timed as
homes.watcher_register; Search initialization already runs in its dedicated actor. The corrected release build finished in 5m 26s (binary SHA-256 c32a7baa0950f65e93975f5dc7fadeb06ebd0ef695e70a8bf42204af45c85d15). The first matrix remains diagnostic and failed. Its remaining obsolete pairs were stopped while queued, without interrupting the other lock holder. The full replacement 18-run matrix is queued behind calact5-1557 and reuses the original genuine restart snapshots. No acceptance claim yet.The deterministic performance guard passes with zero violations. Only four existing exact diagnostic helper pins and six exact in-memory diagnostic capability bindings changed; contracts/perf/ratchet.json is unchanged. Final touched-crate gates are running after the last Rust change.
Before the corrected matrix runs: it will use one perf-run lease for the complete 18-start acceptance phase (expected 45 min, hard cap 60 min), including binary upload. This avoids losing a shared-VM turn between short pairs; seed preparation and the failed diagnostic phase already released their leases. All three interleaved pairs and the pair-2 order reversal remain unchanged. Stop at 60 min and preserve incomplete results if the phase exceeds its cap. Use each cell p50 (median of its three starts) for the posted acceptance rule: default cold and 100-file restart must improve; no cell p50 may regress by more than 10%; all starts must reach correct authenticated page data without a hang or forced stop. Report every raw timing and paired ratio, plus p95 as the nearest-rank maximum for these three observations. The original genuine stopped restart snapshots are reused, with no Home inode replacement. The corrected monitor probes readyz only until its first successful response, then continues page-data probes independently. No result from the failed diagnostic matrix is part of acceptance.
The corrected matrix pair 1 completed without a hang or forced shutdown: default baseline 130245.22 ms vs candidate 121209.16 ms; 100-file restart 10701.16 vs 4390.45 ms; 10,000-file restart 20275.15 vs 14071.12 ms. The other two pairs are still running. Candidate default recorded zero Notes writer waits over 10 ms, compared with the failed candidate maximum 463798.076 ms.
Cold listener readiness still took 64061.25 ms. Source inspection found a third recursive registration inside calternal-collab Hub::new (session.rs:1236), before history installation and room/API construction. Both Search and Home registrations now run separately (61079 ms and 52935 ms in this pair). The collaboration constructor installs the checked-source observer and invalidation watchers before with_history and before rooms open. I keep that existing source/history boundary before serving; making only this watcher deferred would change another crate's startup behavior and needs its own contract. It is a known remaining O(Home directory entries) listener cost, not evidence that the Search constructor still blocks. No Calendar/Notes response-shape change. No acceptance claim until all three pairs complete.
Final Rust gates pass for calternal-fs, calternal-search, calternal-plugin-notes and calternal-server. Web check reports 0 errors/0 warnings; the required two-worker Bun test gate reports 318 test files and 2316 tests passed.
A resource sampling gap was found before the final report: the 5 s periodic sampler can miss the live phase of a 4 s restart. The acceptance metric and all 18 timing comparisons remain unchanged. The monitor now takes one final process-counter sample after the authenticated response and before shutdown, with a focused regression test. Before additional measurement: append one baseline/candidate resource pair per cell after the existing 18-start matrix, under the same owned perf-run phase and its existing 60 min hard cap. Keep these six observations separate from acceptance; report CPU and sampled RSS from their completed page boundary. No extra binary build or route shape change.
The corrected 18-start matrix is complete and meets the posted rule. All 18 starts reached correct authenticated page data; no timeout, forced shutdown, nonzero server exit, incorrect Note response, Auth failure or Files reconciliation failure was recorded. All nine paired comparisons improved. Cell p50 values: default cold 129771.27 -> 121209.16 ms (-6.598%); 100-file restart 10352.89 -> 4368.44 ms (-57.805%); 10,000-file restart 20173.72 -> 14071.12 ms (-30.25%). Nearest-rank p95 and all raw observations will be in the report. All candidate runs recorded zero Notes writer waits over 10 ms.
The matrix phase completed before the supplemental resource block could be appended. No active measurement file was modified. The six endpoint CPU/RSS observations are now a separate perf-run phase (expected 20 min, hard cap 60 min). They are not part of acceptance. The monitor final-sample regression passes (8 Python tests total). Final reporting and cleanup follow those resource observations. No pushes, deployments or issue closure.
Final report: startup-1573d
Branch:
job/startup-1573cHead SHA:
66725770b61d239a6fff5823be9ad59a2dd5f3d2Meets the posted measurement acceptance rule. Remaining startup costs and the deferred owner response contract are listed below. No push or deployment.
#1573 startup escalation
The corrected matrix meets the posted acceptance rule. All 18 starts returned correct authenticated page data. No start hung or needed a forced stop. All nine paired comparisons improved.
Work
homes.watcher_register.Blocker evidence
Three inherited cold reproductions reached correct data in 91.825, 140.831 and 123.542 s. The first verified new candidate pair then failed the 600 s deadline. The expected Note row and source were already present, with a matching stable ID/title/body and nlink=1. The Notes writer wait reached 463.798 s. Background whole Home work retained this writer across thousands of sources. The foreground FIFO regression now passes. The old hard-link fixture also rejected checked metadata writes, but that defect does not explain the reproduced blocker on private source inodes.
The failed candidate sampled 375.06 CPU s, 293.43 MiB RSS, 31.45 GB read characters and 233.09 MB physical reads near the deadline. The baseline pair sampled 6.43 CPU s and 145.27 MiB RSS at first correct data. These samples describe different run durations; they are not an accepted performance comparison.
Acceptance measurements
Linux perf VM, HDD, production binaries, one
perf-runphase. Both binaries embed the same web build. Drop caches for each measured start. Keep the Home path and inodes for restart cells. Restore the same stopped Index snapshot before each start.There are three starts per variant per cell. p95 uses nearest rank, so it is the maximum of three observations. The monitor checks every 5 s. This interval limits precision. The metric is the first completed authenticated Calendar range or Note response with the seeded ID, title and content. It is not full-Home completion. The default and 10,000-file cells finish with the Note. The 100-file cell finishes with Calendar data. Calendar probes in the 10,000-file cell exceed their 2 s client deadline on both variants. They are slow probes, not observed partial 200 responses.
All candidate observations have zero Notes writer waits over 10 ms. The failed diagnostic observation had a maximum wait of 463.798 s. The separate foreground FIFO regression proves admission between background sources. No existing status-code or fixture expectation changed.
The 100-file fixture has 90 ordinary File Index rows and ten page sources. The 10,000-file fixture has 9,990 ordinary File Index rows and the same ten page sources. Both fixtures have a valid bearer session, all five real Note metadata markers and the target Note Index row. The default fixture uses the existing #549 seed: 5,000 Notes, 1,097 Daily notes, 100,000 Files, 50,000 Photos, 2,000 Tasks and 20,000 transaction sources. Fixture content stays in the benchmark only.
docs/perf/baseline.jsonhas no comparable first correct authenticated startup metric. The exact origin/dev source0c712b98c164b6734d54f63485012bd1a11005b4is the baseline for this comparison. The candidate Rust source isafb0bb7ec. The acceptance monitor is2d2b0c513;403d10618adds the final CPU/RSS sample without changing the page-data metric.Raw accepted observations: 2026-10-11-startup-1573d.json. The failed diagnostic matrix is separate: 2026-10-11-startup-1573d-blocker.json.
Before-serving decisions
Partial startup data
Observed:
GET /api/v1/calendar/rangereturns 200 before the seeded Log is projected.GET /api/v1/notes/{id}first returns 404 before the Note projection exists; the failed run then timed out behind the User writer despite the row being present. The matrix reports each route separately. Search keeps its existing indexing state until initialization publishes a reader. Calendar/Notes response shapes are unchanged. The owner must decide their pending-data contract.Decisions outside the design document
Known gaps
docs/perf/baseline.jsonhas no comparable first correct authenticated startup metric. This run uses the exact origin/dev binary as its baseline.For the merge round
tests/adversarial/run.sh: run the full API matrices on the combined branch. Check cross-Plugin startup, protocol access and shutdown while backfills run.cargo clippy --all-targets -- -D warningsandcargo test: run the combined workspace gates. This job ran each touched crate only.Named startup phases
Candidate median phase times, in milliseconds:
Search writer and recursive watcher times are separate background phases. Do not add concurrent phase durations to infer startup wall time. The collaboration constructor still performs recursive registration before rooms and history are installed. This is the remaining cold listener cost.
Cost and resource measurement
The unchanged frontend preflight costs one bounded manifest read and one stat per referenced asset. Search writer and directory registration run in the actor. The saved reader has a 2 s metadata budget. Temporary cleanup costs one Home metadata walk after serving and locks only candidates. Background Notes reconciliation costs a path plan, a Task-target pass, then one current-source projection per path; only one source owns the writer. These passes have ordering and checked-write dependencies, so they remain serialized.
The periodic resource sampler can miss the live phase of a fast restart. The monitor now samples after the final authenticated response and before shutdown. Six separate resource observations use this final sample. They are not mixed into the 18-start acceptance matrix.
Each resource cell has one baseline/candidate pair. CPU is cumulative through the completed page response. RSS is the maximum sampled value, including the final response sample; it is not a continuous peak measurement. The windows are 128.242/120.750 s, 9.300/4.254 s and 20.255/14.166 s. Average CPU use as a fraction of one core is 20.181/18.211%, 10.645/15.046% and 10.220/10.024%. A shorter window can have higher CPU use per second while using fewer total CPU seconds. The candidate uses fewer CPU seconds and less sampled RSS in each cell. No resource regression issue is needed from these observations. Raw resource observations.
Files
crates/calternal-fs/src/{lib,root,journal,web_assets}.rs.crates/calternal-search/src/{index,indexer}.rs.crates/calternal-server/src/{main,wire}.rs.crates/plugins/notes/src/{lib,store}.rs.bench/startup-1573.py,bench/tab-switch-seed.py,bench/test_startup_1573.py.contracts/perf/{registry,exceptions,adoption-1058}.json.ratchet.jsonis unchanged.docs/perf/runs/.Verification
The one requested origin/dev fetch and integration returned
Already up to date.The working tree used the real production web build for server tests. All four touched Rust crates pass format, Clippy and tests. The web check and full two-worker Bun test gate pass. The Python monitor has eight passing tests. The exact summary output follows. Empty format output means exit 0. Full logs stay in the ignored worktree artifacts.The performance registry changes six exact diagnostic capability bindings and refreshes four existing helper pins in each ledger. The ratchet is unchanged. The enabled diagnostic preserves the existing rename/read serialization regression.
UX gaps closed
A foreground Notes operation can run before a background Home refresh ends. Search exposes its existing indexing state while startup opens the generation. Loading does not wait for an animation.
UX gaps left
The owner must decide the response contract for partial Calendar and Notes data. Collaboration still registers its recursive watcher before history and rooms; its cold metadata walk remains a listener cost.
Cleanup
Remote cleanup ran under
perf-run. It removed nine owned benchmark roots, including seeds, restart snapshots and copied binaries. No owned root remained. Every measured server had exited before cleanup. Localcargo cleanran after the gates and reportedRemoved 31036 files, 20.7GiB total. The copied binaries andapps/web/buildoutput were removed. No Mac VM session, push, deployment or issue closure occurred.Independent data-safety review started on
job/rev1573at66725770b61d239a6fff5823be9ad59a2dd5f3d2. Comparison base:0c712b98c164b6734d54f63485012bd1a11005b4. I will test temporary cleanup, writer ownership, Notes projection concurrency and recovery, deferred Search, and frontend inventory reuse. Product code stays unchanged. Findings go inartifacts/rev1573-findings.md. Time box: 90 minutes. I will not read the author reports or result files.BLOCK found in independent review: deferred cleanup deletes a stage still owned by a pending write journal after a backward clock correction. Regression
journal::recovery_tests::deferred_sweep_preserves_pending_write_after_backward_clock_stepfails on candidate66725770b. A real checked write publishes its intent, the existing first-journal-step fault hook returns an error, and its guards drop. Cleanup then treats the pending stages as abandoned because their mtime/ctime precede the process-start cutoff. Registered live writes remain protected; the defect is journal-owned recovery inputs after a failed publication. Product code is unchanged. I will commit the failing regression test and continue the other checks.Two more independent failures on candidate
66725770b:indexer::tests::server_writer_lease_excludes_pre_upgrade_search_writerholds a real pre-upgrade Tantivy writer and proves its OS lock remains live, but the newserver-writer.locklease succeeds. The candidate can recover and admit source writes before the prior server drains. New-to-new locking passes; the defect is compatibility with the base deployment.indexer::tests::deferred_search_repairs_corrupt_metadata_instead_of_staying_emptystarts successfully with damaged Derived metadata, butreconcile_at_start()returnsErr(WorkerStopped). The deferred actor exits permanently, pending queries return empty, and rebuild calls use the closed channel. The base refused startup instead.Both failing tests are committed separately. Product code is unchanged. I am now testing Notes identity across a foreground move between source guards, then will merge origin/dev once and run the crate gates.
BLOCK confirmed:
tests::background_reconcile_keeps_task_identity_after_interleaved_movefails on candidate66725770b. A queued foreground writer moves Task A onto later planned Task path B between background User guards and reconciles the move. The resumed background pass still has A in itsseenset and writes a newcalternal-idinto B. Exact failure:background repair replaced the identity of a moved Task. This loses a durable item identity and breaks stable Task links. The failing test is committed separately; product code remains unchanged. Four concrete failures are now recorded in artifacts/rev1573-findings.md. Next: the required one-time origin/dev merge and per-crate gates.Independent review progress: all five requested areas now have focused evidence. Four committed failing regressions remain at
63fe184eae7e28c1889d8a5377d276975d288a42. The one-timegit fetch origin/git merge origin/devreturnedAlready up to date.Passing independent probes: compatible server-writer timeout occurs before journal recovery and Index creation; a retry can acquire after release; pending Search HTTP returns 200 with indexing state for two Users, then private Search indexes only each owner; overlapping Task source passes and a reopened partial projection equal a serial rebuild; live batch staging and import ScratchFile survive the cutoff; same-inode Rename/no-op + Remove preserves published content; old asset inventories cannot override the current embedded JS or version.json. The separate #1575 patch is absent from the reviewed candidate/origin/dev, so its interaction was exercised with the same journal-step pattern. Passing probes were removed from product files and saved under artifacts, as this job permits commits only for failing regressions.
Final gates are running sequentially for all four changed crates, with four compiler jobs and four test threads. Formatting and calternal-fs Clippy already pass. The report will include exact gate output and remain BLOCK unless the four regressions are addressed by the author; this review does not change product code.
Precision update for finding 4: the test proves that background reconciliation replaces the moved Task's canonical source
calternal-id. Inspection oftask_identity_aliasesshows an old UUID can still resolve through the retained path alias to a payload with the new UUID, so my earlier blanket statement that old Task links fail was too broad. The source identity mutation remains a BLOCK: Note identity and Reminder UID depend on that field. The test asserts identity preservation and does not change any existing expectation.Final formatting and all four Clippy gates pass. The filesystem test gate has the expected cleanup regression failure; its other targets pass. Remaining test gates are running sequentially.
BLOCK — independent data-safety review of #1573
Candidate:
66725770b6.Base:
0c712b98c1(merge-base with origin/dev).Review branch: job/rev1573.
Review head:
63fe184eae.Built four failing regression tests in four atomic commits. No product code or existing test expectation changed. The author reports and result files were not read.
Files and commits:
fa1abaea3, pending stage cleanup.340234501, upgrade writer exclusion;8ac8a9190, deferred Search recovery.63fe184ea, canonical Task identity after an interleaved move.Finding 1 — BLOCK: cleanup deletes pending recovery inputs after backward clock correction
Candidate regression:
journal::recovery_tests::deferred_sweep_preserves_pending_write_after_backward_clock_step. A real Root write publishes its durable intent, then the existingfail_after=1hook returns an I/O error before source publication. Dropping TemporaryGuard removes the registry entry although the journal still owns the input. A process-start cutoff 60 seconds ahead of current real file timestamps models a backward wall-clock correction without changing the host clock. The deferred sweep unlinks both private inputs. Exact failure:cleanup deleted a stage still owned by a pending write journal. A registered, currently executing write is protected; this defect is the pending recovery interval after a failure. The server sweep does not check pending journals or the namespace operation lock.Finding 2 — BLOCK: the new writer lease does not exclude the prior server
The base owns source-writer handoff through its shared Tantivy writer until HTTP and live Note writes drain (
main.rsshutdown;wire.rsSearch constructor). Candidate ownership uses a differentserver-writer.lockand opens Search asynchronously. Regressionindexer::tests::server_writer_lease_excludes_pre_upgrade_search_writerholds a real SearchIndex writer, verifies its old OS lock is still held viaclear_unlocked_writer_lock() == false, then obtains the new server lease anyway. Exact failure:upgrade admitted a second server writer while the prior server was live. Thus the 30-second bound cannot protect an upgrade from this base: the candidate can recover journals and admit source writes while the previous server remains active. New-to-new lease exclusion is covered by the passing existing FS test.Finding 3 — BLOCK: a deferred Search initialization error leaves a terminal empty reader
Regression
indexer::tests::deferred_search_repairs_corrupt_metadata_instead_of_staying_emptycreates a real Note and damaged Derived metadata, then starts the new background constructor. The constructor succeeds, but the actor exits on the metadata error.reconcile_at_start()returnsErr(WorkerStopped). Queries take the!initializedbranch and return empty results;rebuild()andrebuild_user()send to the same closed actor channel. The server only logs the startup error and continues to report ready. This differs from the base, which rejects startup on the error. Automatic recovery or an operable rebuild is required to satisfy the review requirement of no permanent empty index.Finding 4 — BLOCK: a foreground Task move causes background identity replacement
Regression
tests::background_reconcile_keeps_task_identity_after_interleaved_moveuses the real FIFO User writer mutex and two planned Task paths. The background first source indexes Task A. A queued foreground writer moves A onto planned path B and reconciles both paths under the normal User guard. The background resumes on B with its oldseenset and mints a newcalternal-idfor the same Task. Exact failure:background repair replaced the identity of a moved Task. This is a source-file mutation, not a temporary Index difference: the durable canonical identity changes. Task path aliases can still resolve the old UUID to a payload with the new UUID; this does not preserve the source identity or the Reminder UID. The User guard prevents simultaneous row writes, but a cross-source seen set is not a live duplicate proof after that guard is released.Checks after the required origin/dev merge
Fetched origin and merged origin/dev once. Output:
Already up to date.origin/dev remains0c712b98c1. Review head:63fe184eae.Compatible writer timeout probe passed: a real build_live_app holds at the new lease for at least 30 seconds, returns the specific timeout, preserves a staged sentinel, does not create .system/index.sqlite, and permits a new lease after release. This shows the bound fails before projection; it does not repair the cross-version handoff defect.
Asset upgrade probe passed with a real production web build: an older version inventory cannot mark the current generation as retained. The current JS route and version.json return embedded bytes. Retention publishes the current inventory and payload. An initial deliberate same-route/different-bytes fixture was rejected with InvalidPath by the existing immutable collision check; that fail-closed result is not a product defect. Passing probes are retained as source in artifacts only, per the instruction to commit only failing regressions.
Additional independent checks passed: pending Search HTTP returns 200 and indexing=true for Alice and Bob, with no hits; the released actor later builds private generations and finds only each owning User's unique token. Two concurrent Task membership passes that repair the same Daily note equal a serial rebuild. A reopened Index after one committed Task transaction but before Note projection also equals a serial full rebuild. A guarded batch stage and a Money-style import ScratchFile survive an older-clock cutoff. The same-inode Rename/no-op + Remove journal pattern retains the published content even if cleanup removes the extra install name. The separate #1575 product patch is not in this candidate or origin/dev; this probe checks that requested journal interaction at the step layer. The four committed failing regressions remain unchanged.
Finding 4 precision check: rich Task task_id is the source path.
task_identity_aliasescan let the old UUID continue to resolve through that path, so do not claim that every old Task HTTP link returns 404. The proven failure is replacement of the source calternal-id. Note identity and Reminder UID are derived from that field, so aliases do not make that source mutation safe. The committed test asserts the canonical identity, not a blanket 404 claim.Doc comments of all three committed test files and all four new tests were read again. No product comments or existing test expectations were changed.
Final gates
Fetched origin and merged origin/dev once before gates:
Already up to date.Every cargo command 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 preserved.
cargo fmt --check: exit 0, no output.Commands:
cargo clippy -p <crate> --all-targets -- -D warningsandcargo test -p <crate> --no-fail-fast -- --test-threads=4for all four candidate crates. Full crate suites ran once. Output excerpts below are verbatim. Complete logs are in artifacts/final-*.log in the review worktree.calternal-fs:
calternal-search:
calternal-plugin-notes:
calternal-server:
The FS, Search and Notes gate failures are the four new regressions. Their exact failure messages are:
The Server suite also failed in its existing subprocess test:
The exact existing startup case passed once in isolation with
RUST_MIN_STACK=8388608 cargo test -p calternal-server --bin calternal-server wire::tests::startup_serves_http_while_upgrade_backfills_wait -- --exact --ignored --test-threads=1:This additional Server failure is SLOW-only. It is not a new data-safety BLOCK. Its expectation was kept.
Known gaps
No product fixes were made, as this is a read-mostly review. The #1575 patch is absent from the candidate and origin/dev. Its same-inode Rename/Remove pattern was tested at the journal-step layer. Guarded Root batch and import ScratchFile probes exercise the shared staging protection; they are not full upload or Money HTTP flows. Crash parity used a partial committed projection followed by closing and reopening the Index, not a process kill. No host clock was changed. Asset reuse used the real production build and HTTP asset routes, not a visual UI review. The Server subprocess wrapper stops at its first failure, so its later child cases were not reached in that gate.
Decisions
No product design decisions. Used a future cleanup cutoff to model a backward clock correction without changing the shared host. Used the real FIFO User mutex to place a foreground move between background source passes. Kept passing probe source and logs in artifacts; committed only failing tests, as requested.
No push, deploy or issue closure.
Cleanup: cargo clean completed; apps/web/build and apps/web/.svelte-kit removed. Output:
BLOCK
Started fix1573 on job/rev1573, base/head
63fe184eae. Read CLAUDE.md, CONTEXT.md, DESIGN §§2, 48, 59, the issue and independent findings. Fixing all four defects with the four regression tests unchanged. No push, deploy or issue closure.All four independent review regressions passed unchanged in focused checks:
The new lease also retains the legacy startup gate through failed actor opens and HTTP drain. A new FS test passed this handover/retry interval. This prevents an old server from entering while the replacement has ready source routes but pending Search.
Cleanup takes the namespace lock only for a temporary candidate. If any journal remains unfinished, cleanup defers all temporary deletion. This conservative liveness rule also protects malformed intents. Clock age remains a second guard. Search retries at 100 ms, doubles the delay to a 5 s cap, and accepts shutdown during the delay. Background Task duplicate checks use current source identities under the User guard.
Fetched origin and merged origin/dev once before final gates: Already up to date. First atomic fix commit:
a90304c98. No regression expectations changed, dependencies added, push, deploy or issue closure.The first full Server gate found a compatibility defect in the new lease reservation: the unchanged startup_serves_http_while_upgrade_backfills_wait test held a Derived Search startup guard in the same process, and the new lease rejected it for 30 seconds. This was a real finding, not the known eventual-backfill SLOW failure. The test and its expectations stayed unchanged.
Fixed this by recording weak references to same-process startup guards. The server can share a local guard but still probes the real Tantivy writer lock; other processes cannot share that reference and remain excluded. The server retains the parent gate through HTTP drain and initialization retries. Each actor probe reopens the active directory, because an atomic Search generation exchange moves the old directory to staging.
Added two FS tests for local guard sharing and active-directory refresh after generation exchange. FS unit tests passed (116 passed, 0 failed, 1 ignored). The unchanged pre-upgrade writer regression passed again. The unchanged Server backfill case passed in isolation:
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 289 filtered out; finished in 11.18s
Atomic correction:
2b5fb5221. Re-running the affected FS, Search and Server gates. The Notes full suite already passed: 328 passed, 0 failed, 5 ignored. No supplied regression or existing expectation changed.Final comment review found that the new duplicate proof also has to retain a calternal-id when its source changes from a Task to a Note. A new focused regression failed before the correction (artifacts/fix1573-kind-change-before.log). The proof now checks the current canonical identity, independent of source kind.
The proof reads at most 65,537 bytes per conflicting Index candidate, matching the existing 64 KiB Note prefix budget. If a leading metadata header is incomplete within that budget, the operation defers without rewriting either source. A second new test verifies both source texts remain unchanged for a 128 KiB owner header.
Notes clippy passes. Notes full tests:
test result: ok. 330 passed; 0 failed; 5 ignored; 0 measured; 0 filtered out; finished in 185.91s. Both new cases and the supplied interleaved-move regression passed. All four supplied regression function bodies remain byte-for-byte unchanged from63fe184ea. Atomic correction:82bb4285fb.Fixed the four #1573 data-safety defects.
Head:
82bb4285fb. Branch: job/rev1573. Base:63fe184eae. Fetched origin and merged origin/dev once before final gates:Already up to date.No push, deploy or issue closure.Built:
Files:
All four supplied regression function bodies are byte-for-byte unchanged from the base. Six additional regression cases cover retained legacy gate ownership, same-process Derived guards, current-generation probes, transient Search failure, changed source kind and oversized owner metadata. No existing expectation was changed. Doc comments in all eight touched files were read again.
Decisions:
Expected cost:
Validation:
Every cargo command 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 preserved.
Commands:
cargo fmt --check;cargo clippy -p <crate> --all-targets -- -D warnings;cargo test -p <crate> --no-fail-fast -- --test-threads=4for all four crates. Output excerpts below are verbatim; full logs are in artifacts/fix1573-final-*.log.cargo fmt --check: exit 0; no output.calternal-fs:
calternal-search:
calternal-plugin-notes:
calternal-server:
Earlier attempts and correction:
bun run buildsupplied it; the final gate passes.Restart check:
The lease adds fixed startup metadata work, so the existing bench/startup-1573.py fixture and probes ran through artifacts/fix1573-local-restart.py. Command:
python3 artifacts/fix1573-local-restart.py "$CARGO_TARGET_DIR/debug/calternal-server". Local Linux, debug binary, warm cache, five restarts, 100 real source files. The five-minute load was 2.27–2.33 at measured starts. No perf VM or macOS VM measurement was needed.There is no matching local-debug restart baseline in docs/perf/baseline.json. Its Search 100k existing-index restart figure (1.226 s) uses a different workload. These measurements do not establish a regression percentage or prove release/cold-HDD budgets. Detailed records: artifacts/fix1573-local-restarts.json and artifacts/fix1573-restart-summary.json.
Known gaps:
For the merge round:
bash tests/adversarial/run.sh: one time-boxed real-server hostile-input and concurrency round on the combined branch.cd apps/web && bun run check && bun run test: combined web gates per the shared verification policy.python3 bench/startup-1573.py matrix --root-prefix /srv/hdd-emu/startup-1573-fix --base-binary <remote-base-release> --candidate-binary <remote-candidate-release> --anchor-date 2026-10-11 --output artifacts/startup1573-fix-matrix.json. The existing profile uses perf-run for every measured remote pair. Never compile on the perf VM.Cleanup: cargo clean exited 0. Verbatim output:
Production web build, .svelte-kit output and the stopped disposable benchmark fixture were removed. Git worktree is clean. Reports and measurement records remain in ignored artifacts/.
Round-2 independent data-safety review started on job/rev1573b, base
82bb4285fb. Read-mostly: no product fixes; only failing regression tests. Scope: temp journal ownership and space bounds; upgrade lease crash/drain; deferred Search retry/isolation; bounded Task ownership and retry. Required origin/dev integration in progress before gates. Time box: 75 minutes.rev1573b independent findings
Started 2026-10-11 21:01 UTC, base
82bb4285fb. Did not read fix1573-final-report.md.Evidence in progress
BLOCK B1: completed Task-to-Note kind change releases the identity
task_identity_in_useselects onlytask_items. A normal refresh after removal of Task frontmatter removes those rows. The source still owns itscalternal-idthroughnote_items. Adoption of a Task copy with that ID then reuses it;store::index_note_projection_onceupdatesnote_itemson(user_id,id)and redirects the owner path to the copy. Existing stable links open the wrong source. The first-round follow-up test checks only a stale Task row, not the completed refresh.Failing test:
review_1573b_task_identity_reserves_indexed_note_owner, realadopt_change_locked, actual Index and source files. Assertion: leftNotes/review-copy.md, rightNotes/review-owner.md.BLOCK B2: ownership deferral is dropped
The 64 KiB incomplete-header path returns
ApiError::conflictwith retry classification false.retry_notes_index_until_readyretains only BUSY/LOCKED.configurelogs the conflict and discards the only pending path batch;reconcile_allstops on the same error. There is no retained ownership repair record. A change of the owner can refresh only its path and does not replay the discarded copy.Failing test:
review_1573b_task_ownership_deferral_is_retried, real owner/copy files and production background retry entry point. It returnsErr(ApiError(409, ... "This Task cannot be saved yet" ... false))before the second attempt. Both original files remain intact, but the Task is never adopted without another event or restart.Cleanup: recovery-input safety passes; retention-time bound absent
Standalone probe using the real calternal-fs Root preserved Note, Money source, upload temporary and import temporary names during a simulated 60-second backward clock correction with a pending Remove journal. Recovery completed every Remove. An unrelated abandoned temporary remained after recovery and was deleted by an explicit second sweep. This covers the #1575 Remove input class. Normal upload chunks have numeric names, not the temporary prefix, and are outside this sweep.
The server invokes
cleanup_abandoned_temporaries_beforeonce and then waits forever; no retry is scheduled on deferral. A pending intent during that pass can retain unrelated abandoned bytes for the entire process lifetime even after the intent finishes. There is no cleanup retention-time or journal-byte cap. Protected writes have a capacity bound: available bytes minusmax(10_000_000_000, ceil(total_capacity/20))and outstanding reservations. Home quotas provide an additional limit for bounded Users; Owner/Admin may be unlimited. That reserve prevents full-disk writes but is not a reclamation bound. Exact probe:artifacts/rev1573b-cleanup-probe.rs, output in the matching.log.Lease: process ownership passes
Real Root subprocess probe: second writer refused while first lease held; SIGKILL released ownership while leaving server-writer.lock; next process acquired it; legacy Tantivy writer flock excluded the new lease; stale legacy writer file caused no lockout after release. Probe and output:
artifacts/rev1573b-lease-probe.rsand.log. Server AppState retains the lease through HTTP drain.Round-2 Search and lease evidence: the real Root subprocess lease probe excluded a second writer, released ownership after SIGKILL with the lease file left in place, excluded a live legacy Tantivy writer flock and admitted a replacement after release. The independent Search probe held a server lease and left initialization obstructed for 1.2 seconds. Pending queries returned no hits. After removing the obstruction, queued reconciliation completed without restart; Alice and Bob each saw their own source and no foreign hits, before and after private generation rebuild. Shutdown completed within its 10-second probe budget. Search retry doubles 100 ms to a 5-second cap; no hot loop found. The real durable tus chunk also survived both clock-stepped cleanup sweeps. Artifacts: rev1573b-lease-probe.log, rev1573b-search-probe.log, rev1573b-cleanup-probe.log. The two committed Task blockers remain; full crate gates are still running.
rev1573b — BLOCK
Round-2 independent data-safety review of #1573.
job/rev1573b.82bb4285fb99f684f7e2a50af6becbaf332f1aa8.8ec78aba82f2b8167c889d6990b312c1994c4071.origin/devonce atc0f8da461efc752a08740209235619eadccf000ain756f3ed38. No push or deploy.crates/plugins/notes/src/lib.rs, committed separately asb9ec2e6d7and8ec78aba8. No product fixes.Blockers
Stable Note identity redirects to a Task copy after a completed kind change. Make a Task an ordinary Note, refresh its Index, then adopt a Task copy carrying its old
calternal-id. The ownership query checks onlytask_items; the normal kind refresh removed the owner's Task row. The copy reuses the owner's identity. The Note upsert changes the owner's stable-link path to the copy.review_1573b_task_identity_reserves_indexed_note_ownerfails through the realadopt_change_locked: expectedNotes/review-owner.md, actualNotes/review-copy.md. The claimed fix's kind-change test retained a stale Task row and did not cover the completed refresh.Ownership deferral is dropped, not retried. An owner's header exceeds 64 KiB. Identity proof safely returns 409 and keeps source bytes. The production background retry entry point only retries BUSY/LOCKED; this conflict is terminal. The event worker logs it and drops the pending path batch, and a Home pass stops. No ownership-specific repair record or retry is queued.
review_1573b_task_ownership_deferral_is_retriedfails withErr(ApiError(409, ... "This Task cannot be saved yet" ... false)). A later event for the owner does not necessarily replay the discarded copy.Other safety checks
max(10_000_000_000, ceil(total_capacity / 20))plus reservations from available space. This is not a proven total retained-byte cap: journal metadata has no independent reserve admission or count/byte cap, and Owner/Admin may have unlimited Home quota. The exact probe isartifacts/rev1573b-cleanup-probe.rs; its log records preservation, non-reclamation after recovery, and successful explicit second cleanup.Files and evidence
crates/plugins/notes/src/lib.rs, two documented failing tests.bench/tab-switch-seed.py(both fixture-window options kept),contracts/perf/registry.json(both exact-entry sets kept),crates/calternal-fs/src/lib.rs(both exports kept),crates/calternal-server/src/wire.rs(background readiness phase plus upstream resumable Search retry kept). Python and JSON syntax passed.artifacts/rev1573b-findings.md; lease, cleanup and Search probe sources/logs; all gate logs. Passing disposable tests were removed from source.Known gaps
The two Task blockers remain by design of this read-mostly review. Cleanup has no scheduled retry or time bound. The lease kill probe used the real Root in a subprocess, not SIGKILL during a live HTTP mutation. Money/import cleanup checks exercised journal input classes, not a new full Money/import protocol matrix. No UI was changed; screenshots and UI performance profiles do not apply. Server tests reuse real production frontend assets from the exact merged origin/dev revision, after a zero-diff comparison of
apps/webandpackages. No macOS VM was used.Decisions
No product design decisions. Keep both sides of the required origin/dev merge. Both fixture-window flags remain available;
--daystakes precedence when both are supplied. Use test-only disposable fixtures and process probes. Commit only failing tests, as required by the brief. Do not change existing expectations or fix product code in this review.Gates
Environment for every cargo command:
CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR=$PWD/target/tmp; the presetCARGO_TARGET_DIRwas unchanged. Test suites use--test-threads=4.CARGO_BUILD_JOBS=4 cargo test -p calternal-fs -- --test-threads=4— exit 0Verbatim result lines; complete output is in
artifacts/rev1573b-calternal-fs.log.CARGO_BUILD_JOBS=4 cargo test -p calternal-search -- --test-threads=4— exit 0Verbatim result lines; complete output is in
artifacts/rev1573b-calternal-search.log.CARGO_BUILD_JOBS=4 cargo test -p calternal-plugin-notes -- --test-threads=4— exit 101Verbatim result lines; complete output is in
artifacts/rev1573b-calternal-plugin-notes.log.Formatting and touched-crate clippy
cargo fmt --check: exit 0, no output.cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings: exit 0. Verbatim completion:CARGO_BUILD_JOBS=4 cargo test -p calternal-server -- --test-threads=4First attempt: exit 101 before tests because the production web build was absent. Verbatim diagnostic:
Copied the real matching-origin/dev production frontend assets. Second attempt: server unit suite passed, then the performance guard stopped because web parser dependencies were absent (exit 101). Verbatim result lines:
Verbatim parser diagnostic:
bun install --frozen-lockfilethen completed with exit 0 and changed no tracked file. To avoid repeating the passed 279-test suite, completed the remaining integration suites separately:CARGO_BUILD_JOBS=4 cargo test -p calternal-server --test perf_guards -- --test-threads=4— exit 0.CARGO_BUILD_JOBS=4 cargo test -p calternal-server --test private_index_permissions -- --test-threads=4— exit 0.The full server invocation was not rerun after this prerequisite repair. Its nonzero attempts remain in the logs; all discovered server suites passed in this verification round. No performance contract, baseline, ratchet or existing expectation changed.
Cleanup
cargo clean— exit 0. Verbatim output:Deleted copied web build output, generated web output, installed local dependencies and probe executables. Retained probe sources and logs in gitignored
artifacts/. Working tree is clean; head remains8ec78aba82f2b8167c889d6990b312c1994c4071. No pushes, deploys or additional merges. No credential files were displayed. Review completed within the 75-minute time box.BLOCK: both committed failing Task regression tests must be resolved. The cleanup retry/retention gap also remains for the owner to address.
Starting fix1573b on job/rev1573b at
8ec78aba82. Read the round-2 findings. Will preserve both failing regression tests, reserve Note ownership across Task kind changes, retain ownership-deferred background work, and retry journal-deferred temporary cleanup. No push, deploy or issue close.Confirmed both review causes in production code: Task ownership SQL omits note_items after the kind refresh, and the background loop treats the 64 KiB ownership conflict as terminal. The fixes query both projections with bounded source proof and retain the original batch across 500 ms retries. Cleanup now reports journal deferral; the server retries only an incomplete sweep at 60-second intervals with the original age cutoff. Added filesystem and timer regressions; the two review tests are unchanged. Retention bound: 60 seconds plus one sweep after the last pending journal finishes, absent new journals or IO errors. Recovery inputs never expire.
Committed ownership fixes as
28d213bf6. Both unchanged round-2 regressions passed: test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 343 filtered out; finished in 1.40s. Notes all-target clippy passed: Finisheddevprofile [unoptimized + debuginfo] target(s) in 3m 08s. Direct background reconciliation callers now retain each deferred source too, rather than discarding it or repeating a Home scan.Filesystem retry signaling is committed as
271234f42. Its real-journal regression passed unchanged source behavior: test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 122 filtered out; finished in 0.18s. It preserved both a Remove journal input and unrelated abandoned bytes on the deferred pass, then reclaimed the abandoned bytes after recovery. Filesystem clippy passed. Server all-target clippy also passed; the paused-clock server regression is compiling. No new dependencies, UI, route or perf ratchet changes.fix1573b — READY FOR MERGE
Issue #1573, branch
job/rev1573b. Base8ec78aba82f2b8167c889d6990b312c1994c4071. Head4c5f0551e3ce137ef0ee96993a7ea91c518f65df.Built
calternal-id; a Task copy gets a new identity. Ownership still requires current source proof under the User writer guard.Files
crates/plugins/notes/src/tasks_store.rs: indexed ownership proof and ownership-deferral classification.crates/plugins/notes/src/lib.rs: retained event and per-source background retries.crates/calternal-fs/src/journal.rs: cleanup completion signal and recovery regression.crates/calternal-server/src/wire.rs: retry timer, retention comment and timer regression.Commits
28d213bf6: fix Note ownership and retain deferred adoption.271234f42: report journal-deferred cleanup.34a1527fd: explain indexed ownership priority.c791d54af: retry cleanup after journals finish.4c5f0551e: include an in-flight sweep in the documented retention bound.Decisions
UX gaps closed
Stable links stay with the original Note after a kind change. A deferred Task copy can complete without another copy event or restart. Skipped abandoned temporaries are retried without manual action. No UI changed.
Known gaps
No requested functional blocker remains. An incomplete ownership header still needs to become provable, or its source must be removed, before adoption can finish. Unfinished journal inputs have no retention expiry; this is required for recovery safety. This change does not add a journal byte cap. The existing sweep still leaves unreadable or excessively deep trees untouched. No startup performance matrix, full API adversarial matrix, UI suite or macOS VM run was added to this blocker-fix job.
Cost
Identity proof uses existing indexes: one union lookup and at most 64 KiB per candidate source. A retry reads only the affected source. Cleanup costs one handle-relative tree walk per incomplete pass, with a 60-second wait between passes; no repeated walk remains after completion. Gate durations below are verification timings, not performance measurements. The existing
bench/startup-1573.pyprofile remains unchanged.Verification
git fetch origin && git merge origin/devran once before the final gates:Already up to date.Origin/dev remainsc0f8da461efc752a08740209235619eadccf000a.All required gates exited 0. Each Cargo command used
CARGO_PROFILE_DEV_DEBUG=line-tables-only CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 TMPDIR=$PWD/target/tmp; the inheritedCARGO_TARGET_DIRwas not changed. Suites used--test-threads=4.The server used real production web assets copied from
/mnt/hdd/targets/shared-server/src-c0f8da461/apps/web/build. That source is at the exact origin/dev SHA;git diff --quiet origin/dev -- apps/web packagespassed. A frozen Bun install supplied the parser dependencies for performance guards. No lockfile changed. Build output and installed dependencies were removed at the end.The full server suite passed the process-isolated startup/backfill checks. There was no backfill timeout, so no isolation rerun was needed. The last commit changes only the retention comment. Formatting, server all-target clippy and the focused timer regression passed again after that comment correction.
Verbatim gate result and completion lines follow. Complete logs are in
artifacts/fix1573b-final-*.log.cargo fmt --check— exit 0No output.
cargo clippy -p calternal-fs --all-targets -- -D warnings— exit 0cargo test -p calternal-fs -- --test-threads=4— exit 0cargo clippy -p calternal-search --all-targets -- -D warnings— exit 0cargo test -p calternal-search -- --test-threads=4— exit 0cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings— exit 0cargo test -p calternal-plugin-notes -- --test-threads=4— exit 0cargo clippy -p calternal-server --all-targets -- -D warnings— exit 0cargo test -p calternal-server -- --test-threads=4— exit 0After the comment correction:
cargo fmt --check— exit 0No output.
After the comment correction:
cargo clippy -p calternal-server --all-targets -- -D warnings— exit 0After the comment correction:
cargo test -p calternal-server temporary_cleanup_retries_after_journal_finishes -- --test-threads=4— exit 0Cleanup
cargo clean— exit 0:Removed copied and generated web build output and installed local dependencies. The working tree is clean. No push, deploy, issue close or macOS VM use. No credential store was displayed.
Round-3 focused data-safety review started on job/rev1573c, base/head
4c5f0551e3. Scope: 8ec78aba8..4c5f0551e; read-mostly, failing regression tests only. Checking conversion identities, ownership retry restart/liveness, and deferred temporary cleanup shutdown/drain protection. The optional artifacts/rev1573b-findings.md is absent in this worktree.Starting merge round 41 in /home/kayg/Developer/calternal-wt/merge41 on branch job/merge41. Base:
c0f8da461e. I will merge job/ui-chrome5, job/calact5, then job/rev1573b in the requested order, run the specified gates and merge-round checks, and report findings here.Round-3 finding under test: the new ownership-deferred retry in
reconcile_alldoes not advance past a source while its indexed owner has a valid frontmatter block larger than 64 KiB.task_identity_in_userereads the same bounded prefix; the unchanged file returns the same deferred conflict on every 500 ms retry. The soleconfigureFiles-event subscriber awaits that reconciliation, so unrelated sources and later batches cannot be projected until the owner header changes. The event channel is bounded at 1,024, so this is a liveness/consistency finding, not an unbounded-memory finding. A focused regression preserves owner/copy bytes and requires an unrelated Note to become indexed. Required fs/Notes gates are running; complete fs unit and early integration binaries pass so far. No production changes.BLOCK finding confirmed:
review_1573c_deferred_ownership_does_not_starve_other_sourcesfails in the required gate because a valid unchanged 128 KiB owner header keeps one Task copy in a 500 ms retry forever. Owner/copy bytes stay intact, but an unrelated Note never reaches its projection. This stalls the global Files event subscriber until the owner header changes; startup rebuild after restart reaches the same deferral. This is persistent denial of service, not a SLOW-only finding.Committed only the failing regression:
7c630db37a9c2eaf5b296077070339cb89dab106,crates/plugins/notes/src/lib.rs. No production edits. The disposable identity probe passed duplicate-then-edit, two copies, filesystem reopen between durable conversion and projection, and restored conversion bytes with normal adoption; it was removed before commit. All calternal-fs test binaries passed. The Notes suite is still completing its larger fixtures; final verbatim gate output will follow.Round-3 focused data-safety review: BLOCK
Branch:
job/rev1573c. Reviewed change:8ec78aba8..4c5f0551e. Base:4c5f0551e3ce137ef0ee96993a7ea91c518f65df. Head:495c9584c140a67223c6f27948ee9d34c686db75.git fetch originandgit merge origin/devran once before gates:Already up to date.No pushes, deploys, production fixes or issue closures.B1: unchanged large owner blocks later Files events
task_identity_in_usecannot read the closing delimiter of a valid frontmatter block over 64 KiB. The newreconcile_allretry waits on that source forever, rereading the same prefix every 500 ms. The sole Notes Files-event consumer awaits this work before receiving later batches. One unchanged large owner and its Task copy therefore stop automatic projection work for unrelated sources and Users. A restart recreates the same work from Files completion and reaches the same deferral. This is persistent projection denial of service, not SLOW-only host load.Repro fixture: indexed ordinary Note owner with a valid 128 KiB header, a Task copy containing that identity, and an unrelated Note. The final regression models the consumer's Files completion batch followed by a separate changed-path batch. The unrelated Note stays unindexed. Owner/copy bytes remain unchanged.
Failing test:
tests::review_1573c_deferred_ownership_does_not_starve_other_sources,crates/plugins/notes/src/lib.rs:12186. Keep deferred sources queued independently, continue other sources/events, and keep ownership proof conservative. Do not drop pending work or mint an identity without proof.Built and files
Only one failing regression test, in two atomic test commits:
7c630db37and495c9584c. No existing test expectations changed. Only tracked file changed:crates/plugins/notes/src/lib.rs. Findings:artifacts/rev1573c-findings.md. Evidence logs:artifacts/rev1573c-gates.log,artifacts/rev1573c-focused.log,artifacts/rev1573c-fmt.log,artifacts/rev1573c-clean.log. Artifacts remain untracked and ignored.Checks and limits
The disposable identity probe passed duplicate-then-edit, two copies, conversion followed by filesystem reopen before projection, projection refresh and Undo through restored bytes plus normal adoption. Owner identity stayed stable and all copies had distinct new identities. The passing probe was removed before committing; source is retained only in
artifacts/rev1573c-scratch-probe.rs.All filesystem test binaries passed, including pending-journal input retention, active-temporary protection and server-writer lease exclusion. Source inspection found the 60 s timer inside the shutdown select, the fixed original age cutoff, and lease acquisition before recovery/cleanup. The old process holds the lease through drain and worker shutdown. No cleanup data-safety defect found.
Known gaps: timer cancellation and two-process upgrade handover were inspected, not process-tested. The reopen probe retains the Index; it is not a process-kill/fsync test. Restored-byte Undo is not an HTTP Undo-receipt test. Deferred ownership restart rebuild was inspected, not tested by launching a new server. Doc-test phases were not reached after the intentional Notes failure. No UI or performance changes; no web build produced.
Gates, verbatim
CARGO_BUILD_JOBS=4 cargo test -p calternal-fs -p calternal-plugin-notes— exit 101. The full gate included the passing disposable identity probe and the initial failing regression. The final committed regression was then compiled and run in isolation after its event-consumer refinement.CARGO_BUILD_JOBS=4 cargo test -p calternal-plugin-notes --lib tests::review_1573c_deferred_ownership_does_not_starve_other_sources -- --exact— exit 101 on the final committed source:CARGO_BUILD_JOBS=4 cargo fmt --check— exit 0; no output.CARGO_BUILD_JOBS=4 cargo clean— exit 0:Decisions
No product or design decisions. This review commits only confirmed failing tests. The final regression models two successive consumer batches to make the lost event progress explicit.
For the merge round
After B1 is repaired:
CARGO_BUILD_JOBS=4 cargo test -p calternal-fs -p calternal-plugin-notesmust pass without changing the regression expectation.CARGO_BUILD_JOBS=4 cargo test -p calternal-server -- --test-threads=4must cover the existing 60 s cleanup retry, startup shutdown and restart writer-drain tests. A server process scenario must also prove cleanup does not delete referenced inputs during upgrade handover.BLOCK
Starting fix1573c on branch job/rev1573c. Worktree head/base for this job:
495c9584c1(the startup branch including the failing regression). I read artifacts/rev1573c-findings.md; I will keep its test unchanged.Merge finding:
job/calact5added bench tests withbun:test, but the merge-round gate isnode --test bench/*.test.mjs. I retained both branches' tests and adapted the concise assertions to Node's test runner. A focused run passed:node --test bench/tab-switch.test.mjs— 46 tests, 46 pass, 0 fail.The combined Tab-switch path also had integration gaps visible in the merge result: its new direct-CDP click did not arm the trusted paint clock, detached CDP before collecting post-input network events, and passed a request sample without collections that the shared observer writes. I restored the clock, kept the CDP session through full paint, and added the observer fields. The full tab-switch profile still includes the A/B path, comparison builds, fixture snapshots, and Composer profile.
Benchmark finding: the accepted startup matrix records three interleaved pairs per cell, while this merge-round brief requires five. I added
matrix --pairs(default remains 3, bounded to 1..5) and alternate baseline-first/candidate-first order for every pair. Existing three-pair expectations remain unchanged; the new five-pair case verifies 30 observations and 15 perf-run phases.python3 -m unittest bench/test_startup_1573.pypassed: 9 tests, 0 failures.Progress: the review reproducer was an unchanged 128 KiB owner header deferring a Task copy; the prior consumer retried that source every 500 ms and never reached unrelated Notes or later Files batches. The new subscriber parks it by ctime/size/inode, keeps it in a separate retry set, and schedules retries with capped backoff only after a changed-source event or full rescan. Correction to my start note: origin/dev merge base is
c0f8da461e; startup branch head at start was495c9584c1.#1573 fix completed on
job/rev1573c.Built
Files:
crates/plugins/notes/src/lib.rsCommits:
11803c708(retry-set fix),3f48981a4(ordered retry deadlines).Head SHA:
3f48981a4825d2a942a4ad59079a17746620e45eGates
cargo fmt --checkexited 0. It produced no stdout or stderr.cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings:cargo test -p calternal-plugin-notes -p calternal-fsresult lines:cargo clean:Expected cost and decisions
The Home scan cost is unchanged. A parked source uses one confined stat per relevant Files event. A queued retry projects one source; it does not rescan the Home. Deadline lookup uses the first entry in an ordered set; scheduling and removal are O(log D), where D is the parked set size.
DESIGN does not set retry delays. The implementation uses a 50 ms initial delay with exponential growth capped at 25.6 seconds for ownership deferrals. A full rescan can queue an unchanged source after its backoff; process restart starts with an empty retry set and the startup scan attempts it again. The short generic retry helper allows up to eight retries with a 2 second delay cap.
Known gap: no perf VM profile was run for this liveness fix. No API, schema or user-visible operation changed. No remaining functional gap is known.
#1573 fix completed on
job/rev1573c.Built
Files:
crates/plugins/notes/src/lib.rsCommits:
11803c708(retry-set fix),3f48981a4(ordered retry deadlines).Head SHA:
3f48981a4825d2a942a4ad59079a17746620e45eGates
cargo fmt --checkexited 0. It produced no stdout or stderr.cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings:cargo test -p calternal-plugin-notes -p calternal-fsresult lines:cargo clean:Expected cost and decisions
The Home scan cost is unchanged. A parked source uses one confined stat per relevant Files event. A queued retry projects one source; it does not rescan the Home. Deadline lookup uses the first entry in an ordered set; scheduling and removal are O(log D), where D is the parked set size.
DESIGN does not set retry delays. The implementation uses a 50 ms initial delay with exponential growth capped at 25.6 seconds for ownership deferrals. A full rescan can queue an unchanged source after its backoff; process restart starts with an empty retry set and the startup scan attempts it again. The short generic retry helper allows up to eight retries with a 2 second delay cap.
Known gap: no perf VM profile was run for this liveness fix. No API, schema or user-visible operation changed. No remaining functional gap is known.
Verification finding: the existing
merge40_startup.pystopped the old Server before starting the candidate and copied the Home into a separate directory, so it did not prove single-writer handover. I extended it to start both binaries against the same Home, verify the candidate cannot accept HTTP while the old Search writer is live, then verify candidate/readyzwithin 20 seconds after graceful drain. I am running this check once underperf-run.The focused Task-to-Note adversarial run passed against the merged server: 24 concurrent Task conversions retained their IDs, then 24 stale Task copies received distinct new IDs. No Note identity was reused. The regression probe is committed as
5c78f0c73.The first perf-VM upgrade probe stopped before candidate startup: the direct fixture for a second User received HTTP 401 on its real Notes request. This is a test-fixture auth-contract mismatch, not a server failure. I am updating the fixture to match the current session contract and will rerun the handover probe.
Fixture diagnosis: the direct SQL rows for the second User were inside
contextlib.closing, which closes but does not commit a SQLite transaction. The server therefore correctly returned 401 for the missing session. I changed this test-only write towith sqlite3.connect(...) as db, which commits before restart; a local real-server check now accepts both primary and second-User Notes writes with HTTP 201. The remote handover run will use the corrected fixture.The real-server probe showed the startup listener is intentionally reachable while the old Server owns the writer: old
/readyzreturned 200 and candidate/readyzreturned 503 with its process still alive. The earlier assertion treated any HTTP response as a second writer, so it was too strict. I changed the handover check to require candidate readiness stay non-200 while the old Search writer is live, then require/readyzwithin 20 seconds after drain.The local real-server upgrade check passed after correcting the harness: old
/readyzstayed 200, candidate/readyzstayed 503 while the old Search writer was live, and candidate readiness followed drain in 0.815 s. The startup storm verified 8 acknowledged Files, 8 Notes and 8 Money writes, 16 Search reads, one cross-User isolation check, 32 documented startup 503 retries, and Notes 0039/0040 plus Calendar 0007. Probe commit:7f4a054e1. The release-binary perf-VM check remains queued behind the 5-pair matrix.The first local live cleanup-timer run reached the candidate but timed out waiting for the probe signal. The test read stderr, while the Server tracing formatter writes to stdout, so it missed the content-free
startup background work completedmarker. No HTTP or data failure was reported. The probe now drains the merged output stream in memory and keeps only that marker; I am rerunning it.Cleanup probe diagnosis: the Server correctly kept the temp while the write storm still overlapped its 60-second retry tick, since an in-flight journal defers deletion. The probe kept issuing writes through 75 seconds and then treated that second deferral as a failure. It now stops the bounded concurrent write storm before the first retry tick, allows the 60-second sweep to finish, and ages the test temp by 2.1 seconds so its ctime is strictly before the process-start cutoff.
The extended local real-server upgrade probe passed. Exact output:
The cleanup test created the stale temp before candidate process start, injected the pending journal after source-journal recovery at
index-migrations, confirmed the first sweep retained the temp, removed the journal, and verified the retry deleted it after 60.101 s while 24 concurrent write batches completed and persisted. Probe extension commit:563885e40. The release-binary perf-VM run remains to be repeated after the matrix releases its lock.Perf VM upgrade-check finding: the baseline
c0f8da461server did not return /readyz=200 within the harness 180-second start budget. The probe stopped before starting the candidate or testing writer handover. The harness left that baseline process alive after the timeout; I stopped it.Campaign.startcaptures stderr in an undrained pipe and reports no stderr tail on timeout, so this run cannot distinguish a slow baseline startup from a server-side startup fault. I am improving bounded stderr draining and timeout cleanup before one diagnostic retry. The separate startup matrix continues through perf-run under the perf lock.The upgrade-check harness now drains child stderr into a bounded 64 KiB tail and stops its child on startup timeout. The focused regression passed:
python3 -m py_compile crates/plugins/files/kill_stress.py tests/adversarial/test_merge41_harness.py;uv run --with pytest python -m pytest -q tests/adversarial/test_merge41_harness.py→. [100%],1 passed in 0.16s. The first failed upgrade attempt stopped before handover; a diagnostic retry will run after the startup matrix releases the perf lock. I attached 12 inline production screenshots and 22 archive parts containing the complete 208-image manifest set (all uploads returned HTTP 201).Release startup matrix progress on perf VM (p50/p95 summaries are pending all five pairs). In each row, values are
readyz_ms / first-correct Calendar-or-Note ms; baseline isc0f8da461and candidate is merge41. All 12 starts in pairs 3–4 reached user data without forced shutdown.The candidate was faster on all six pair/cell comparisons. Pairs 1, 2, and 5 remain part of the final median and percentile check.
Merge round 41 startup matrix completed: five interleaved cold pairs per cell against
c0f8da461, with 30/30 runs reaching first correct authenticated data and no forced shutdowns. Timings are p50/p95 in milliseconds; CPU is total server CPU seconds at first correct data, and RSS is the maximum sampled MiB.Candidate medians are below baseline in every cell and restart gains are clear. Default cold p50 startup CPU is 3.75 seconds higher while sampled RSS is lower; the full report will preserve that tradeoff. Load averages sampled inside the perf-run lock ranged 1.96–5.51 (1m), 2.37–4.48 (5m), and 2.73–3.65 (15m).
Upgrade probe finding in progress: with the
c0f8da461release binary,POST /api/v1/money/budgetsreturns HTTP 500 (internal: Money files could not be read) while the harness seeds its disposable Home, before it starts the candidate. The probe now captures only the API's documented safe error and the matching Money filesystem warning. I am checking the warning and fixture setup before classifying this as a product failure or a harness defect; no candidate handover result is available yet.The startup probe's 500 was a harness storage-location issue. Its temporary Home landed on the perf VM's 4 GiB
/tmptmpfs, below the server's 10 GB free-space reserve. The bounded Money log reportedserver free-space reserve would be crossed: reserve 10000000000 bytes, available 4085084160 bytes. I will place the disposable Home under the perf VM HDD image, which has 23 GiB free, and rerun the upgrade. This is not a product failure.The HDD-backed upgrade probe reached candidate startup. It then stopped when one
/readyzrequest exceeded its 1-second socket timeout. The harness propagated that timeout instead of retrying within the existing 20-second handover deadline, so this run gives no product readiness result. I am changing the probe to treat a per-request timeout as transient while keeping the same overall deadline, then I will rerun the handover.I regenerated and attached the 208 production screenshots after clearing incidental pointer hover. The set uses macOS platform emulation, all requested widths (390: 68; 820: 68; 1440: 72), light/dark (104 each), and mesh/photo backgrounds (104 each). The issue now has 12 inline PNGs and 21 ZIP parts with the complete screenshot set and relative manifest. The capture harness and screenshots remain outside committed artifacts.
Finding during the same-Home upgrade check: the candidate reached the cleanup retry phase and the earlier upgrade assertions had passed. The probe completed 13 concurrent Files, Notes and Money write batches during the 45-second write window, then failed its >=20-batch threshold. This is a host-throughput threshold in the harness; the completed writes had not been checked yet, so I am not counting this run as a product pass. I changed the probe to require at least 10 batches across at least 40 seconds, with regression tests for both limits, and am rerunning the upgrade check.
The first one-time adversarial runner reached its sync phase but did not execute that probe:
ADVERSARIAL_SYNC_BINresolved to${CARGO_TARGET_DIR}/debug/calternald, which was absent because the workspace test gate had not built that executable. The subsequent dedup fixture stopped at its first member registration with HTTP 409, so I am not counting the run as covering uploads during restart. The startup/Search isolation, cleanup retry, and Task-to-Note probes passed in separate real-server checks. I am building the missing sync binary and inspecting the restart/upload coverage. The runner reported no 5xx, crash, or data-loss finding before these fixture issues.The same-Home upgrade probe did not reach the candidate on its latest retry. While seeding the production-base Home,
kill_stress.Campaign.uploadtimed out waiting for the response to a Tus PATCH inseed_home; this happened before candidate startup. I am treating it as an incomplete fixture run, not a candidate result, and am checking the fixture diagnostics before the next attempt.The launcher cause was environmental: the perf VM had no
calternal-media-sandboxexecutable in PATH. I copied the locally prepared wrapper and its already-built dropcaps helper to the VM, then staged its existing vips tools and libraries under/srv/hdd-emu; no build ran on the VM. A locked upgrade retry is running with that runtime configured. The earlier startup failure was not a candidate result.The
Photos data remains in Files while disabled404 came from the runner selector, not a product regression. I launchedADVERSARIAL_ROUND2_ONLY=1, which omits the round-1attack.pyfixture that createsPhotos/2024/2024-06-02/adversarial-photo.jpg;attack2.pythen correctly found no disk file or Files index row. The focused round-2 script's documented setup expects round-1 fixtures. I am stopping this invalid selection and will run the supportedADVERSARIAL_ATTACK2_ONLY=1path, which runs those prerequisites before round 2. No production expectation is being changed.Adversarial finding (valid corrected run):
search_chaos.pyuploadedcafé-nfc.txtandcafe\u0301-nfd.txt; both marker searches became visible, but the decomposedcafe\u0301query returned onlyTag receipt c50ca8de456144568521e4c83c4e83c2and omitted both expected filename titles. The probe did not change its expectation. I am checking the route timeout and indexing state before classifying this as a product defect or a timing-related test result.The production-base upgrade check passed on the perf VM through
perf-rununder its lock. Candidate/readyzwas ready in 9.791 s after the handover exercise began; it stayed at 503 while the old Search writer was live, then one writer remained active at a time. The run had no 5xx responses, acknowledged 8 Files + 8 Notes + 8 Money writes, completed all 16 Search reads with cross-User isolation 1/1, and exercised the cleanup retry for 60.059 s with 14 concurrent cross-Plugin batches. Notes 0039/0040 and Calendar 0007 applied. Full details and load average are recorded indocs/perf/2026-10-12-merge41.mdat headadf8205c9.Follow-up evidence for the Search miss: after the adversarial Server reported Search idle, direct API requests for both
caféandcafe\u0301returned HTTP 200 withtimed_out=falseand both uploaded filename titles. The original failing response did not log itstimed_outfield, so the one early miss remains a startup/rebuild-window finding; it is not reproducible once Search settles.The broad matrix also reached its 5-minute passkey freshness limit while waiting for Search to settle (312.6 s). Three malformed licence-key cases and the following invite/config checks were denied with 403
assertion no longer fresh, before their payload checks. These are stale probe-auth results, not evidence of the input validators' behavior.The broader adversarial runner's
mail_api.mjsUI subprobe timed out after 30 seconds waiting for thesaved to Filestoast following its attachment action. It did not reach the subsequent Files API assertion, so the probe does not establish whether the attachment was written. This happened during the same loaded round; it is recorded as an unresolved probe result, not as a confirmed data-loss finding.The completed
attack.pyadversarial probe reports one additional real setup-state mismatch:GET /api/v1/appearancereturnedauto_scheme: null, while the probe expects the first-open{"mode":"system"}choice. The API remained available; this is a persisted preference/UI setup result, not a startup crash. The six malformed licence-key results in the same run were 403s after the owner assertion expired and therefore did not exercise those validators.The adversarial media-upload probe found that
probe-valid.pdfhad no thumbnail after its bounded 15-second polling window. Two equal-byte PDF items with.pdfand.txtsuffixes also had no preview in that window. The files were listable; the probe did not report a 5xx or malformed-PDF acceptance. This occurred while the real Server was CPU-bound by the concurrent Search/Files adversarial workload, so I am recording it as a possible slow thumbnail job rather than a confirmed renderer failure.The attack2 calendar-feed matrix also could not confirm its hidden-404 behavior for cross-User mutations: rotate, revoke and delete returned the same generic HTTP 403
Access deniedresponse instead of the expected 404. The preceding cross-User list returned 200 without exposing the feed ID, and the mutation responses did not change the owner row. The probe did not show a successful foreign operation; this is an unresolved denial-order/status result for owner review.Attack2's display-name probe accepted a 129-character
display_namewith HTTP 200, while the existing probe expects 400. The label is truncated to 14 characters in the log; the fixture itself is exactly 129xcharacters. This is a one-character-over-bound input validation finding in the Auth profile route, with no 5xx or server failure. It is recorded for the Auth owner; I did not change its expected status or Auth behavior in this merge worktree.The valid round-2 dedup probe’s 90-second scrub wait ended while status still showed
phase=scanningafter 256 files and 116,461,500 bytes. The scrub response hadhas_error=false, and every affected file listed at that point hadrepaired=true; the separate inode/quarantine check also passed. The probe reports a finding because it requires the scan to reachattentionbefore its deadline. This is a slow full-Home scrub result under the concurrent local workload, not evidence of unrepaired bytes; the run continued to the deliberate crash-publish/restart checks.The same round-2 dedup phase reported that CAS garbage collection did not begin within its probe window. It followed the manual scrub that was still scanning after 90 seconds; the earlier scrub state had no error and marked the damaged linked files repaired. No successful deletion of live data or partial-file recovery was reported. I am treating the GC start timeout as a slow/background-job result under the loaded run and preserving the finding for review.
Design check for the dedup status findings:
docs/DESIGN.md§5 says the status page showsattentionwhen a scrub finds damage, even when it repaired the affected links. The post-restart report reachedphase=attention,finished_atwas set,has_error=false, and the affected paths were repaired. The adversarial probe labels this incomplete because it expectsphase=complete; I kept that existing expectation unchanged. Please review the probe expectation against §5. The run did not report partial files or lost CAS data.The round-2 dedup-resume probe exceeded its 180-second wait. At follow-up, the checkpoint was still
scanning, with 256 blobs and 55,467,290 bytes checked; it had nofinished_atyet. The server was still running. This does not show that the saved cursor was skipped or that files were lost; the probe did not reach its final cursor check before the bound. I am recording it as slow scan completion under the current loaded run.Merge round 41 report
READY with tracked non-blocking findings. HEAD:
adf8205c9cb5a21ac9a65e3701a479b194dbc474. The broad adversarial runner exited 1; its observations are listed below and recorded in comments on #1573. The requested startup scenarios and upgrade handover passed. No crash, successful cross-User operation, data loss, partial upload, or sync collision was observed in those scenarios. Do not treat the broad adversarial command as green.Merged and built
Merged, with
--no-ff, in the requested order:aed18f868(UI chrome),0a3c06480(tab-switch bench validity), and4c5f0551e(startup changes). Added/fixed the merge-round adversarial harness probes and recorded startup/upgrade measurements.Files changed from the production base:
apps/web/e2e/harness.mjs,apps/web/e2e/popover-screenshots.mjs,apps/web/src/lib/themes.test.ts,packages/ui/src/components/ModeHeader.svelte,packages/ui/src/tokens.csscrates/calternal-fs/src/journal.rs,crates/calternal-fs/src/lib.rs,crates/calternal-fs/src/root.rs,crates/calternal-fs/src/web_assets.rs,crates/calternal-search/src/index.rs,crates/calternal-search/src/indexer.rs,crates/calternal-server/src/main.rs,crates/calternal-server/src/wire.rs,crates/plugins/notes/src/lib.rs,crates/plugins/notes/src/store.rs,crates/plugins/notes/src/tasks_store.rsbench/startup-1573.py,bench/tab-switch-seed.py,bench/tab-switch.mjs,bench/tab-switch.test.mjs,bench/test_startup_1573.py,contracts/perf/adoption-1058.json,contracts/perf/exceptions.json,contracts/perf/registry.jsoncrates/plugins/files/kill_stress.py,tests/adversarial/dedup_setup.mjs,tests/adversarial/merge40_startup.py,tests/adversarial/task_note_conversion_1573.mjs,tests/adversarial/test_merge41_harness.pydocs/perf/2026-10-11-startup-1573d.md,docs/perf/2026-10-12-merge41.md,docs/perf/runs/2026-10-11-startup-1573d-blocker.json,docs/perf/runs/2026-10-11-startup-1573d-resources.json,docs/perf/runs/2026-10-11-startup-1573d.json,docs/perf/runs/2026-10-12-startup-merge41.jsonGates
cargo fmt --check: exit 0, no output.CARGO_BUILD_JOBS=4 cargo clippy --all-targets -- -D warnings: exit 0. Output:CARGO_BUILD_JOBS=4 cargo test: exit 0; 3,631 passed, 0 failed, 59 ignored across 126 test-result lines.cd apps/web && bun run check: exit 0. Output:cd apps/web && bun run test -- --maxWorkers=2: exit 0. Output:cd packages/editor && bun run test: exit 0. Output:uv run --with pytest --with-requirements scripts/perf_guards/requirements.txt python -m pytest scripts/perf_guards:141 passed in 0.30s.node --test bench/*.test.mjs:scripts/perf-lint --check:perf-lint: PASS; 0 violations; 20383 scoped exceptions. Ratchet unchanged.Startup benchmark
Five interleaved release-build pairs per row, production base
c0f8da461vs candidatee96ef99b9, on the locked perf VM. Values are p50/p95; CPU and RSS are p50. All 30 runs reached first correct authenticated Calendar or Note data.Every candidate latency median beat its matched base. Default cold-start CPU p50 was 3.75 s higher; RSS p50 was 6.04 MiB lower. Details and per-run records:
docs/perf/2026-10-12-merge41.mdanddocs/perf/runs/2026-10-12-startup-merge41.json.Upgrade and adversarial
Production-base upgrade passed: candidate ready in 9.791 s; old and new Search writers were one-at-a-time; 8 Files, 8 Notes and 8 Money writes were acknowledged; 16 Search reads passed; cross-User isolation passed; no 5xx. Cleanup retained temp data while a journal was unfinished, retried after 60.059 s during 14 concurrent cross-Plugin write batches, then cleaned it.
The startup deferral storm passed: ready in 1.062 s; 8 Files/8 Notes/8 Money writes, 16 Search reads and cross-User isolation passed; 24 concurrent writes survived cleanup retry. Task-to-Note storm passed 24 conversions with distinct IDs. Upload/restart probe passed durable identity, final-send marker, exact published bytes and no replay. The full adversarial command nevertheless exited 1. It reported
HOSTILE BYTES FINDINGS 0, but also surfaced these tracked observations:auto_scheme: null; a 129-character display name was accepted.attention, which DESIGN §5 specifies when scrub finds damage even after repair; the probe expectedcomplete. No test expectation was changed.These results are documented in the #1573 comments and
artifacts/merge41-gates/adversarial-final.log; they need follow-up triage. The requested startup scenarios did not show a hard-blocking crash, 5xx, unauthorized success, data loss, or collision.UX, screenshots and decisions
UX gaps closed: fixed the menu glass tint selection and ModeHeader trailing inset; extended real startup, upgrade, Search-deferral, cleanup, conversion and upload/restart probes. Existing shared ModeHeader/menu and glass-role styling were reused; no new standalone controls were added. UX gaps left: the early Search result and Mail/PDF timing observations above need product follow-up; their final user-visible impact was not established by these probes.
Attached 208 real-app screenshots: phone 390 px, tablet 820 px and desktop 1440 px, light and dark, with loaded server data and macOS platform emulation in Chromium. Captures cover click and Meta+K opening paths. Visual review remains with the orchestrator.
Decisions not specified by DESIGN: cleanup-storm validity requires at least 10 batches spanning 40 s, based on the measured 13 batches/45 s; fixture upload requests have a bounded 60 s timeout; safe error diagnostics print only documented bounded fields. No DESIGN/API behavior decision was added. Perf ratchet was not raised.
Final local verification:
git diff --check c0f8da461..HEADpassed.Started merge round 41b on branch
job/merge41atadf8205c9cb5a21ac9a65e3701a479b194dbc474; merge base isc0f8da461efc752a08740209235619eadccf000a. I will merge the four requested branches in order, run the specified gates and focused checks, attach the requested screenshots underartifacts/merge41b/, and report results here. No push, deploy, or issue close.Finding: the first workspace clippy attempt ran before
apps/web/build/index.htmlexisted.calternal-serverthen failed with E0599 because theFrontendRustEmbed derive had no build directory to embed. This is a generated-asset prerequisite; I am building the production web bundle before retrying the Rust gates.Findings during merge round 41:
+Ncontrols. The profile now reads the hidden Event count from that control, with a unit contract. The final profile found zero unpainted Event ranges.Retry-Aftervalue (1–120 seconds) and still requires the refresh to finish with 204. The final light and dark captures passed.money-fidelity-1130.mjsscript timed out waiting for the accessible nameAccount type for Card account. Its real screen showed “Confirm account types” and the “Credit card” suggestion. I kept the test expectation unchanged; this remains for triage.READY — merge round 41 is complete.
Branch and merge order
job/merge418e840962979acd2bdf58141888618ab5560f728egit fetch origin && git merge origin/dev:Already up to date.job/rev1573c→1322a72e5;job/ui-chrome6→dfb228799;job/ui-money4→4257fc76f;job/hscroll-1579→7a378b794.01fc5ab2d(parked retry probe),dfcd877e1(count overflow Events),b371b2b97(Money phone captures),8e8409629(bounded refresh retry).What changed
+Ncontrol so the scroll profile records compact Events without changing its presentation.Retry-Aftervalue, bounded to 1–120 seconds, and still require a final 204.--adopt-7c; the ratchet total decreased from 20,383 to 20,373. No ratchet increase.Gates and probes
The following are the final command outputs or verbatim final summary lines. Empty output means exit 0 with no stdout or stderr.
The first clippy attempt could not see the embedded web build. After the web build completed, the final clippy run above passed.
The requested release-server Money resize flow passed. It kept the same centered transaction visible after resizing, and wrote both 390×844 captures. The startup handover probe against production
c0f8da461passed: ready in 0.675 s; one writer at a time; 8/8 Files, Notes, and Money writes acknowledged; 16 Search reads succeeded; cross-User isolation 1/1; cleanup retry recovered; Notes migrations 0039/0040 and Calendar 0007 were present. Its 32 startup 503 responses all had the documentedRetry-After; there were no other 5xx responses.The parked-owner Notes probe passed:
Calendar performance profile
The local Chromium profile used macOS platform emulation, 7 samples, 84 days, 1,680 Events, 84 Tasks, and 84 Logs. It reported zero unpainted Event ranges at all three widths. Median sample values:
This was a local UI profile. The existing
calendar_scroll_1461baseline uses Chrome 153 with 4× CPU throttling on a different host, so its frame numbers are not a controlled comparison. This profile records browser JS heap, not process RSS, and did not collect process CPU. Treat the CPU/RSS comparison as a follow-up measurement gap; performance is not a merge gate.Screenshots
Captured clean, API-backed screens at the requested widths/themes. The eight full-size PNGs are attached to this issue and returned HTTP 200 as
image/png.The source captures remain under
artifacts/merge41b/. Screenshots used Linux Chromium with macOS platform emulation; the macOS VM was not used, per the owner instruction.UX gaps closed
Known gaps
apps/web/e2e/money-fidelity-1130.mjstimed out waiting for the old accessible nameAccount type for Card account; the live screen showed “Confirm account types” and the “Credit card” suggestion. I did not change its expected behavior. Keep it for triage.Decisions where DESIGN is silent
+Ncontrol via a data attribute, with no visual change.Retry-Afterresponse and require the final refresh to return 204.Files
Final cleanup
apps/web/buildandapps/web/.svelte-kitwere removed.git status --shortis clean at head8e840962979acd2bdf58141888618ab5560f728e.Supplemental focus-ring proof, captured from the real API-backed Money register at 390 px in dark Theme with macOS platform emulation. The first image follows a pointer click that opens the header More actions menu; the second follows Cmd+K opening Search. Neither shows a focus ring after the opening action.
I temporarily extended the existing screenshot runner for this capture, restored it from HEAD, and confirmed
git diff --checkwith no tracked changes. The branch head remains8e840962979acd2bdf58141888618ab5560f728e.Final cleanup after the supplemental visual proof:
apps/web/buildandapps/web/.svelte-kitare absent.git status --shortis clean; head remains8e840962979acd2bdf58141888618ab5560f728e.Deployed in merge round 41 (build
8e8409629, 09:44 IST 2026-10-12): state ok after 24 s, /readyz 200, served-page check passed. Restart 100 files 9.7->5.1 s, 10k files 21.5->13.8 s, no hangs; four review rounds' blockers fixed. Cold start remains about 121 s; continued in a follow-up issue.