Mail: selecting a message fires about 75 requests #1449

Closed
opened 2026-10-08 05:46:20 +00:00 by kayg · 31 comments
Owner

Problem

The interaction audit on the perf VM (2026-10-07, see comment on #1368) showed that selecting one Mail message fires about 75 network requests. Selecting a message must cost at most: the body read (or 304 from the cache, #1372) and one flag update when it marks read. Everything else (counts, lists, other mailboxes, prefetch) must not refetch per selection.

Work

Measure on the perf VM (shared release server from /mnt/hdd/targets/shared-server/CURRENT, flock -w 2700 /root/perf.lock, no builds there): requests, bytes, and input-to-body-visible p50/p95 for 20 selections with arrow keys and with clicks, 10k-message mailbox. List every request with its initiator. Remove the redundant ones (deduplicate, cache, coalesce, cancel superseded). Re-measure interleaved against origin/dev. Regression test: a browser test counting requests per selection (budget ≤ 2 after warmup). Gates: bun run check, bun run test, cargo gates for any crate touched; quote verbatim.

## Problem The interaction audit on the perf VM (2026-10-07, see comment on #1368) showed that selecting one Mail message fires about 75 network requests. Selecting a message must cost at most: the body read (or 304 from the cache, #1372) and one flag update when it marks read. Everything else (counts, lists, other mailboxes, prefetch) must not refetch per selection. ## Work Measure on the perf VM (shared release server from /mnt/hdd/targets/shared-server/CURRENT, `flock -w 2700 /root/perf.lock`, no builds there): requests, bytes, and input-to-body-visible p50/p95 for 20 selections with arrow keys and with clicks, 10k-message mailbox. List every request with its initiator. Remove the redundant ones (deduplicate, cache, coalesce, cancel superseded). Re-measure interleaved against origin/dev. Regression test: a browser test counting requests per selection (budget ≤ 2 after warmup). Gates: bun run check, bun run test, cargo gates for any crate touched; quote verbatim.
Author
Owner

Starting issue #1449 on branch job/mailsel-1449 at base/worktree SHA d395b6e25d (origin/dev currently af202e1514). I will trace and measure the Mail selection request path, then add the per-selection browser regression test.

Starting issue #1449 on branch job/mailsel-1449 at base/worktree SHA d395b6e25da20d8c67b8b9fc1a593b813939c8a7 (origin/dev currently af202e1514955ddf0f8b4b3d2503c780d807ed84). I will trace and measure the Mail selection request path, then add the per-selection browser regression test.
Author
Owner

Finding: MailView.svelte calls prefetchWindow() from both openRow and selectRowAt. That window spans 96 rows, and prefetchRows issues both a detail read and a thread-header read per row. The SSE refresh path also clears list, thread, and detail caches before rereading the list and selected items. I am measuring the baseline to separate the selection-triggered prefetch from change-stream refreshes before editing those paths.

Finding: `MailView.svelte` calls `prefetchWindow()` from both `openRow` and `selectRowAt`. That window spans 96 rows, and `prefetchRows` issues both a detail read and a thread-header read per row. The SSE refresh path also clears list, thread, and detail caches before rereading the list and selected items. I am measuring the baseline to separate the selection-triggered prefetch from change-stream refreshes before editing those paths.
Author
Owner

Finding while tracing #1449: MailProxyStore::queue_read_state can call mutate once per live membership with bounded concurrency 4. Each committed mutation publishes a mail-change event through changed(), and the SSE payload has only {\"scope\":\"folders\"} with no operation ID. One client write can therefore produce multiple unkeyed events; suppressing only one would leave a folder read on the same selection.

The web stream bridge now applies typed read changes through a User-scoped BroadcastChannel, suppresses the event burst while the write is pending plus a 250 ms drain after success, and rechecks server state after a failed write. A 10-second cap releases stalled expectations. Focused tests cover duplicate events, later external invalidation, and failed-write reconciliation.

Finding while tracing #1449: `MailProxyStore::queue_read_state` can call `mutate` once per live membership with bounded concurrency 4. Each committed mutation publishes a `mail-change` event through `changed()`, and the SSE payload has only `{\"scope\":\"folders\"}` with no operation ID. One client write can therefore produce multiple unkeyed events; suppressing only one would leave a folder read on the same selection. The web stream bridge now applies typed read changes through a User-scoped BroadcastChannel, suppresses the event burst while the write is pending plus a 250 ms drain after success, and rechecks server state after a failed write. A 10-second cap releases stalled expectations. Focused tests cover duplicate events, later external invalidation, and failed-write reconciliation.
Author
Owner

Finding: selecting a Message changes the URL to its thread, which reruns MailView.load(). If the active list snapshot was older than 15 seconds, that route change could start a list-page request and repeat the warm-window prefetch as part of selection. I now keep the current in-memory list and its prefetch generation for stable Message route changes; list-scope changes and explicit retry still reload and warm once. The browser regression ages browser cache timestamps past 15 seconds before measuring the 20 arrow and 20 click selections, so the request budget covers this path.

Finding: selecting a Message changes the URL to its thread, which reruns `MailView.load()`. If the active list snapshot was older than 15 seconds, that route change could start a list-page request and repeat the warm-window prefetch as part of selection. I now keep the current in-memory list and its prefetch generation for stable Message route changes; list-scope changes and explicit retry still reload and warm once. The browser regression ages browser cache timestamps past 15 seconds before measuring the 20 arrow and 20 click selections, so the request budget covers this path.
Author
Owner

Finding: the focused 10k-message browser probe still observed 8 requests during the first measured arrow selection: one thread read, one body read, one read-state write, an Inbox list read, two account-folder reads, then a duplicate body and thread read. The live stream had no event during that measured selection. I am isolating a delayed startup invalidation that overlaps the selection and will rerun the probe after it settles.

Finding: the focused 10k-message browser probe still observed 8 requests during the first measured arrow selection: one thread read, one body read, one read-state write, an Inbox list read, two account-folder reads, then a duplicate body and thread read. The live stream had no event during that measured selection. I am isolating a delayed startup invalidation that overlaps the selection and will rerun the probe after it settles.
Author
Owner

Finding: the browser request trace exposed a fixture mismatch. The 10k screenshot fixture inserts credential_ciphertext = 'screenshot-fixture', but the real read-state route decrypts the account credential before it queues a flag update. A failed write rolls back the optimistic edit and correctly asks both Mail views to reconcile, which explains the extra list/folder/body/thread reads in this probe. The request-budget browser test now accepts only that synthetic account's read-state POST as 204 at the browser boundary; its GETs still use the real Mail API and the server route keeps its own validation tests.

Finding: the browser request trace exposed a fixture mismatch. The 10k screenshot fixture inserts `credential_ciphertext = 'screenshot-fixture'`, but the real read-state route decrypts the account credential before it queues a flag update. A failed write rolls back the optimistic edit and correctly asks both Mail views to reconcile, which explains the extra list/folder/body/thread reads in this probe. The request-budget browser test now accepts only that synthetic account's read-state POST as 204 at the browser boundary; its GETs still use the real Mail API and the server route keeps its own validation tests.
Author
Owner

Finding: selecting a retained Message called GET /api/v1/mail/preferences every time. That made a warm selection exceed the #1449 request budget.

Decision: use the existing 15-second remote-content recheck window for retained bodies. A recent server check is shared across selections; stale checks and in-flight checks are awaited. Revision and access invalidation still reject a body after a saved setting or account-access change. This keeps the documented current-preference check while bounding external changes by the existing focus and periodic rechecks.

Regression coverage now checks both fresh reuse and stale refresh. The browser probe also uses a successful read-state response because the shared disposable fixture intentionally has undecryptable provider ciphertext; it still counts that POST and exercises real list/body GETs.

Finding: selecting a retained Message called GET /api/v1/mail/preferences every time. That made a warm selection exceed the #1449 request budget. Decision: use the existing 15-second remote-content recheck window for retained bodies. A recent server check is shared across selections; stale checks and in-flight checks are awaited. Revision and access invalidation still reject a body after a saved setting or account-access change. This keeps the documented current-preference check while bounding external changes by the existing focus and periodic rechecks. Regression coverage now checks both fresh reuse and stale refresh. The browser probe also uses a successful read-state response because the shared disposable fixture intentionally has undecryptable provider ciphertext; it still counts that POST and exercises real list/body GETs.
Author
Owner

Finding: the browser probe warmed 240 unique thread-header requests (all HTTP 200), but an arrow selection later repeated one of those thread URLs. No SSE event or session/access cache invalidation occurred during that selection. The cause was first-page reconciliation: it correctly discarded an unproven cached tail and cleared the thread cache, but left the 240-ready marker set and did not restart the warm window.

Fix in progress: reset the readiness marker and restart the bounded 240-row body/thread warm after a successful first-page reconciliation. The browser test now waits for the post-reconciliation warm to finish before sampling.

Finding: the browser probe warmed 240 unique thread-header requests (all HTTP 200), but an arrow selection later repeated one of those thread URLs. No SSE event or session/access cache invalidation occurred during that selection. The cause was first-page reconciliation: it correctly discarded an unproven cached tail and cleared the thread cache, but left the 240-ready marker set and did not restart the warm window. Fix in progress: reset the readiness marker and restart the bounded 240-row body/thread warm after a successful first-page reconciliation. The browser test now waits for the post-reconciliation warm to finish before sampling.
Author
Owner

Finding: the warmed Inbox had 240 distinct thread GETs (all 200), but the thread cache reached its 256-entry limit before selection. A later selection repeated a previously warmed thread route without an intervening live event or invalidation. I increased the cache entry headroom to 512 while retaining the existing 4 MiB byte cap. The focused browser test is rerunning against this change.

Finding: the warmed Inbox had 240 distinct thread GETs (all 200), but the thread cache reached its 256-entry limit before selection. A later selection repeated a previously warmed thread route without an intervening live event or invalidation. I increased the cache entry headroom to 512 while retaining the existing 4 MiB byte cap. The focused browser test is rerunning against this change.
Author
Owner

The focused browser regression now passes with a real 10,000-message Inbox: 20 arrow selections and 20 click selections each used at most one Mail API request (arrow counts: 20×1; click counts: 19×1 and 1×0). Raising the bounded thread cache eliminated warmed-thread misses. The probe also observed an independently scheduled /_app/version.json shell check during one click, so the regression budget now counts Mail API requests attributable to selection and leaves the shell's unrelated version heartbeat out of that API budget.

The focused browser regression now passes with a real 10,000-message Inbox: 20 arrow selections and 20 click selections each used at most one Mail API request (arrow counts: 20×1; click counts: 19×1 and 1×0). Raising the bounded thread cache eliminated warmed-thread misses. The probe also observed an independently scheduled `/_app/version.json` shell check during one click, so the regression budget now counts Mail API requests attributable to selection and leaves the shell's unrelated version heartbeat out of that API budget.
Author
Owner

The first origin-A perf attempt reached the shared lock but failed before measurement because the SSH output delivered the lock marker before the next chunk with the load average. The wrapper now waits for both the marker and a complete newline-terminated load line. The failed attempt released the lock; perf-run status then showed another job as holder. The helper fix is committed as 2ae4d9a3b7a1313fde82d0c6d18e883ce8477231.

The first origin-A perf attempt reached the shared lock but failed before measurement because the SSH output delivered the lock marker before the next chunk with the load average. The wrapper now waits for both the marker and a complete newline-terminated load line. The failed attempt released the lock; `perf-run status` then showed another job as holder. The helper fix is committed as `2ae4d9a3b7a1313fde82d0c6d18e883ce8477231`.
Author
Owner

The extended visual-evidence pass overlapped the existing 30-second new-message poll (GET /api/v1/mail/inbox/messages?limit=1&category=all). I now record that timer-driven request and its initiator separately, assert it remains periodic, and apply the ≤2 budget to selection-attributed Mail API requests. The 10k-Inbox test still passes: 20 arrow selections each used 1 request; click selections used 0 once and 1 nineteen times. Mac-platform screenshots are saved in artifacts/mail-selection-1449/ at 390, 820 and 1440 px in light and dark themes.

The extended visual-evidence pass overlapped the existing 30-second new-message poll (`GET /api/v1/mail/inbox/messages?limit=1&category=all`). I now record that timer-driven request and its initiator separately, assert it remains periodic, and apply the ≤2 budget to selection-attributed Mail API requests. The 10k-Inbox test still passes: 20 arrow selections each used 1 request; click selections used 0 once and 1 nineteen times. Mac-platform screenshots are saved in `artifacts/mail-selection-1449/` at 390, 820 and 1440 px in light and dark themes.
Author
Owner

The second origin-A attempt acquired the perf lock, then failed before selection measurement because the probe's window function was installed on about:blank and lost on navigation. I moved probe installation to the loaded Mail page before warmup. The failed run released the lock; the current holder belongs to another job. Fix committed as 8acee7b515f90983aef62798a1055d6f66f64f3d.

The second origin-A attempt acquired the perf lock, then failed before selection measurement because the probe's window function was installed on `about:blank` and lost on navigation. I moved probe installation to the loaded Mail page before warmup. The failed run released the lock; the current holder belongs to another job. Fix committed as `8acee7b515f90983aef62798a1055d6f66f64f3d`.
Author
Owner

The first locked origin-A profile exposed a benchmark harness artifact: route changes caused requests for /favicon-16.png, /favicon-32.png, and /favicon.svg; routeCurrentBuild treated those root assets as SPA routes and returned the 614,540-byte app shell for each. I am adding those real build assets to the harness's static path allowlist and will discard this contaminated profile before repeating origin-A.

The first locked origin-A profile exposed a benchmark harness artifact: route changes caused requests for `/favicon-16.png`, `/favicon-32.png`, and `/favicon.svg`; `routeCurrentBuild` treated those root assets as SPA routes and returned the 614,540-byte app shell for each. I am adding those real build assets to the harness's static path allowlist and will discard this contaminated profile before repeating origin-A.
Author
Owner

Resolved the profile artifact: routeCurrentBuild now serves /favicon.ico, /favicon-16.png, /favicon-32.png, and /favicon.svg from the production build, not the SPA document. The focused 10k-Inbox browser test passes after this harness change. Commit: 430af084f9b002ebe394a89fa7c82d1c5629ff9f. I am discarding the contaminated origin-A file and will remeasure after the current perf lock releases.

Resolved the profile artifact: `routeCurrentBuild` now serves `/favicon.ico`, `/favicon-16.png`, `/favicon-32.png`, and `/favicon.svg` from the production build, not the SPA document. The focused 10k-Inbox browser test passes after this harness change. Commit: `430af084f9b002ebe394a89fa7c82d1c5629ff9f`. I am discarding the contaminated origin-A file and will remeasure after the current perf lock releases.
Author
Owner

Clean origin-A profile on the perf VM (server and web source af202e1514, Browser 153.0.8010.12, real 10,000-message Inbox; lock load average 2.29 3.74 3.74).

Arrow selections (20): input-to-body-visible p50/p95 260.5/383.6 ms, 89 requests, 219,224 observed bytes. Inventory (method, route, status, count, initiator): GET /_app/version.json 200 ×1 (script /_app/immutable/chunks/BVYX0Xuk.js:1); GET /api/v1/mail/messages/{id} 200 ×4 (same script); POST /api/v1/mail/messages/{id}/read-state 204 ×20 (same script); GET /api/v1/mail/preferences 200 ×20 (same script); GET /api/v1/mail/threads/{id}/messages?limit=50 200 ×24 (same script); GET /favicon.svg 200 ×20 (other).

Click selections (20): input-to-body-visible p50/p95 244.1/306.5 ms, 100 requests, 234,040 observed bytes. Inventory: GET /api/v1/mail/inbox/messages?limit=1&category=all 200 ×20 (script /_app/immutable/chunks/BVYX0Xuk.js:1); POST /api/v1/mail/messages/{id}/read-state 204 ×20 (same script); GET /api/v1/mail/preferences 200 ×20 (same script); GET /api/v1/mail/threads/{id}/messages?limit=50 200 ×20 (same script); GET /favicon.svg 200 ×20 (other).

The candidate profile will determine which requests remain after the fix. Full per-selection records, request body/response byte totals, CPU/RSS samples, and the load after each run are retained in the benchmark JSON.

Clean origin-A profile on the perf VM (server and web source af202e1514955ddf0f8b4b3d2503c780d807ed84, Browser 153.0.8010.12, real 10,000-message Inbox; lock load average 2.29 3.74 3.74). Arrow selections (20): input-to-body-visible p50/p95 260.5/383.6 ms, 89 requests, 219,224 observed bytes. Inventory (method, route, status, count, initiator): GET /_app/version.json 200 ×1 (script /_app/immutable/chunks/BVYX0Xuk.js:1); GET /api/v1/mail/messages/{id} 200 ×4 (same script); POST /api/v1/mail/messages/{id}/read-state 204 ×20 (same script); GET /api/v1/mail/preferences 200 ×20 (same script); GET /api/v1/mail/threads/{id}/messages?limit=50 200 ×24 (same script); GET /favicon.svg 200 ×20 (other). Click selections (20): input-to-body-visible p50/p95 244.1/306.5 ms, 100 requests, 234,040 observed bytes. Inventory: GET /api/v1/mail/inbox/messages?limit=1&category=all 200 ×20 (script /_app/immutable/chunks/BVYX0Xuk.js:1); POST /api/v1/mail/messages/{id}/read-state 204 ×20 (same script); GET /api/v1/mail/preferences 200 ×20 (same script); GET /api/v1/mail/threads/{id}/messages?limit=50 200 ×20 (same script); GET /favicon.svg 200 ×20 (other). The candidate profile will determine which requests remain after the fix. Full per-selection records, request body/response byte totals, CPU/RSS samples, and the load after each run are retained in the benchmark JSON.
Author
Owner

Finding #1449 closed in commit 97b505de2e9e51d7c38cf353dc07d5ca480a3187. The origin profile showed one /favicon.svg request per selection (20 arrow, 20 click), initiated as “other”; the browser issued it when the sanitized message iframe replaced its srcdoc. Each body frame now has a data-only inline icon. The browser regression now counts every app request, excluding the EventSource stream, the timed build-version check, and the 30s Inbox poll. The real-server E2E passed on a 10,000-message Inbox: 20 arrows used 1 request each; clicks used 0 for the first warm body and 1 each for the next 19. Ambient Inbox polls were 30.07s apart.

Fresh Mac-platform screenshots (390/820/1440, paper and tokyo-night):

Finding #1449 closed in commit 97b505de2e9e51d7c38cf353dc07d5ca480a3187. The origin profile showed one /favicon.svg request per selection (20 arrow, 20 click), initiated as “other”; the browser issued it when the sanitized message iframe replaced its srcdoc. Each body frame now has a data-only inline icon. The browser regression now counts every app request, excluding the EventSource stream, the timed build-version check, and the 30s Inbox poll. The real-server E2E passed on a 10,000-message Inbox: 20 arrows used 1 request each; clicks used 0 for the first warm body and 1 each for the next 19. Ambient Inbox polls were 30.07s apart. Fresh Mac-platform screenshots (390/820/1440, paper and tokyo-night): - [390 paper](https://git.kayg.org/attachments/ba0acc0a-af33-4545-9a34-18f730277ad7), [390 tokyo-night](https://git.kayg.org/attachments/a9fdfe5d-29c9-463c-8804-b56280db82d4) - [820 paper](https://git.kayg.org/attachments/bae22a18-c361-4774-ba31-ef36f0defeca), [820 tokyo-night](https://git.kayg.org/attachments/97282de1-ae83-4e72-b3b5-70bf07443d0f) - [1440 paper](https://git.kayg.org/attachments/d37150c1-f942-4a01-b0a5-a8c7cbecb69d), [1440 tokyo-night](https://git.kayg.org/attachments/267963c6-03e6-4b27-a8a4-7648aa6e5405)
Author
Owner

Correction: the favicon fix commit hash is 97b505de2437bf7a3e3c4e5f9fb261ad73743c79 (the earlier comment transposed characters).

Correction: the favicon fix commit hash is `97b505de2437bf7a3e3c4e5f9fb261ad73743c79` (the earlier comment transposed characters).
Author
Owner

Candidate-A profile on Chromium 153 (source fc1692a2b2, build SHA-256 55fac1fb521ed8800a600b390f3b169cf181d81d48f195cf3cf89f55ca14b8e0) found that the data-GIF icon did not stop the VM browser from requesting /favicon.svg for each changed message srcdoc. For 20 arrow selections, the profile recorded 40 requests (20 read-state POSTs and 20 favicon GETs) and 167,840 observed bytes; click selections recorded 42 requests (20 read-state POSTs, 20 favicon GETs, plus one periodic Inbox poll and one version check) and 169,260 bytes. I am discarding this as a passing candidate result and changing the icon to a Chromium-supported PNG before re-measuring. The lock load average was 2.67 4.39 4.76.

Candidate-A profile on Chromium 153 (source fc1692a2b2b0587c4431bbfab405974936c6730c, build SHA-256 55fac1fb521ed8800a600b390f3b169cf181d81d48f195cf3cf89f55ca14b8e0) found that the data-GIF icon did not stop the VM browser from requesting /favicon.svg for each changed message srcdoc. For 20 arrow selections, the profile recorded 40 requests (20 read-state POSTs and 20 favicon GETs) and 167,840 observed bytes; click selections recorded 42 requests (20 read-state POSTs, 20 favicon GETs, plus one periodic Inbox poll and one version check) and 169,260 bytes. I am discarding this as a passing candidate result and changing the icon to a Chromium-supported PNG before re-measuring. The lock load average was 2.67 4.39 4.76.
Author
Owner

Follow-up from Chromium 153 candidate-A-SVG (source 114679159a, build SHA-256 b23e3d791835eb434090fe6607bef7f5bc235400b0e7f413adf15c4d175d9005): the inline SVG icon also did not prevent the browser from fetching /favicon.svg ×20 per input mode. Arrow had 41 requests / 167,932 observed bytes; click had 41 / 168,558 bytes. Both include 20 read-state writes and 20 favicon fetches; a timed build-version check appeared once in arrows, and one notifications poll appeared in clicks. The favicon initiator is reported by Chromium as “other”. I am adding frame ID to the trace to distinguish the top page from the two message frames before selecting the next fix. The lock load average was 0.67 0.59 2.29.

Follow-up from Chromium 153 candidate-A-SVG (source 114679159ab3bea51929ce4e979ee67cdb730338, build SHA-256 b23e3d791835eb434090fe6607bef7f5bc235400b0e7f413adf15c4d175d9005): the inline SVG icon also did not prevent the browser from fetching /favicon.svg ×20 per input mode. Arrow had 41 requests / 167,932 observed bytes; click had 41 / 168,558 bytes. Both include 20 read-state writes and 20 favicon fetches; a timed build-version check appeared once in arrows, and one notifications poll appeared in clicks. The favicon initiator is reported by Chromium as “other”. I am adding frame ID to the trace to distinguish the top page from the two message frames before selecting the next fix. The lock load average was 0.67 0.59 2.29.
Author
Owner

The frame-ID trace resolves the source: every /favicon.svg request belongs to the main frame, with document_url /mail/t/{id}?layout=columns&format=plain and initiator type “other”. The Mail route changes to the selected Message ID; the body iframe is not the source, so the inline frame icons did not help. The server currently returns Cache-Control: no-cache for unversioned app icons, and the route-based perf asset seam bypasses browser caching. I am reverting the ineffective iframe icon and changing the root app-icon cache policy to one hour, then I will profile using release binaries that embed their own production frontend so the browser uses the real server cache headers.

The frame-ID trace resolves the source: every /favicon.svg request belongs to the main frame, with document_url /mail/t/{id}?layout=columns&format=plain and initiator type “other”. The Mail route changes to the selected Message ID; the body iframe is not the source, so the inline frame icons did not help. The server currently returns Cache-Control: no-cache for unversioned app icons, and the route-based perf asset seam bypasses browser caching. I am reverting the ineffective iframe icon and changing the root app-icon cache policy to one hour, then I will profile using release binaries that embed their own production frontend so the browser uses the real server cache headers.
Author
Owner

Correction to my earlier comment: the inline frame icon was not the source of the favicon requests, and it did not fix them. The later frame-ID trace shows /favicon.svg belongs to the main Mail route after its URL changes, not either message iframe. I am removing that ineffective frame change. The remaining fix is the one-hour cache policy for root app icons, verified with actual embedded server builds.

Correction to my earlier comment: the inline frame icon was not the source of the favicon requests, and it did not fix them. The later frame-ID trace shows `/favicon.svg` belongs to the main Mail route after its URL changes, not either message iframe. I am removing that ineffective frame change. The remaining fix is the one-hour cache policy for root app icons, verified with actual embedded server builds.
Author
Owner

Server test gate finding for #1449: cargo test -p calternal-server completed with 271 passed, 1 failed, and 12 ignored. The failure was the nested startup_search_reconciliation_keeps_calendar_writes_responsive_on_large_home performance assertion: search_reconcile_calendar_write_profile files=20000 writes=32 p50_ms=13563 p95_ms=13935 p95_budget_ms=10000; it failed because calendar-write p95 was 13.93551839s. During the run, the shared build host also showed multiple concurrent Node and rustc processes consuming CPU. The changed root-icon cache assertion passed in the suite; this slow profile does not exercise the changed cache policy. I am treating this as a SLOW host-load finding under the job contract.

Server test gate finding for #1449: `cargo test -p calternal-server` completed with 271 passed, 1 failed, and 12 ignored. The failure was the nested `startup_search_reconciliation_keeps_calendar_writes_responsive_on_large_home` performance assertion: `search_reconcile_calendar_write_profile files=20000 writes=32 p50_ms=13563 p95_ms=13935 p95_budget_ms=10000`; it failed because calendar-write p95 was 13.93551839s. During the run, the shared build host also showed multiple concurrent Node and rustc processes consuming CPU. The changed root-icon cache assertion passed in the suite; this slow profile does not exercise the changed cache policy. I am treating this as a SLOW host-load finding under the job contract.
Author
Owner

First embedded origin-A profile on the perf VM used the origin/dev release server (server_commit and frontend build_id af202e151) under lock load 0.84 0.59 2.22. The first arrow selection recorded 4 network requests, above the two-request budget. The profile assertion stopped before it serialized the per-request initiators, and the lock released cleanly. I am updating the recorder to finish all 20 arrow and 20 click samples and retain over-budget request details, then I will rerun origin-A to capture the full trace.

First embedded origin-A profile on the perf VM used the origin/dev release server (`server_commit` and frontend `build_id` af202e151) under lock load `0.84 0.59 2.22`. The first arrow selection recorded 4 network requests, above the two-request budget. The profile assertion stopped before it serialized the per-request initiators, and the lock released cleanly. I am updating the recorder to finish all 20 arrow and 20 click samples and retain over-budget request details, then I will rerun origin-A to capture the full trace.
Author
Owner

The completed embedded origin/dev profile confirms the over-budget selection traffic. The 10k Inbox profile ran on af202e1514955ddf0f8b4b3d2503c780d807ed84 (Chrome 153.0.8010.12), with the perf lock load average 0.47 0.75 2.03 1/199 2138332 and post-run 1.29 0.97 1.99 2/443 2138767.

Arrow selection: 20 inputs, input-to-body-visible p50/p95 166.4/281.5 ms; 90 network requests total, per-selection p50/p95/max 4/6/6; request body 260 B, responses 201,465 B, observed transfer 201,725 B. Click selection: 20 inputs, p50/p95 188.3/231.7 ms; 80 network requests, per-selection p50/p95/max 4/4/4; request body 260 B, responses 186,900 B, observed transfer 187,160 B.

Every arrow sample initiated GET /api/v1/mail/preferences, GET /api/v1/mail/threads/{id}/messages?limit=50, GET /favicon.svg, and POST /api/v1/mail/messages/{id}/read-state; five arrows also had a body GET. Each click sample initiated preferences, thread messages, favicon, and read-state. The click phase also overlapped the ambient Inbox poll (20 requests); the one build-version request occurred outside the selection windows. API initiators were script /_app/immutable/chunks/BLVggWdf.js:1; favicon initiator was other in the main frame. The read-state POST is expected. Preferences, thread messages, and favicon repeat with each selection and violate the budget. I am fixing those paths and adding response cache-header capture to the profiler before the candidate comparison.

The completed embedded origin/dev profile confirms the over-budget selection traffic. The 10k Inbox profile ran on `af202e1514955ddf0f8b4b3d2503c780d807ed84` (Chrome 153.0.8010.12), with the perf lock load average `0.47 0.75 2.03 1/199 2138332` and post-run `1.29 0.97 1.99 2/443 2138767`. Arrow selection: 20 inputs, input-to-body-visible p50/p95 `166.4/281.5 ms`; 90 network requests total, per-selection p50/p95/max `4/6/6`; request body `260 B`, responses `201,465 B`, observed transfer `201,725 B`. Click selection: 20 inputs, p50/p95 `188.3/231.7 ms`; 80 network requests, per-selection p50/p95/max `4/4/4`; request body `260 B`, responses `186,900 B`, observed transfer `187,160 B`. Every arrow sample initiated GET `/api/v1/mail/preferences`, GET `/api/v1/mail/threads/{id}/messages?limit=50`, GET `/favicon.svg`, and POST `/api/v1/mail/messages/{id}/read-state`; five arrows also had a body GET. Each click sample initiated preferences, thread messages, favicon, and read-state. The click phase also overlapped the ambient Inbox poll (20 requests); the one build-version request occurred outside the selection windows. API initiators were `script /_app/immutable/chunks/BLVggWdf.js:1`; favicon initiator was `other` in the main frame. The read-state POST is expected. Preferences, thread messages, and favicon repeat with each selection and violate the budget. I am fixing those paths and adding response cache-header capture to the profiler before the candidate comparison.
Author
Owner

Merged-tree browser regression update for #1449 (candidate HEAD b6dffca38): on the real 10,000-message Inbox, 20 Arrow-key selections and 20 distinct clicks each stayed at one Mail API request in the warm-cache run. The body was already available from the bounded reader cache, so each sample sent only its read-state update. The warmup made zero thread-header requests. The separate Notifications unread-count request repeated on its own 60-second timer and did not restart per selection.

The explicit row action still opens the selected thread. The final E2E run used the merged production server and passed.

Fresh Mac-platform captures from that production build:

The interleaved perf VM run is queued behind its current single-tenant profile; I will add its request inventory, byte totals and p50/p95 results in the final report.

Merged-tree browser regression update for #1449 (candidate HEAD `b6dffca38`): on the real 10,000-message Inbox, 20 Arrow-key selections and 20 distinct clicks each stayed at one Mail API request in the warm-cache run. The body was already available from the bounded reader cache, so each sample sent only its read-state update. The warmup made zero thread-header requests. The separate Notifications unread-count request repeated on its own 60-second timer and did not restart per selection. The explicit row action still opens the selected thread. The final E2E run used the merged production server and passed. Fresh Mac-platform captures from that production build: - 390 px, Paper: https://git.kayg.org/attachments/03825d12-724f-4e12-9e31-d0941d191b44 - 390 px, Tokyo Night: https://git.kayg.org/attachments/7418a5ec-5f0c-46b2-8918-4d526ed9a1ff - 820 px, Paper: https://git.kayg.org/attachments/516a9ad4-25a8-447e-9af3-b253eddbd951 - 820 px, Tokyo Night: https://git.kayg.org/attachments/1e478661-8039-479b-9849-5faf335d60d6 - 1440 px, Paper: https://git.kayg.org/attachments/eefabacd-6832-45c2-8d9f-99675b73d8a8 - 1440 px, Tokyo Night: https://git.kayg.org/attachments/6d5ba6d8-a4bf-40b4-9502-879ebb7d8b7f - Arrow-key selection, 1440 px, Paper: https://git.kayg.org/attachments/338f2b3c-e974-4ae8-934b-6d4b0955c04c The interleaved perf VM run is queued behind its current single-tenant profile; I will add its request inventory, byte totals and p50/p95 results in the final report.
Author
Owner

Current-origin confirmation from the interleaved 10k-message profile (cc1af3c12, lock load 2.08 2.71 3.06): 20 arrow selections had p50/p95 input-to-body-visible of 164.3/246.1 ms and 4/6 requests (90 total, 201,725 observed bytes). Twenty clicks had 184.9/231.9 ms and 4/4 requests (80 total, 187,160 observed bytes). Both were measured after warming the real Inbox.

Per-selection request inventory and initiators:

  • POST /api/v1/mail/messages/{id}/read-state — 20 each, app script /_app/immutable/chunks/CBLlWaIN.js:1 (the expected read update).
  • GET /api/v1/mail/preferences — 20 each, same app script.
  • GET /api/v1/mail/threads/{id}/messages?limit=50 — 25 arrow / 20 click, same app script.
  • GET /favicon.svg — 20 each, browser initiator other.
  • Arrow also had 5 GET /api/v1/mail/messages/{id} body/detail reads, same app script.
  • Click-only ambient requests: 20 Inbox count polls and one Notifications count poll, plus one build-version check; all came from the same app script and were excluded from the selection budget.

The origin's repeated preference, thread and icon requests remain per-selection traffic. The interleaved candidate run is queued behind the current perf VM lock holder.

Current-origin confirmation from the interleaved 10k-message profile (`cc1af3c12`, lock load `2.08 2.71 3.06`): 20 arrow selections had p50/p95 input-to-body-visible of 164.3/246.1 ms and 4/6 requests (90 total, 201,725 observed bytes). Twenty clicks had 184.9/231.9 ms and 4/4 requests (80 total, 187,160 observed bytes). Both were measured after warming the real Inbox. Per-selection request inventory and initiators: - `POST /api/v1/mail/messages/{id}/read-state` — 20 each, app script `/_app/immutable/chunks/CBLlWaIN.js:1` (the expected read update). - `GET /api/v1/mail/preferences` — 20 each, same app script. - `GET /api/v1/mail/threads/{id}/messages?limit=50` — 25 arrow / 20 click, same app script. - `GET /favicon.svg` — 20 each, browser initiator `other`. - Arrow also had 5 `GET /api/v1/mail/messages/{id}` body/detail reads, same app script. - Click-only ambient requests: 20 Inbox count polls and one Notifications count poll, plus one build-version check; all came from the same app script and were excluded from the selection budget. The origin's repeated preference, thread and icon requests remain per-selection traffic. The interleaved candidate run is queued behind the current perf VM lock holder.
Author
Owner

The first merged-candidate profile reached the perf VM but its server exited before measurement: the local release binary links libsherpa-onnx-c-api.so from the worktree's build directory, and the benchmark harness only exposed runtime libraries already under /mnt/hdd/targets/shared-server. I am updating the harness to stage the candidate's non-system shared libraries into its disposable VM directory, then I will rerun this candidate phase under perf-run. No selection measurements were recorded for that failed attempt.

The first merged-candidate profile reached the perf VM but its server exited before measurement: the local release binary links `libsherpa-onnx-c-api.so` from the worktree's build directory, and the benchmark harness only exposed runtime libraries already under `/mnt/hdd/targets/shared-server`. I am updating the harness to stage the candidate's non-system shared libraries into its disposable VM directory, then I will rerun this candidate phase under `perf-run`. No selection measurements were recorded for that failed attempt.
Author
Owner

The candidate retry now completed on the perf VM (b6dffca38, lock load 0.28 1.49 2.35) against a 10k Inbox. After warmup, both 20-arrow and 20-click profiles recorded 2 requests per selection and no body GET: one POST /api/v1/mail/messages/{id}/read-state from the app script and one GET /favicon.svg from Chromium's other initiator. Each input totaled 147,760 observed bytes. Input-to-body-visible was arrow p50/p95 69.7/152.5 ms and click 88.8/117.1 ms. The favicon returned public, max-age=3600 but Chromium still fetched it on each selection. I will include it in the interleaved second candidate trace and report it separately from Mail API traffic.

The candidate retry now completed on the perf VM (`b6dffca38`, lock load `0.28 1.49 2.35`) against a 10k Inbox. After warmup, both 20-arrow and 20-click profiles recorded 2 requests per selection and no body GET: one `POST /api/v1/mail/messages/{id}/read-state` from the app script and one `GET /favicon.svg` from Chromium's `other` initiator. Each input totaled 147,760 observed bytes. Input-to-body-visible was arrow p50/p95 69.7/152.5 ms and click 88.8/117.1 ms. The favicon returned `public, max-age=3600` but Chromium still fetched it on each selection. I will include it in the interleaved second candidate trace and report it separately from Mail API traffic.
Author
Owner

Finished — Forgejo #1449

Head: 2310e190accae53785b5fdc98d1045eda027eed8 on job/mailsel-1449.

Built

Message-row selection now opens the latest Message by its stable ID. The reader reuses the warm body cache and no longer fetches thread headers, Mail preferences, or list/count data on selection. Prefetch warms Message bodies only. Read-state changes update the shared Mail cache and sidebar counts locally; the corresponding unkeyed SSE events are coalesced. “Open thread” remains an explicit row-menu action.

The perf profile stages candidate runtime libraries into its disposable VM directory. Its request allowlist now handles normalized Message IDs correctly. The baseline records the two interleaved runs, request initiators, bytes, latency, CPU and RSS.

Perf VM results

Two origin/candidate pairs ran on Chromium 153.0.8010.12, with a real 10,000-message Inbox and 20 sequential ArrowDown plus 20 click selections per profile. The comparison below averages the two runs’ p50/p95 values.

Input Origin p50/p95 body-visible Candidate p50/p95 Origin requests p50/p95 Candidate requests p50/p95 Bytes per 20 selections, origin → candidate
Arrow keys 181.1 / 253.4 ms 69.2 / 161.9 ms 4 / 6 2 / 2 201,725 → 147,760
Clicks 185.6 / 220.0 ms 81.3 / 143.3 ms 4 / 4 2 / 2 187,160 → 147,760

Candidate warm selections made one Mail API request: POST /api/v1/mail/messages/{id}/read-state (20 each, initiator script /_app/immutable/chunks/CZE8Mvsv.js:1). There were no body GETs. Chromium also fetched GET /favicon.svg once per selection (20 each, initiator other), despite the response’s Cache-Control: public, max-age=3600. This browser request is included in the 2-request cap and accounts for 145,280 response bytes per 20 selections. Mail API bytes were 2,480 per 20 selections, down from 56,685 Arrow / 42,120 click bytes on origin.

Origin request inventory during the selections:

  • GET /api/v1/mail/messages/{id} — 5 Arrow selections; initiator script /_app/immutable/chunks/CBLlWaIN.js:1.
  • POST /api/v1/mail/messages/{id}/read-state — 20 Arrow and 20 click selections; same app-script initiator.
  • GET /api/v1/mail/preferences — 20 each; same app-script initiator.
  • GET /api/v1/mail/threads/{id}/messages?limit=50 — 25 Arrow and 20 click selections; same app-script initiator.
  • GET /favicon.svg — 20 each; initiator other.

Ambient requests were separated from the selection budget: GET /_app/version.json (app script), periodic GET /api/v1/mail/inbox/messages?limit=1&category=all (app script), and one GET /api/v1/notifications/inbox?limit=1 in origin run A (app script). The long-lived GET /api/v1/mail/events stream was opened before timing. Candidate had no per-selection list, count, thread or preference requests.

Across paired runs, average CPU was 1.22% server / 119.41% browser on origin and 0.98% / 117.79% on candidate. Mean of before/after RSS samples was 265,212,928 B server / 2,015,253,504 B browser on origin and 256,026,624 B / 2,005,473,280 B on candidate. Candidate high-water RSS was 257,474,560 B server and 2,019,291,136 B browser. The largest profile used 10,000 messages and 40 sequential selections; its maximum candidate body-visible latency was 287 ms. The read-state POST is accepted as a 204 by the HTTPS measurement front because fixture provider ciphertext is synthetic; request bytes and client behavior are measured, but this mock excludes the Mail write handler from server CPU.

Gates

cargo fmt --check
(no output; exit 0)

cargo clippy -p calternal-server --all-targets -- -D warnings
Finished dev profile [unoptimized + debuginfo] target(s) in 1m 15s

cargo test -p calternal-server -- --test-threads=4
test result: ok. 273 passed; 0 failed; 12 ignored; 0 measured; 0 filtered out; finished in 218.79s
test deterministic_performance_guards ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 46.42s
test sqlite_index_sidecars_and_snapshots_use_mode_0600 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.24s

cd apps/web && bun run check
PASS; 0 violations; 22544 scoped exceptions
svelte-check found 0 errors and 4 warnings in 2 files

Focused Vitest
Test Files  5 passed (5)
Tests  34 passed (34)

Production browser regression
MAIL_SELECTION_REQUESTS {"inbox_messages":10000,"selections":40,"request_counts":{"arrow":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"click":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]}}

Production web build
Compressed 912 static variants; saved 21556993 bytes.

The full bun run test, full e2e sweep, and adversarial matrix were deferred to the merge round under the verification policy.

UX gaps closed / left

Closed: selection works by click and Arrow keys; the selected Message deep-links by stable ID; explicit Open thread still works; warmed selection does not reload threads or counts; read badges update locally. Seven clean production-build screenshots cover macOS emulation, Paper and Tokyo Night, widths 390/820/1440, plus keyboard selection.

Left: touch tap was not separately profiled. It uses the same row activation path. Chromium’s browser-owned favicon GET remains in the total two-request budget as described above.

Decisions

  • A row now opens its latest Message directly; the design doc did not choose between a Message and a full thread as the default. Full thread remains available as “Open thread.”
  • App icons use a one-hour cache lifetime. Chromium still re-fetches the favicon on changed Message URLs; the profile keeps and reports those requests rather than filtering them out.
  • The perf fixture mocks only the successful read-state response at the HTTPS front because its provider ciphertext is synthetic.

Screenshots

For the merge round

  • cd apps/web && bun run test --maxWorkers=2 — full web tests.
  • cd apps/web && bun run test:e2e — package e2e entry; run the merge-round full e2e sweep as configured.
  • tests/adversarial/run.sh — XUser/authz and robustness probes against a real local server.

The web build output was deleted and cargo clean removed 14.4 GiB. No push, deploy or merge was performed.

# Finished — Forgejo #1449 Head: `2310e190accae53785b5fdc98d1045eda027eed8` on `job/mailsel-1449`. ## Built Message-row selection now opens the latest Message by its stable ID. The reader reuses the warm body cache and no longer fetches thread headers, Mail preferences, or list/count data on selection. Prefetch warms Message bodies only. Read-state changes update the shared Mail cache and sidebar counts locally; the corresponding unkeyed SSE events are coalesced. “Open thread” remains an explicit row-menu action. The perf profile stages candidate runtime libraries into its disposable VM directory. Its request allowlist now handles normalized Message IDs correctly. The baseline records the two interleaved runs, request initiators, bytes, latency, CPU and RSS. ## Perf VM results Two origin/candidate pairs ran on Chromium 153.0.8010.12, with a real 10,000-message Inbox and 20 sequential ArrowDown plus 20 click selections per profile. The comparison below averages the two runs’ p50/p95 values. | Input | Origin p50/p95 body-visible | Candidate p50/p95 | Origin requests p50/p95 | Candidate requests p50/p95 | Bytes per 20 selections, origin → candidate | |---|---:|---:|---:|---:|---:| | Arrow keys | 181.1 / 253.4 ms | 69.2 / 161.9 ms | 4 / 6 | 2 / 2 | 201,725 → 147,760 | | Clicks | 185.6 / 220.0 ms | 81.3 / 143.3 ms | 4 / 4 | 2 / 2 | 187,160 → 147,760 | Candidate warm selections made one Mail API request: `POST /api/v1/mail/messages/{id}/read-state` (20 each, initiator `script /_app/immutable/chunks/CZE8Mvsv.js:1`). There were no body GETs. Chromium also fetched `GET /favicon.svg` once per selection (20 each, initiator `other`), despite the response’s `Cache-Control: public, max-age=3600`. This browser request is included in the 2-request cap and accounts for 145,280 response bytes per 20 selections. Mail API bytes were 2,480 per 20 selections, down from 56,685 Arrow / 42,120 click bytes on origin. Origin request inventory during the selections: - `GET /api/v1/mail/messages/{id}` — 5 Arrow selections; initiator `script /_app/immutable/chunks/CBLlWaIN.js:1`. - `POST /api/v1/mail/messages/{id}/read-state` — 20 Arrow and 20 click selections; same app-script initiator. - `GET /api/v1/mail/preferences` — 20 each; same app-script initiator. - `GET /api/v1/mail/threads/{id}/messages?limit=50` — 25 Arrow and 20 click selections; same app-script initiator. - `GET /favicon.svg` — 20 each; initiator `other`. Ambient requests were separated from the selection budget: `GET /_app/version.json` (app script), periodic `GET /api/v1/mail/inbox/messages?limit=1&category=all` (app script), and one `GET /api/v1/notifications/inbox?limit=1` in origin run A (app script). The long-lived `GET /api/v1/mail/events` stream was opened before timing. Candidate had no per-selection list, count, thread or preference requests. Across paired runs, average CPU was 1.22% server / 119.41% browser on origin and 0.98% / 117.79% on candidate. Mean of before/after RSS samples was 265,212,928 B server / 2,015,253,504 B browser on origin and 256,026,624 B / 2,005,473,280 B on candidate. Candidate high-water RSS was 257,474,560 B server and 2,019,291,136 B browser. The largest profile used 10,000 messages and 40 sequential selections; its maximum candidate body-visible latency was 287 ms. The read-state POST is accepted as a 204 by the HTTPS measurement front because fixture provider ciphertext is synthetic; request bytes and client behavior are measured, but this mock excludes the Mail write handler from server CPU. ## Gates ```text cargo fmt --check (no output; exit 0) cargo clippy -p calternal-server --all-targets -- -D warnings Finished dev profile [unoptimized + debuginfo] target(s) in 1m 15s cargo test -p calternal-server -- --test-threads=4 test result: ok. 273 passed; 0 failed; 12 ignored; 0 measured; 0 filtered out; finished in 218.79s test deterministic_performance_guards ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 46.42s test sqlite_index_sidecars_and_snapshots_use_mode_0600 ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.24s cd apps/web && bun run check PASS; 0 violations; 22544 scoped exceptions svelte-check found 0 errors and 4 warnings in 2 files Focused Vitest Test Files 5 passed (5) Tests 34 passed (34) Production browser regression MAIL_SELECTION_REQUESTS {"inbox_messages":10000,"selections":40,"request_counts":{"arrow":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],"click":[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]}} Production web build Compressed 912 static variants; saved 21556993 bytes. ``` The full `bun run test`, full e2e sweep, and adversarial matrix were deferred to the merge round under the verification policy. ## UX gaps closed / left Closed: selection works by click and Arrow keys; the selected Message deep-links by stable ID; explicit Open thread still works; warmed selection does not reload threads or counts; read badges update locally. Seven clean production-build screenshots cover macOS emulation, Paper and Tokyo Night, widths 390/820/1440, plus keyboard selection. Left: touch tap was not separately profiled. It uses the same row activation path. Chromium’s browser-owned favicon GET remains in the total two-request budget as described above. ## Decisions - A row now opens its latest Message directly; the design doc did not choose between a Message and a full thread as the default. Full thread remains available as “Open thread.” - App icons use a one-hour cache lifetime. Chromium still re-fetches the favicon on changed Message URLs; the profile keeps and reports those requests rather than filtering them out. - The perf fixture mocks only the successful read-state response at the HTTPS front because its provider ciphertext is synthetic. ## Screenshots - [390 Paper](https://git.kayg.org/attachments/03825d12-724f-4e12-9e31-d0941d191b44) · [390 Tokyo Night](https://git.kayg.org/attachments/7418a5ec-5f0c-46b2-8918-4d526ed9a1ff) - [820 Paper](https://git.kayg.org/attachments/516a9ad4-25a8-447e-9af3-b253eddbd951) · [820 Tokyo Night](https://git.kayg.org/attachments/1e478661-8039-479b-9849-5faf335d60d6) - [1440 Paper](https://git.kayg.org/attachments/eefabacd-6832-45c2-8d9f-99675b73d8a8) · [1440 Tokyo Night](https://git.kayg.org/attachments/6d5ba6d8-a4bf-40b4-9502-879ebb7d8b7f) - [1440 keyboard selection](https://git.kayg.org/attachments/338f2b3c-e974-4ae8-934b-6d4b0955c04c) ## For the merge round - `cd apps/web && bun run test --maxWorkers=2` — full web tests. - `cd apps/web && bun run test:e2e` — package e2e entry; run the merge-round full e2e sweep as configured. - `tests/adversarial/run.sh` — XUser/authz and robustness probes against a real local server. The web build output was deleted and `cargo clean` removed 14.4 GiB. No push, deploy or merge was performed.
kayg referenced this issue from a commit 2026-10-09 06:04:53 +00:00
Author
Owner

Deployed to calternal.cloud in merge round 35c (build 5a2c02c13, live 2026-10-09 ~16:35 IST). Closing.

Deployed to calternal.cloud in merge round 35c (build 5a2c02c13, live 2026-10-09 ~16:35 IST). Closing.
kayg closed this issue 2026-10-09 11:11:40 +00:00
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
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#1449
No description provided.