Slow selection and open paths: Notes, Settings, Composer, Notifications, Tasks #1392

Open
opened 2026-10-07 18:18:34 +00:00 by kayg · 61 comments
Owner

Finding (interaction audit #1369, perf VM)

Selection and open actions over the 150 ms accepted-action budget (DESIGN §59), p50 / p95:

  • Notes / move selection next: 64 / 443 ms (NotesExplorer onKeydown; issues GET /api/v1/notes ×4 during the move)
  • Settings / move selection next: 264 / 406 ms; previous: 242 / 265 ms (settings/[...path]/+page.svelte select; re-fetches /auth/me and /plugins on each move)
  • Calendar / open Composer: 283 / 346 ms; Tasks / open Composer: 166 / 217 ms (composer chunk + CSS fetched each open)
  • Notifications / open inbox: 274 / 340 ms; reopen: 200 / 252 ms (refetches the inbox each open)
  • Tasks / close item: 229 / 442 ms (refetches /auth/me, calendar preferences, calendar year)
  • Calendar / move selection previous: 94 / 210 ms
  • Notifications / move focus next/previous: 177–196 / 231–237 ms (no requests; render cost)
    Full audit: https://git.kayg.org/kayg/calternal/src/branch/job/instaudit-1369/docs/perf/2026-10-07-1369-interaction-audit.md

Required

Fix each row at its cause: no network on a selection move (use the loaded data; refresh in the background), cache/preload the Composer chunk on idle, render the inbox from its cached list then refresh, no /auth/me or preferences refetch on close. Each fix gets a test that would fail if the request or the work came back. Re-measure every row with the audit harness on the perf VM, before vs after. Notifications mark read/unread is #1387; Mail selection request fan-out is #1368; warm Tab switches are #1233: do not duplicate them.

## Finding (interaction audit #1369, perf VM) Selection and open actions over the 150 ms accepted-action budget (DESIGN §59), p50 / p95: - Notes / move selection next: 64 / 443 ms (NotesExplorer onKeydown; issues GET /api/v1/notes ×4 during the move) - Settings / move selection next: 264 / 406 ms; previous: 242 / 265 ms (settings/[...path]/+page.svelte select; re-fetches /auth/me and /plugins on each move) - Calendar / open Composer: 283 / 346 ms; Tasks / open Composer: 166 / 217 ms (composer chunk + CSS fetched each open) - Notifications / open inbox: 274 / 340 ms; reopen: 200 / 252 ms (refetches the inbox each open) - Tasks / close item: 229 / 442 ms (refetches /auth/me, calendar preferences, calendar year) - Calendar / move selection previous: 94 / 210 ms - Notifications / move focus next/previous: 177–196 / 231–237 ms (no requests; render cost) Full audit: https://git.kayg.org/kayg/calternal/src/branch/job/instaudit-1369/docs/perf/2026-10-07-1369-interaction-audit.md ## Required Fix each row at its cause: no network on a selection move (use the loaded data; refresh in the background), cache/preload the Composer chunk on idle, render the inbox from its cached list then refresh, no /auth/me or preferences refetch on close. Each fix gets a test that would fail if the request or the work came back. Re-measure every row with the audit harness on the perf VM, before vs after. Notifications mark read/unread is #1387; Mail selection request fan-out is #1368; warm Tab switches are #1233: do not duplicate them.
Author
Owner

Starting work on branch job/slowsel-1392 from base SHA 0419170fe4. I will align with origin/dev once before implementation, then trace the audit rows, add focused regression tests, and measure before/after on the perf VM.

Starting work on branch job/slowsel-1392 from base SHA 0419170fe456b58614befcb12d9c9d255118f1c0. I will align with origin/dev once before implementation, then trace the audit rows, add focused regression tests, and measure before/after on the perf VM.
Author
Owner

Finding from source trace and audit #1369: the root +layout.svelte URL effect calls guardRoute on each route change, and guardRoute performs GET /api/v1/auth/me. Settings selection calls goto for each key move, so the route guard can add that request to the accepted-action interval. Settings itself starts one User and Plugin refresh on mount; the prior audit saw one /api/v1/plugins request across five moves. I will cache the signed-in route-check result and coalesce its initial read; the explicit auth-change path will still refresh it.

Finding from source trace and audit #1369: the root +layout.svelte URL effect calls guardRoute on each route change, and guardRoute performs GET /api/v1/auth/me. Settings selection calls goto for each key move, so the route guard can add that request to the accepted-action interval. Settings itself starts one User and Plugin refresh on mount; the prior audit saw one /api/v1/plugins request across five moves. I will cache the signed-in route-check result and coalesce its initial read; the explicit auth-change path will still refresh it.
Author
Owner

Finding: a Notes tree move set the new row's tabindex to 0 in focusRow, then wrote the same value again in its onfocus handler. I changed both paths to skip no-op writes. The interaction profile checks that one move changes at most the previous and current row. The exact live Notes render pin now follows the source hash; no performance threshold changed. bun run check reports 0 perf-lint violations and 0 Svelte errors (4 existing warnings); the focused suite passes 21/21 tests. The five-sample before/after profile is pending.

Finding: a Notes tree move set the new row's `tabindex` to `0` in `focusRow`, then wrote the same value again in its `onfocus` handler. I changed both paths to skip no-op writes. The interaction profile checks that one move changes at most the previous and current row. The exact live Notes render pin now follows the source hash; no performance threshold changed. `bun run check` reports 0 perf-lint violations and 0 Svelte errors (4 existing warnings); the focused suite passes 21/21 tests. The five-sample before/after profile is pending.
Author
Owner

Finding for #1392: Inbox.show() refreshed the first page on every open, even when the loaded real page was younger than the existing 60-second poll interval. That caused an unnecessary inbox GET on each reopen.

Changed reopen behavior to reuse a recent loaded page. First open still loads, a page at or beyond the poll interval refreshes, and push/visibility refreshes remain immediate. Added coverage for both warm reuse and stale reopen while cached rows stay visible. No API or server behavior changed.

Evidence: focused Vitest passed (3 files, 22 tests); bun run check passed with 0 errors and 4 existing Svelte warnings. perf-lint passed with 0 violations; the exact source pin was refreshed without changing the ratchet.

Finding for #1392: `Inbox.show()` refreshed the first page on every open, even when the loaded real page was younger than the existing 60-second poll interval. That caused an unnecessary inbox GET on each reopen. Changed reopen behavior to reuse a recent loaded page. First open still loads, a page at or beyond the poll interval refreshes, and push/visibility refreshes remain immediate. Added coverage for both warm reuse and stale reopen while cached rows stay visible. No API or server behavior changed. Evidence: focused Vitest passed (3 files, 22 tests); `bun run check` passed with 0 errors and 4 existing Svelte warnings. `perf-lint` passed with 0 violations; the exact source pin was refreshed without changing the ratchet.
Author
Owner

Perf setup finding (before samples): the first VM pair exited before collecting interactions because /root/calternal-slowsel-1392/tests/adversarial/prepare-media-runtime.sh is absent from the perf worktree (ENOENT). The checkout also lacks the media launcher sources. The profile needs this runtime through the shared e2e harness, so I am transferring a supplied runtime built locally from the existing launcher and prebuilt dropcaps helper. This keeps VM setup under perf-run and performs no build on the VM. Branch head: 52feb8df0170026d0666eea25cf2c8404ac8277e.

Perf setup finding (before samples): the first VM pair exited before collecting interactions because `/root/calternal-slowsel-1392/tests/adversarial/prepare-media-runtime.sh` is absent from the perf worktree (`ENOENT`). The checkout also lacks the media launcher sources. The profile needs this runtime through the shared e2e harness, so I am transferring a supplied runtime built locally from the existing launcher and prebuilt dropcaps helper. This keeps VM setup under `perf-run` and performs no build on the VM. Branch head: `52feb8df0170026d0666eea25cf2c8404ac8277e`.
Author
Owner

Second perf setup finding (before samples): after the media runtime was supplied, the release server exited before readiness with error while loading shared libraries: libsherpa-onnx-c-api.so: cannot open shared object file. The matching prebuilt runtime has libsherpa-onnx-c-api.so and libonnxruntime.so under the shared server target. I am transferring those prebuilt libraries under perf-run and setting LD_LIBRARY_PATH for both sides; the VM will not build or replace the release server. No samples were collected. Branch head: 52feb8df0170026d0666eea25cf2c8404ac8277e.

Second perf setup finding (before samples): after the media runtime was supplied, the release server exited before readiness with `error while loading shared libraries: libsherpa-onnx-c-api.so: cannot open shared object file`. The matching prebuilt runtime has `libsherpa-onnx-c-api.so` and `libonnxruntime.so` under the shared server target. I am transferring those prebuilt libraries under `perf-run` and setting `LD_LIBRARY_PATH` for both sides; the VM will not build or replace the release server. No samples were collected. Branch head: `52feb8df0170026d0666eea25cf2c8404ac8277e`.
Author
Owner

Round 3 local production profile of a real API-created 5,000-paragraph Note (server REV d395b6e25): editor-ready in 7,669 ms; 5,001 rendered paragraphs; 8,310 DOM nodes. Five-minute use sample: JS heap 52.7 MB after ready, 62.5 MB at 30 s, then 35.6 MB at 300 s after GC; long-task observer recorded 43 tasks / 11.186 s total, including load. Five distant scroll-and-select samples measured 592–973 ms; ten more ranged 391–1,506 ms. These include scrolling between distant blocks. With a 1 s delayed Note GET route, selection caused zero Note GETs. The view stayed visible offline and online. The host load average was 26.21 / 28.31 / 24.19. The existing #1125 local baseline is 1,874 ms for 900 blocks; this is not a controlled comparison. The data and scripts are in target/tmp/bash3-large-profile-round3.log and target/tmp/bash3-large-profile.mjs.

Round 3 local production profile of a real API-created 5,000-paragraph Note (server REV d395b6e25): editor-ready in 7,669 ms; 5,001 rendered paragraphs; 8,310 DOM nodes. Five-minute use sample: JS heap 52.7 MB after ready, 62.5 MB at 30 s, then 35.6 MB at 300 s after GC; long-task observer recorded 43 tasks / 11.186 s total, including load. Five distant scroll-and-select samples measured 592–973 ms; ten more ranged 391–1,506 ms. These include scrolling between distant blocks. With a 1 s delayed Note GET route, selection caused zero Note GETs. The view stayed visible offline and online. The host load average was 26.21 / 28.31 / 24.19. The existing #1125 local baseline is 1,874 ms for 900 blocks; this is not a controlled comparison. The data and scripts are in `target/tmp/bash3-large-profile-round3.log` and `target/tmp/bash3-large-profile.mjs`.
Author
Owner

Perf VM measurement status: the benchmark transfer and input hashes match the committed work. At 06:11 CEST, perf-run status reported held: calternal apiaudit2-read pid=1949367 start=2026-10-08T06:06:41+02:00 expected_min=50; my first before 1 phase is queued through perf-run. No benchmark sample has started, and no queued or failed setup attempt is counted. I will continue with interleaved before/after phases once the shared lock is released.

Perf VM measurement status: the benchmark transfer and input hashes match the committed work. At 06:11 CEST, `perf-run status` reported `held: calternal apiaudit2-read pid=1949367 start=2026-10-08T06:06:41+02:00 expected_min=50`; my first `before 1` phase is queued through `perf-run`. No benchmark sample has started, and no queued or failed setup attempt is counted. I will continue with interleaved before/after phases once the shared lock is released.
Author
Owner

Benchmark harness finding: the first before 1 run indexed the full fixture (1,000 Notes, 1,000 Tasks, 12 Notifications, 84 Events and 84 Calendar Tasks) and recorded 10/11 requested rows. Tasks / close item failed its visibility assertion because the filtered harness fell back to double-clicking the whole Task row: the URL changed to /t/6fd61e34-1a22-4787-913d-508f273aab6f, then Escape left that URL instead of returning to /tasks. The earlier #1369 audit targets the visible Task title link, which opens the closable preview. This first report is excluded from all measurements; I have changed the filtered harness to require that link and will rerun it.

Benchmark harness finding: the first `before 1` run indexed the full fixture (1,000 Notes, 1,000 Tasks, 12 Notifications, 84 Events and 84 Calendar Tasks) and recorded 10/11 requested rows. `Tasks / close item` failed its visibility assertion because the filtered harness fell back to double-clicking the whole Task row: the URL changed to `/t/6fd61e34-1a22-4787-913d-508f273aab6f`, then Escape left that URL instead of returning to `/tasks`. The earlier #1369 audit targets the visible Task title link, which opens the closable preview. This first report is excluded from all measurements; I have changed the filtered harness to require that link and will rerun it.
Author
Owner

Benchmark isolation finding: the filtered runner selected only requested interaction actions, but main() still navigated through every Tab before runTab() returned early for unrelated tabs. That left unrelated route requests and render work inside each sample's resource window. The harness now skips unrequested Tabs before navigation. The updated script hash on the perf VM is 5ac6f8aa2e305f44a9354bae695ed474d5d24581220abd425100ccb9fda9d3fe; the queued baseline phase will use it.

Benchmark isolation finding: the filtered runner selected only requested interaction actions, but `main()` still navigated through every Tab before `runTab()` returned early for unrelated tabs. That left unrelated route requests and render work inside each sample's resource window. The harness now skips unrequested Tabs before navigation. The updated script hash on the perf VM is `5ac6f8aa2e305f44a9354bae695ed474d5d24581220abd425100ccb9fda9d3fe`; the queued baseline phase will use it.
Author
Owner

Follow-up to the Task harness finding: the second before 1 report confirmed that the Task title link is a stable Calendar deep link (/calendar/day/<date>?task=<id>). Escape had been timed as soon as that URL changed, before the Calendar popover finished opening, so it did not test the close action. The harness now waits for .cal-popover[role="dialog"] to become visible before timing Escape, then asserts that the popover detaches while the deep-link URL remains. The second report is excluded as incomplete.

Follow-up to the Task harness finding: the second `before 1` report confirmed that the Task title link is a stable Calendar deep link (`/calendar/day/<date>?task=<id>`). Escape had been timed as soon as that URL changed, before the Calendar popover finished opening, so it did not test the close action. The harness now waits for `.cal-popover[role="dialog"]` to become visible before timing Escape, then asserts that the popover detaches while the deep-link URL remains. The second report is excluded as incomplete.
Author
Owner

After sample 1 failed its Composer invariant: the filtered audit uses full browser navigations, then triggered Meta+n before the shell's 1.2-second requestIdleCallback timeout could finish. It measured a cold route remount, so immutable Composer module/style requests began during the action (Calendar open 590.2 ms, Tasks open 283.9 ms). The harness now waits for network idle plus 1.3 seconds on both before and after sides before timing Composer open. The no-immutable-request assertion remains enabled for the after side; this failed report is excluded.

After sample 1 failed its Composer invariant: the filtered audit uses full browser navigations, then triggered `Meta+n` before the shell's 1.2-second `requestIdleCallback` timeout could finish. It measured a cold route remount, so immutable Composer module/style requests began during the action (Calendar open 590.2 ms, Tasks open 283.9 ms). The harness now waits for network idle plus 1.3 seconds on both before and after sides before timing Composer open. The no-immutable-request assertion remains enabled for the after side; this failed report is excluded.
Author
Owner

The idle-preload rerun measured all 11 rows, but the after assertions found setup work crossing two input windows: Tasks / close item saw a /api/v1/calendar/year request during Calendar route activation, and Notifications focus-next changed four row tab stops while the fixture refresh/focus effects were still settling. I added the same networkidle plus two-frame settle before those measurements on both sides, including after focusing the initial row. The assertions remain unchanged: no forbidden request on Task close, and at most two row tabstop mutations per focus move. This report is excluded.

The idle-preload rerun measured all 11 rows, but the after assertions found setup work crossing two input windows: `Tasks / close item` saw a `/api/v1/calendar/year` request during Calendar route activation, and Notifications focus-next changed four row tab stops while the fixture refresh/focus effects were still settling. I added the same `networkidle` plus two-frame settle before those measurements on both sides, including after focusing the initial row. The assertions remain unchanged: no forbidden request on Task close, and at most two row tabstop mutations per focus move. This report is excluded.
Author
Owner

Follow-up on the route/list settle finding: Playwright's networkidle can report immediately for an in-app route change because the document itself did not reload. The final harness helper now watches the real request set and waits for 800 ms with no active requests, then two paint frames, before timing. It keeps a 20-second bound and fails the sample if the route never settles.

Follow-up on the route/list settle finding: Playwright's `networkidle` can report immediately for an in-app route change because the document itself did not reload. The final harness helper now watches the real request set and waits for 800 ms with no active requests, then two paint frames, before timing. It keeps a 20-second bound and fails the sample if the route never settles.
Author
Owner

Finished — job/slowsel-1392

HEAD: 6dd9dfc9615c72c33039d8ce8e931303daadadb3

Built

  • Cache verified private-route status and coalesce the first route check. The server still authorizes every API request.
  • Keep Notes and Notifications focus moves to two direct tab-stop updates instead of rerendering every loaded row.
  • Reuse a recent Notifications page on reopen; refresh on the existing 60-second poll cadence, visibility change, or push event.
  • Preload Composer during idle time and retain the mounted component. Refresh the exact performance pins without changing thresholds.
  • Add the filtered #1392 interaction profile and setup checks.

Files

apps/web/src/lib/auth/route-status.ts, route-status.test.ts; apps/web/src/lib/notes/NotesExplorer.svelte; apps/web/src/lib/notifications/InboxPanel.svelte, inbox.svelte.ts, inbox.svelte.test.ts; apps/web/src/lib/search/access.svelte.ts, access.test.ts; apps/web/src/routes/+layout.svelte; bench/interaction-audit-1392.mjs; contracts/perf/adoption-1058.json; contracts/perf/exceptions.json.

Performance evidence

Measured on the perf VM with the production release server and production web assets. Profile size: 1,000 Notes, 1,000 Tasks, 12 Notifications, 84 Events and 84 Calendar Tasks. Before commit ef0aa12b; after app build commit 52feb8df. Both phases used the same audit script hash. There was one baseline phase and one after phase, with 3 samples per row. The requested 5+ samples and repeated before/after interleaving were not completed within the job time limit, so treat this table as preliminary.

Latency values are p50 / p95 milliseconds (before → after, n=3 each):

Interaction Before After
Calendar — previous selection 627.7 / 734.1 455.2 / 536.2
Calendar — open Composer 395.0 / 543.7 208.6 / 217.4
Notes — next selection 86.2 / 149.6 149.2 / 232.8
Notifications — open inbox 285.5 / 311.6 271.9 / 299.3
Notifications — reopen inbox 383.5 / 434.7 224.8 / 316.8
Notifications — next focus 246.5 / 294.4 227.9 / 282.2
Notifications — previous focus 168.0 / 230.7 105.2 / 159.8
Settings — next selection 306.4 / 397.4 95.9 / 284.4
Settings — previous selection 198.9 / 226.9 216.1 / 225.9
Tasks — close preview 978.1 / 1051.6 958.0 / 1010.3 †
Tasks — open Composer 322.3 / 339.3 216.9 / 249.2

† The after audit exited 1: one of the three Task preview-close samples started GET /api/v1/calendar/year during the measured action. The reported raw latency includes that sample; only two samples passed the request invariant. This is the separate #1391 overlay-close path, which this job was told not to change. Other after rows passed their invariants.

Whole-run resources (fixture setup included): before CPU 39.09% of host, mean RSS 1,207 MiB, peak RSS 2,023 MiB; after CPU 37.85%, mean RSS 1,204 MiB, peak RSS 2,146 MiB. Host load averages (1/5/15 min) were 0.60/1.94/2.97 at before start and 4.98/3.29/3.26 at finish; after start and finish were 3.88/3.13/3.20 and 5.55/3.89/3.45. The load shift and three-sample count limit comparisons.

Expected work per warm interaction: route changes reuse the verified status with zero auth reads; Notes and Notifications focus moves update at most two row tab stops; a recent Notifications reopen performs zero reads and a stale reopen performs one; a warmed Composer open performs no module load. Notes/Tasks/Calendar fixture seeding remains bounded-concurrency setup work.

UX gaps closed / left

  • Closed: repeated list-wide renders on keyboard focus movement; repeated private-route checks; repeat inbox reads on a fresh reopen; cold Composer load on the first open after signed-in idle.
  • Left: Task preview close still produced the one forbidden Calendar year request noted above. It belongs to excluded #1391. The 62 macOS-emulated screenshots covering touched screens, light/dark and 390/820/1440 widths are attached to this issue.

Gates

bun run check (passed):

PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages
perf-lint: PASS; 0 violations; 22543 scoped exceptions
svelte-check found 0 errors and 4 warnings in 2 files

Focused Vitest (passed):

Test Files  3 passed (3)
      Tests  22 passed (22)
   Duration  22.42s

cargo fmt --check: passed, no output. No Rust files changed, so no Rust crate clippy or test gates ran. node --check bench/interaction-audit-1392.mjs and git diff --check passed. cargo clean completed; web build output was removed.

Decisions not specified by DESIGN

  • Keep the UI route-status cache in memory until an auth/session change; API authorization remains server-side on every request.
  • Use requestIdleCallback with a 1,200 ms timeout for Composer preload and a 180 ms timer fallback. Keep Composer mounted to preserve drafts and uploads.
  • Use the existing 60-second Notifications poll interval as the reopen freshness window; visibility and push refreshes remain immediate.
  • Update roving tab stops directly because the rows are already mounted and the operation only needs to change the previous/current row.
## Finished — `job/slowsel-1392` **HEAD:** `6dd9dfc9615c72c33039d8ce8e931303daadadb3` ### Built - Cache verified private-route status and coalesce the first route check. The server still authorizes every API request. - Keep Notes and Notifications focus moves to two direct tab-stop updates instead of rerendering every loaded row. - Reuse a recent Notifications page on reopen; refresh on the existing 60-second poll cadence, visibility change, or push event. - Preload Composer during idle time and retain the mounted component. Refresh the exact performance pins without changing thresholds. - Add the filtered #1392 interaction profile and setup checks. ### Files `apps/web/src/lib/auth/route-status.ts`, `route-status.test.ts`; `apps/web/src/lib/notes/NotesExplorer.svelte`; `apps/web/src/lib/notifications/InboxPanel.svelte`, `inbox.svelte.ts`, `inbox.svelte.test.ts`; `apps/web/src/lib/search/access.svelte.ts`, `access.test.ts`; `apps/web/src/routes/+layout.svelte`; `bench/interaction-audit-1392.mjs`; `contracts/perf/adoption-1058.json`; `contracts/perf/exceptions.json`. ### Performance evidence Measured on the perf VM with the production release server and production web assets. Profile size: 1,000 Notes, 1,000 Tasks, 12 Notifications, 84 Events and 84 Calendar Tasks. Before commit `ef0aa12b`; after app build commit `52feb8df`. Both phases used the same audit script hash. There was one baseline phase and one after phase, with 3 samples per row. The requested 5+ samples and repeated before/after interleaving were not completed within the job time limit, so treat this table as preliminary. Latency values are p50 / p95 milliseconds (`before → after`, n=3 each): | Interaction | Before | After | |---|---:|---:| | Calendar — previous selection | 627.7 / 734.1 | 455.2 / 536.2 | | Calendar — open Composer | 395.0 / 543.7 | 208.6 / 217.4 | | Notes — next selection | 86.2 / 149.6 | 149.2 / 232.8 | | Notifications — open inbox | 285.5 / 311.6 | 271.9 / 299.3 | | Notifications — reopen inbox | 383.5 / 434.7 | 224.8 / 316.8 | | Notifications — next focus | 246.5 / 294.4 | 227.9 / 282.2 | | Notifications — previous focus | 168.0 / 230.7 | 105.2 / 159.8 | | Settings — next selection | 306.4 / 397.4 | 95.9 / 284.4 | | Settings — previous selection | 198.9 / 226.9 | 216.1 / 225.9 | | Tasks — close preview | 978.1 / 1051.6 | 958.0 / 1010.3 † | | Tasks — open Composer | 322.3 / 339.3 | 216.9 / 249.2 | † The after audit exited 1: one of the three Task preview-close samples started `GET /api/v1/calendar/year` during the measured action. The reported raw latency includes that sample; only two samples passed the request invariant. This is the separate #1391 overlay-close path, which this job was told not to change. Other after rows passed their invariants. Whole-run resources (fixture setup included): before CPU 39.09% of host, mean RSS 1,207 MiB, peak RSS 2,023 MiB; after CPU 37.85%, mean RSS 1,204 MiB, peak RSS 2,146 MiB. Host load averages (1/5/15 min) were 0.60/1.94/2.97 at before start and 4.98/3.29/3.26 at finish; after start and finish were 3.88/3.13/3.20 and 5.55/3.89/3.45. The load shift and three-sample count limit comparisons. Expected work per warm interaction: route changes reuse the verified status with zero auth reads; Notes and Notifications focus moves update at most two row tab stops; a recent Notifications reopen performs zero reads and a stale reopen performs one; a warmed Composer open performs no module load. Notes/Tasks/Calendar fixture seeding remains bounded-concurrency setup work. ### UX gaps closed / left - Closed: repeated list-wide renders on keyboard focus movement; repeated private-route checks; repeat inbox reads on a fresh reopen; cold Composer load on the first open after signed-in idle. - Left: Task preview close still produced the one forbidden Calendar year request noted above. It belongs to excluded #1391. The 62 macOS-emulated screenshots covering touched screens, light/dark and 390/820/1440 widths are attached to this issue. ### Gates `bun run check` (passed): ```text PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages perf-lint: PASS; 0 violations; 22543 scoped exceptions svelte-check found 0 errors and 4 warnings in 2 files ``` Focused Vitest (passed): ```text Test Files 3 passed (3) Tests 22 passed (22) Duration 22.42s ``` `cargo fmt --check`: passed, no output. No Rust files changed, so no Rust crate clippy or test gates ran. `node --check bench/interaction-audit-1392.mjs` and `git diff --check` passed. `cargo clean` completed; web build output was removed. ### Decisions not specified by DESIGN - Keep the UI route-status cache in memory until an auth/session change; API authorization remains server-side on every request. - Use `requestIdleCallback` with a 1,200 ms timeout for Composer preload and a 180 ms timer fallback. Keep Composer mounted to preserve drafts and uploads. - Use the existing 60-second Notifications poll interval as the reopen freshness window; visibility and push refreshes remain immediate. - Update roving tab stops directly because the rows are already mounted and the operation only needs to change the previous/current row.
Author
Owner

Starting requested continuation on job/slowsel-1392, head 6dd9dfc9615c72c33039d8ce8e931303daadadb3; merge base with locally recorded origin/dev: ef0aa12b12e0047cf9a779827cc0ed32261b934e. I will refresh origin/dev for the A/B baseline, investigate Notes selection and Calendar previous-selection on the perf VM, then run the requested web gates and report the VM table.

Starting requested continuation on `job/slowsel-1392`, head `6dd9dfc9615c72c33039d8ce8e931303daadadb3`; merge base with locally recorded `origin/dev`: `ef0aa12b12e0047cf9a779827cc0ed32261b934e`. I will refresh `origin/dev` for the A/B baseline, investigate Notes selection and Calendar previous-selection on the perf VM, then run the requested web gates and report the VM table.
Author
Owner

Source comparison against origin/dev at af202e1514955ddf0f8b4b3d2503c780d807ed84: that branch removes RouteStatusCache and NotesExplorer’s direct old/new row tabindex writes. It restores reactive focusedPath and moves DOM focus through the queued selector lookup. The Calendar route and Calendar data files show no diff between the two trees. This is source evidence only; the perf VM trace and A/B samples will determine the measured cause.

Source comparison against `origin/dev` at `af202e1514955ddf0f8b4b3d2503c780d807ed84`: that branch removes `RouteStatusCache` and NotesExplorer’s direct old/new row `tabindex` writes. It restores reactive `focusedPath` and moves DOM focus through the queued selector lookup. The Calendar route and Calendar data files show no diff between the two trees. This is source evidence only; the perf VM trace and A/B samples will determine the measured cause.
Author
Owner

The #1392 audit always set CALTERNAL_E2E_ASSET_OVERRIDE=1, so it could not measure the server-embedded origin/dev build as the baseline. I changed it to use embedded assets by default and use the worktree build only when the environment flag is set; each report records the asset source. The shared server CURRENT points to revision af202e151, so the A condition uses that embedded build and B uses this worktree build. node --check bench/interaction-audit-1392.mjs and git diff --check pass. Committed as a71226b30.

The #1392 audit always set `CALTERNAL_E2E_ASSET_OVERRIDE=1`, so it could not measure the server-embedded origin/dev build as the baseline. I changed it to use embedded assets by default and use the worktree build only when the environment flag is set; each report records the asset source. The shared server `CURRENT` points to revision `af202e151`, so the A condition uses that embedded build and B uses this worktree build. `node --check bench/interaction-audit-1392.mjs` and `git diff --check` pass. Committed as `a71226b30`.
Author
Owner

VM trace finding (pre-fix; perf-run calternal slowsel2-trace): one Calendar previous-selection sample took 295.1 ms input-to-visible. The Chrome trace recorded 111.25 ms of FunctionCall time; the largest single call was 77.42 ms in /_app/immutable/chunks/BIPLcumV.js:2. It also recorded 53.92 ms UpdateLayoutTree, 19.78 ms Layout and 12.76 ms Paint. The input window started 18 requests, including multiple /api/v1/calendar/range and /api/v1/calendar/items reads plus Day-only reads. Source inspection found the grid view effect clears its loaded chunk map on every view change, so switching Week → Day re-requests unchanged week-aligned chunks. The trace is diagnostic evidence only; final A/B results will use five interleaved pairs on the VM.

VM trace finding (pre-fix; `perf-run calternal slowsel2-trace`): one Calendar previous-selection sample took 295.1 ms input-to-visible. The Chrome trace recorded 111.25 ms of FunctionCall time; the largest single call was 77.42 ms in `/_app/immutable/chunks/BIPLcumV.js:2`. It also recorded 53.92 ms UpdateLayoutTree, 19.78 ms Layout and 12.76 ms Paint. The input window started 18 requests, including multiple `/api/v1/calendar/range` and `/api/v1/calendar/items` reads plus Day-only reads. Source inspection found the grid view effect clears its loaded chunk map on every view change, so switching Week → Day re-requests unchanged week-aligned chunks. The trace is diagnostic evidence only; final A/B results will use five interleaved pairs on the VM.
Author
Owner

Notes source finding: the keyboard selection path stays inside the mounted Notes explorer and does not navigate the route, so the layout route-status cache is not on this action path. The changed focusRow() queried the target row and imperatively wrote both tab stops before queuing focus. I restored the Svelte-managed focusedPath and bound tabindex path, which updates row state through the existing keyed tree. This addresses the owner-supplied 149/233 ms regression signal; the VM A/B run will confirm it is at or below origin/dev. The audit still checks that no Notes index read starts and observes the row tab-stop mutation count. Web check, production build, and full web tests passed.

Notes source finding: the keyboard selection path stays inside the mounted Notes explorer and does not navigate the route, so the layout route-status cache is not on this action path. The changed `focusRow()` queried the target row and imperatively wrote both tab stops before queuing focus. I restored the Svelte-managed `focusedPath` and bound `tabindex` path, which updates row state through the existing keyed tree. This addresses the owner-supplied 149/233 ms regression signal; the VM A/B run will confirm it is at or below origin/dev. The audit still checks that no Notes index read starts and observes the row tab-stop mutation count. Web check, production build, and full web tests passed.
Author
Owner

The final VM trace completed under perf-run calternal slowsel2-trace-final using the server-embedded baseline web build (server revision af202e151). The host had 4 CPUs and load averages [4.13, 4.26, 3.25] at start. The seeded Calendar contained 84 Events and 84 Tasks.

Baseline Calendar previous-selection measured 384.8 ms input-to-visible and started 14 requests before the visible checkpoint, including four /api/v1/calendar/range and four /api/v1/calendar/items reads. The trace attributed 478.31 ms to FunctionCall events, with the top minified runtime calls at 308.95 ms (i in BIPLcumV.js) and 125.18 ms (anonymous in the same chunk); UpdateLayoutTree totaled 80.51 ms, Layout 12.69 ms, and Paint 8.21 ms.

The candidate defers route/data work until after the selector's first painted frame and retains loaded week-aligned chunks across Day/Week switches. The interleaved five-sample A/B runs are queued behind the current VM lock; I will report their results separately.

The final VM trace completed under `perf-run calternal slowsel2-trace-final` using the server-embedded baseline web build (server revision `af202e151`). The host had 4 CPUs and load averages `[4.13, 4.26, 3.25]` at start. The seeded Calendar contained 84 Events and 84 Tasks. Baseline Calendar previous-selection measured 384.8 ms input-to-visible and started 14 requests before the visible checkpoint, including four `/api/v1/calendar/range` and four `/api/v1/calendar/items` reads. The trace attributed 478.31 ms to `FunctionCall` events, with the top minified runtime calls at 308.95 ms (`i` in `BIPLcumV.js`) and 125.18 ms (anonymous in the same chunk); `UpdateLayoutTree` totaled 80.51 ms, `Layout` 12.69 ms, and `Paint` 8.21 ms. The candidate defers route/data work until after the selector's first painted frame and retains loaded week-aligned chunks across Day/Week switches. The interleaved five-sample A/B runs are queued behind the current VM lock; I will report their results separately.
Author
Owner

The first locked VM A/B attempt produced invalid samples: both runs stopped with Notes and Tasks fixtures still missing their requested 1,000 rows at the 180,000 ms limit. Candidate sample 1 also reported id is not defined in the Calendar first-frame assertion, and Settings selection included an /api/v1/auth/me read that was still in flight from route setup. I am tightening the audit so incomplete required fixtures fail before measurement, Settings setup reads settle before arming the input, and Calendar checks use the same interaction key as the measurement. I will rerun the sweep only after confirming complete fixtures.

The first locked VM A/B attempt produced invalid samples: both runs stopped with Notes and Tasks fixtures still missing their requested 1,000 rows at the 180,000 ms limit. Candidate sample 1 also reported `id is not defined` in the Calendar first-frame assertion, and Settings selection included an `/api/v1/auth/me` read that was still in flight from route setup. I am tightening the audit so incomplete required fixtures fail before measurement, Settings setup reads settle before arming the input, and Calendar checks use the same interaction key as the measurement. I will rerun the sweep only after confirming complete fixtures.
Author
Owner

The first complete-fixture trial on the perf VM was rejected before measuring interactions: the required Notes fixture reported indexed 0/1,000 after 600,000 ms at bounded seed concurrency 32. The VM load average recorded at the end was 4.33, 4.12, 3.38. I am checking the existing Notes write contracts and the audit seeding path; this partial run is excluded from the A/B table.

The first complete-fixture trial on the perf VM was rejected before measuring interactions: the required Notes fixture reported indexed 0/1,000 after 600,000 ms at bounded seed concurrency 32. The VM load average recorded at the end was 4.33, 4.12, 3.38. I am checking the existing Notes write contracts and the audit seeding path; this partial run is excluded from the A/B table.
Author
Owner

The timeout is consistent with the production write contract: POST /api/v1/notes acquires the per-User Notes lock and writes plus indexes one Note per request (crates/plugins/notes/src/lib.rs, create). The Notes plugin exposes no batch create route. Bounded browser concurrency therefore does not parallelize the durable writes. I have queued one retry with the same 1,000-row fixture and a 20-minute per-fixture cap; no API behavior is being added for this audit.

The timeout is consistent with the production write contract: `POST /api/v1/notes` acquires the per-User Notes lock and writes plus indexes one Note per request (`crates/plugins/notes/src/lib.rs`, `create`). The Notes plugin exposes no batch create route. Bounded browser concurrency therefore does not parallelize the durable writes. I have queued one retry with the same 1,000-row fixture and a 20-minute per-fixture cap; no API behavior is being added for this audit.
Author
Owner

I committed the audit's single-fixture A/B mode (f6af7bc9b): it clones the authenticated browser state, uses separate baseline and candidate pages on the same real server, and alternates one sample per side for five passes. It asserts five valid timings for every requested row and only enforces candidate invariants. node --check and git diff --check pass. The retry is queued behind the current perf VM holder with a 20-minute cap per required fixture; the 600-second attempt remains excluded.

I committed the audit's single-fixture A/B mode (`f6af7bc9b`): it clones the authenticated browser state, uses separate baseline and candidate pages on the same real server, and alternates one sample per side for five passes. It asserts five valid timings for every requested row and only enforces candidate invariants. `node --check` and `git diff --check` pass. The retry is queued behind the current perf VM holder with a 20-minute cap per required fixture; the 600-second attempt remains excluded.
Author
Owner

The 1,200,000 ms retry also failed before interaction measurement: the real Notes API did not finish indexing 1,000 Notes, so the report guard rejected it. The perf VM load average at the end was 4.55, 4.51, 3.97. No partial A/B timing is accepted. I am checking existing public file-write routes for a way to create the same indexed fixture without adding a server API or changing the fixture size.

The 1,200,000 ms retry also failed before interaction measurement: the real Notes API did not finish indexing 1,000 Notes, so the report guard rejected it. The perf VM load average at the end was 4.55, 4.51, 3.97. No partial A/B timing is accepted. I am checking existing public file-write routes for a way to create the same indexed fixture without adding a server API or changing the fixture size.
Author
Owner

Finished work on job/slowsel-1392

Head: f6af7bc9bc99525013ab30797462b3344aa5f158.

Built

  • Reused verified session status across private route changes and cached Search access across Settings navigation. Auth and plugin changes still force refreshes; the server remains the API access gate.
  • Kept the Notes tree’s roving focus update in Svelte state before moving focus. Notifications now reuse a fresh loaded page and update only the old and new row tab stops during keyboard navigation.
  • Made Calendar view selection paint immediately, then start route work after a frame. Week-aligned data stays cached across view changes and is cleared after writes.
  • Preloaded Composer while the signed-in shell is idle.
  • Added strict fixture completion checks and interleaved baseline/candidate mode to the #1392 interaction audit. It alternates five samples per side on one real fixture server and rejects incomplete required fixtures instead of writing partial reports.

Expected hot-path work: warm route changes reuse client state without another auth/catalog request; inbox focus changes write two tab stops; a warm inbox reopen makes no request until its 60-second freshness window expires; Calendar reuses loaded week-aligned chunks when their range is unchanged.

Files

  • apps/web/src/lib/auth/route-status.ts, apps/web/src/lib/auth/route-status.test.ts
  • apps/web/src/lib/notes/NotesExplorer.svelte
  • apps/web/src/lib/notifications/InboxPanel.svelte, apps/web/src/lib/notifications/inbox.svelte.ts, apps/web/src/lib/notifications/inbox.svelte.test.ts
  • apps/web/src/lib/search/access.svelte.ts, apps/web/src/lib/search/access.test.ts
  • apps/web/src/routes/+layout.svelte
  • apps/web/src/routes/calendar/[view]/[date]/+page.svelte
  • bench/interaction-audit-1392.mjs
  • contracts/perf/adoption-1058.json, contracts/perf/exceptions.json

Perf VM evidence and gap

The valid pre-change Calendar trace ran on the perf VM at load average 4.13, 4.26, 3.25. Previous selection took 384.8 ms input-to-visible and issued 14 requests before visibility (four Calendar range and four Calendar item reads among them). Trace totals: FunctionCall 478.31 ms; minified i in BIPLcumV.js 308.95 ms; anonymous work in that chunk 125.18 ms; UpdateLayoutTree 80.51 ms; Layout 12.69 ms; Paint 8.21 ms. The trace is attached: https://git.kayg.org/attachments/8d54bd8a-7786-480d-98bc-e360b703e228

No valid post-change A/B table was produced. Both VM attempts failed before measurement: the required Notes fixture reported indexed 0/1000 after 600,000 ms and again after 1,200,000 ms. The Notes writer takes a per-User lock for each create, and this plugin has no batch-create route, so bounded client concurrency did not make the 1,000-Note fixture settle. The perf VM lock was then reserved by another job through the remaining job window. These attempts produced no valid selection samples. Notes next, Settings previous, and Calendar previous are therefore unverified against origin/dev; I cannot claim the ≤100 ms Calendar p95 target or the Notes non-regression. The requested final A/B table remains outstanding.

Gates

bun run check output:

perf-lint: PASS; 0 violations; 22544 scoped exceptions
User browser caches use userStorage; only documented device/public-link exceptions remain.
Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css.
Text sizes and UI shape values use shared role tokens.
Keyboard focus rings use the shared focus tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
svelte-check found 0 errors and 4 warnings in 2 files

bun run test --maxWorkers=2 Vitest output:

 Test Files  298 passed (298)
      Tests  2127 passed (2127)
   Start at  17:46:07
   Duration  536.41s (environment 27%, transform 27%, import 22%, tests 18%, setup 5%)

The zsh wrapper then reported zsh:1: read-only variable: status while it assigned the captured exit code; Vitest itself reported all 298 files and 2,127 tests passed. git diff --check and the benchmark script syntax check passed. No Rust files changed, so Rust gates were not run. cargo clean completed (Removed 0 files); apps/web/build and apps/web/.svelte-kit were removed.

UX gaps

Closed: warm Notes focus movement, Notifications row focus updates, cached warm inbox reopen, immediate Calendar selected-view paint.

Left: no candidate-side performance verification or complete VM table; additional Notes, Tasks, Settings and Notifications screenshots were not captured. The Notifications bell stayed hidden in the capture context and the build-update toast persisted despite the asset override. Calendar evidence is attached: click, shortcut, 390 light, 390 dark, 820 light, 820 dark, 1440 light, 1440 dark.

Decisions not covered by DESIGN

  • Keep the required Notes fixture at 1,000 items and reject incomplete reports; do not lower the fixture size to obtain a result that is not comparable.
  • Run baseline and candidate contexts against one seeded real server, alternating their samples to avoid repeating fixture writes between sides.
  • Do not add a Notes batch-create endpoint just for this audit; that changes the server contract. A fixture path that can seed the existing real API data reliably is still needed.

The branch is committed and clean. The performance acceptance requested in the review is not complete; rerun the VM A/B after the Notes fixture setup can settle.

## Finished work on `job/slowsel-1392` Head: `f6af7bc9bc99525013ab30797462b3344aa5f158`. ### Built - Reused verified session status across private route changes and cached Search access across Settings navigation. Auth and plugin changes still force refreshes; the server remains the API access gate. - Kept the Notes tree’s roving focus update in Svelte state before moving focus. Notifications now reuse a fresh loaded page and update only the old and new row tab stops during keyboard navigation. - Made Calendar view selection paint immediately, then start route work after a frame. Week-aligned data stays cached across view changes and is cleared after writes. - Preloaded Composer while the signed-in shell is idle. - Added strict fixture completion checks and interleaved baseline/candidate mode to the #1392 interaction audit. It alternates five samples per side on one real fixture server and rejects incomplete required fixtures instead of writing partial reports. Expected hot-path work: warm route changes reuse client state without another auth/catalog request; inbox focus changes write two tab stops; a warm inbox reopen makes no request until its 60-second freshness window expires; Calendar reuses loaded week-aligned chunks when their range is unchanged. ### Files - `apps/web/src/lib/auth/route-status.ts`, `apps/web/src/lib/auth/route-status.test.ts` - `apps/web/src/lib/notes/NotesExplorer.svelte` - `apps/web/src/lib/notifications/InboxPanel.svelte`, `apps/web/src/lib/notifications/inbox.svelte.ts`, `apps/web/src/lib/notifications/inbox.svelte.test.ts` - `apps/web/src/lib/search/access.svelte.ts`, `apps/web/src/lib/search/access.test.ts` - `apps/web/src/routes/+layout.svelte` - `apps/web/src/routes/calendar/[view]/[date]/+page.svelte` - `bench/interaction-audit-1392.mjs` - `contracts/perf/adoption-1058.json`, `contracts/perf/exceptions.json` ### Perf VM evidence and gap The valid pre-change Calendar trace ran on the perf VM at load average `4.13, 4.26, 3.25`. Previous selection took `384.8 ms` input-to-visible and issued 14 requests before visibility (four Calendar range and four Calendar item reads among them). Trace totals: `FunctionCall 478.31 ms`; minified `i` in `BIPLcumV.js` `308.95 ms`; anonymous work in that chunk `125.18 ms`; `UpdateLayoutTree 80.51 ms`; `Layout 12.69 ms`; `Paint 8.21 ms`. The trace is attached: https://git.kayg.org/attachments/8d54bd8a-7786-480d-98bc-e360b703e228 No valid post-change A/B table was produced. Both VM attempts failed before measurement: the required Notes fixture reported `indexed 0/1000` after 600,000 ms and again after 1,200,000 ms. The Notes writer takes a per-User lock for each create, and this plugin has no batch-create route, so bounded client concurrency did not make the 1,000-Note fixture settle. The perf VM lock was then reserved by another job through the remaining job window. These attempts produced no valid selection samples. Notes next, Settings previous, and Calendar previous are therefore **unverified against origin/dev**; I cannot claim the ≤100 ms Calendar p95 target or the Notes non-regression. The requested final A/B table remains outstanding. ### Gates `bun run check` output: ```text perf-lint: PASS; 0 violations; 22544 scoped exceptions User browser caches use userStorage; only documented device/public-link exceptions remain. Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css. Text sizes and UI shape values use shared role tokens. Keyboard focus rings use the shared focus tokens. UI transitions and animation options use shared motion tokens or documented exceptions. svelte-check found 0 errors and 4 warnings in 2 files ``` `bun run test --maxWorkers=2` Vitest output: ```text Test Files 298 passed (298) Tests 2127 passed (2127) Start at 17:46:07 Duration 536.41s (environment 27%, transform 27%, import 22%, tests 18%, setup 5%) ``` The zsh wrapper then reported `zsh:1: read-only variable: status` while it assigned the captured exit code; Vitest itself reported all 298 files and 2,127 tests passed. `git diff --check` and the benchmark script syntax check passed. No Rust files changed, so Rust gates were not run. `cargo clean` completed (`Removed 0 files`); `apps/web/build` and `apps/web/.svelte-kit` were removed. ### UX gaps Closed: warm Notes focus movement, Notifications row focus updates, cached warm inbox reopen, immediate Calendar selected-view paint. Left: no candidate-side performance verification or complete VM table; additional Notes, Tasks, Settings and Notifications screenshots were not captured. The Notifications bell stayed hidden in the capture context and the build-update toast persisted despite the asset override. Calendar evidence is attached: [click](https://git.kayg.org/attachments/30316e97-f4bb-46d1-ac84-135b751052b6), [shortcut](https://git.kayg.org/attachments/a49b812b-b818-4a9b-9c6f-886f15185db0), [390 light](https://git.kayg.org/attachments/f791bb82-c68e-4494-bf75-e26f1f60d619), [390 dark](https://git.kayg.org/attachments/0fb8e4e2-198a-4ffd-90d5-36cf714a2c27), [820 light](https://git.kayg.org/attachments/11d8a9a3-e930-4678-8359-0d103b1ee7dd), [820 dark](https://git.kayg.org/attachments/47964e35-b5d5-4463-ab9a-03dd85eb9696), [1440 light](https://git.kayg.org/attachments/c37b0aee-c9c2-4492-9a12-9c915bd36345), [1440 dark](https://git.kayg.org/attachments/f3633df9-c6fc-4dcf-a116-6f5c6d9d3442). ### Decisions not covered by DESIGN - Keep the required Notes fixture at 1,000 items and reject incomplete reports; do not lower the fixture size to obtain a result that is not comparable. - Run baseline and candidate contexts against one seeded real server, alternating their samples to avoid repeating fixture writes between sides. - Do not add a Notes batch-create endpoint just for this audit; that changes the server contract. A fixture path that can seed the existing real API data reliably is still needed. The branch is committed and clean. The performance acceptance requested in the review is not complete; rerun the VM A/B after the Notes fixture setup can settle.
Author
Owner

Starting #1392 on job/slowsel-1392, head f6af7bc9bc99525013ab30797462b3344aa5f158, based on origin/dev merge base af202e1514955ddf0f8b4b3d2503c780d807ed84.

I am tracing why the prior Notes fixture reported indexed 0/1000. I will make fixture readiness observable with a bounded failure if the harness is at fault, or file a separate issue with a minimal repro if the server indexer is stalled. Then I will run the requested 3-round interleaved Mac Chrome comparison, attempt the perf VM confirmation within the 30-minute window, and run the specified web gates.

Starting #1392 on `job/slowsel-1392`, head `f6af7bc9bc99525013ab30797462b3344aa5f158`, based on `origin/dev` merge base `af202e1514955ddf0f8b4b3d2503c780d807ed84`. I am tracing why the prior Notes fixture reported `indexed 0/1000`. I will make fixture readiness observable with a bounded failure if the harness is at fault, or file a separate issue with a minimal repro if the server indexer is stalled. Then I will run the requested 3-round interleaved Mac Chrome comparison, attempt the perf VM confirmation within the 30-minute window, and run the specified web gates.
Author
Owner

Finding: the indexed 0/1000 status was the benchmark harness's all-or-nothing seed counter, not a measured Index count. In crates/plugins/notes/src/lib.rs, create() holds the per-User Notes lock and awaits store::index() before it returns HTTP 201. The harness sent 1,000 separate POSTs; its bounded browser workers could not parallelize those durable writes. seedNotes() only assigned fixture.indexed.notes = noteCount after the entire batch resolved, so any fixture timeout left the report at 0 even if earlier Notes had been indexed. The filtered Notes path then waited for only two visible tree rows, not the requested fixture count. This is a fixture setup/reporting flaw; I found no evidence of an Indexer stall and will not change server behavior for the audit.

I will seed the 1,000 test Notes on disk while the disposable server is stopped, restart it, call the existing POST /api/v1/notes/reconcile, and verify the real paged GET /api/v1/notes result reaches 1,000 before measurement. The failure message will include the actual count and timeout.

Finding: the `indexed 0/1000` status was the benchmark harness's all-or-nothing seed counter, not a measured Index count. In `crates/plugins/notes/src/lib.rs`, `create()` holds the per-User Notes lock and awaits `store::index()` before it returns HTTP 201. The harness sent 1,000 separate POSTs; its bounded browser workers could not parallelize those durable writes. `seedNotes()` only assigned `fixture.indexed.notes = noteCount` after the entire batch resolved, so any fixture timeout left the report at 0 even if earlier Notes had been indexed. The filtered Notes path then waited for only two visible tree rows, not the requested fixture count. This is a fixture setup/reporting flaw; I found no evidence of an Indexer stall and will not change server behavior for the audit. I will seed the 1,000 test Notes on disk while the disposable server is stopped, restart it, call the existing `POST /api/v1/notes/reconcile`, and verify the real paged `GET /api/v1/notes` result reaches 1,000 before measurement. The failure message will include the actual count and timeout.
Author
Owner

The disk-backed Notes fixture completed in the focused run. It wrote 1,000 Markdown files while the server was stopped, restarted the same Instance, called POST /api/v1/notes/reconcile, and waited for paginated GET /api/v1/notes results. The real Notes Index exposed 1,000/1,000 Audit Note #### rows, then the Notes selection sample completed. This reproduces the prior indexed 0/1000 as a harness counter bug; no server indexing stall was observed, so I did not create a separate server issue.

The disk-backed Notes fixture completed in the focused run. It wrote 1,000 Markdown files while the server was stopped, restarted the same Instance, called `POST /api/v1/notes/reconcile`, and waited for paginated `GET /api/v1/notes` results. The real Notes Index exposed 1,000/1,000 `Audit Note ####` rows, then the Notes selection sample completed. This reproduces the prior `indexed 0/1000` as a harness counter bug; no server indexing stall was observed, so I did not create a separate server issue.
Author
Owner

The first Mac Chrome A/B reached final sample validation but rejected the Calendar day samples. The click ran, but the readiness predicate called native document.querySelector with Playwright-only :visible syntax, so the browser rejected the selector and the harness marked those timings invalid. This is a benchmark assertion bug, not evidence of an app selection failure. I replaced it with a standard DOM query and will rerun the complete three-round set.

The first Mac Chrome A/B reached final sample validation but rejected the Calendar day samples. The click ran, but the readiness predicate called native `document.querySelector` with Playwright-only `:visible` syntax, so the browser rejected the selector and the harness marked those timings invalid. This is a benchmark assertion bug, not evidence of an app selection failure. I replaced it with a standard DOM query and will rerun the complete three-round set.
Author
Owner

The corrected Mac run passed Calendar day readiness, then stopped before writing a report because the baseline had only 2 of the 3 requested Notes/move selection next samples (2 !== 3). The interleaved harness caught the underlying Notes route error and discarded it, leaving only the final count assertion. I am changing the interleaved path to surface that original error, then I will decide whether this is a fixture/harness issue or an app failure.

The corrected Mac run passed Calendar day readiness, then stopped before writing a report because the baseline had only 2 of the 3 requested `Notes/move selection next` samples (`2 !== 3`). The interleaved harness caught the underlying Notes route error and discarded it, leaving only the final count assertion. I am changing the interleaved path to surface that original error, then I will decide whether this is a fixture/harness issue or an app failure.
Author
Owner

The diagnostic run exposed the next setup issue: candidate Notifications failed at locator('#chrome-notifications').waitFor({ state: 'visible' }) after 5,000 ms. The audit navigates to /today but skips its usual route-content assertion for Notifications, so it can seek the bell before the signed-in shell restores it. I added the route activity settle check before looking for the bell and a 20-second readiness timeout. This is a harness setup race; no UI code changed.

The diagnostic run exposed the next setup issue: candidate Notifications failed at `locator('#chrome-notifications').waitFor({ state: 'visible' })` after 5,000 ms. The audit navigates to `/today` but skips its usual route-content assertion for Notifications, so it can seek the bell before the signed-in shell restores it. I added the route activity settle check before looking for the bell and a 20-second readiness timeout. This is a harness setup race; no UI code changed.
Author
Owner

The latest Mac run reached the candidate Notes route and timed out after 20 seconds waiting for the second .notes-explorer [role="treeitem"]; no Notes-next sample was taken. The audit's fixture API had already indexed 1,000 Notes, but the UI did not expose two visible tree rows in that candidate context. I added a bounded diagnostic snapshot of the explorer row count and recent Notes/files API statuses, with no Note body content, so the next run can distinguish a UI load delay from a missing or failed list response. I have not changed UI code.

The latest Mac run reached the candidate Notes route and timed out after 20 seconds waiting for the second `.notes-explorer [role="treeitem"]`; no Notes-next sample was taken. The audit's fixture API had already indexed 1,000 Notes, but the UI did not expose two visible tree rows in that candidate context. I added a bounded diagnostic snapshot of the explorer row count and recent Notes/files API statuses, with no Note body content, so the next run can distinguish a UI load delay from a missing or failed list response. I have not changed UI code.
Author
Owner

The isolated three-round Mac Chrome Notes A/B completed successfully after indexing 1,000/1,000 Notes. Notes-next baseline was p50/p95 1100.6/1192.8 ms; candidate was 1107.0/1172.1 ms (+0.6% p50). No sample failed. The prior candidate tree timeout did not reproduce in the isolated run; I am rerunning the complete six-action set for the fixed decision.

The isolated three-round Mac Chrome Notes A/B completed successfully after indexing 1,000/1,000 Notes. Notes-next baseline was p50/p95 1100.6/1192.8 ms; candidate was 1107.0/1172.1 ms (+0.6% p50). No sample failed. The prior candidate tree timeout did not reproduce in the isolated run; I am rerunning the complete six-action set for the fixed decision.
Author
Owner

The fixture now waits for both real indexes: paginated GET /api/v1/notes and paginated GET /api/v1/files/entries?path=Notes&notes_filter=true. The focused Linux run indexed 1,000/1,000 in each API and then completed the Notes selection sample. This explains the full-run tree timeout: the Notes Index was ready, but the separate Files Index used by the sidebar was not. I am committing this readiness check and will rerun the full Mac comparison.

The fixture now waits for both real indexes: paginated `GET /api/v1/notes` and paginated `GET /api/v1/files/entries?path=Notes&notes_filter=true`. The focused Linux run indexed 1,000/1,000 in each API and then completed the Notes selection sample. This explains the full-run tree timeout: the Notes Index was ready, but the separate Files Index used by the sidebar was not. I am committing this readiness check and will rerun the full Mac comparison.
Author
Owner

Completed

Branch: job/slowsel-1392
Head: 8255d108678c58228a8d7d3f8f312ac808da932a

bench/interaction-audit-1392.mjs now writes 1,000 valid Note files before server startup, restarts the disposable server, runs the existing reconcile action, and waits with bounded diagnostics for both the Notes Index and the Files Index used by the Notes sidebar. The old indexed 0/1000 report was a harness counter issue; a focused run reached 1,000/1,000 in both indexes.

The filtered Inbox selection audit now starts at the existing /notifications deep link. This removes lazy bell restoration from the row-focus setup. No UI source or styles changed. The origin/dev merge is committed at 02987bc46; the exact performance pins changed by that merge were refreshed without changing their scopes, rules, limits or ratchet.

Mac Chrome audit

The run used Mac Chrome for Testing 155.0.8059.39 with the Metal renderer, a 1440 × 900 viewport, the shared release server at cc1af3c12, and three interleaved baseline/candidate rounds. Five interactions completed. Notes was excluded after the separate full run stopped at the Notes route.

Interaction Baseline p50 / p95 ms Candidate p50 / p95 ms p50 change
Calendar select day 1307.0 / 1589.4 1548.5 / 1709.4 +18.5%
Calendar select Event 1353.0 / 1462.5 1456.9 / 1556.8 +7.7%
Calendar previous 1408.9 / 1480.7 1465.9 / 2022.8 +4.0%
Settings previous 1008.3 / 1298.9 1104.4 / 1133.6 +9.5%
Inbox move 1216.1 / 1307.0 1151.5 / 1193.1 -5.3%
Notes next Not measured Not measured —

Decision: REJECT. Calendar day p50 regressed by 18.5%. Calendar select p95 was 1709.4 ms for day and 1556.8 ms for Event; both exceed the 100 ms limit. The Notes metric is also incomplete.

The Notes route finding is filed as #1480. The full Mac run reached /notes after the fixture indexes reported 1,000/1,000, but the tree stayed empty and its /api/v1/files/entries and /api/v1/notes requests remained pending for 20 seconds. No 5xx response was observed.

The perf VM confirmation was skipped. perf-run status showed it occupied by searchperf2; the VM also had no Chrome/Chromium or Node executable available.

Gates

bun run check passed. These status lines are verbatim from its output; the guard also printed its scoped DEBT records:

NOTE Bun skipped 1 lock package(s) not installed in this production environment
PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages
perf-lint: PASS; 0 violations; 22544 scoped exceptions
User browser caches use userStorage; only documented device/public-link exceptions remain.
Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css.
Text sizes and UI shape values use shared role tokens.
Focus policy passes: 50 focus ring rules, 134 DOM focus calls and 3 data-input writers audited.
UI transitions and animation options use shared motion tokens or documented exceptions.

====================================
svelte-check found 0 errors and 4 warnings in 2 files

node --check bench/interaction-audit-1392.mjs passed. cargo clean removed 1 file, 356 B. The generated apps/web/build directory was removed.

Gaps and decisions

  • UX gaps closed: None; this job changed the measurement harness only.
  • UX gaps left: the Notes tree remains empty while its route list requests are pending; tracked in #1480.
  • Decision: the Notes sidebar needs readiness from both indexes because it reads the Files Index.
  • Decision: the selection-only Inbox audit uses the stable /notifications deep link to avoid measuring shell restoration.
  • For the merge round: run bun run test -- --maxWorkers=2 to prove the full web Vitest suite passes on the merged branch. This suite was deferred by the shared verification policy for ordinary jobs.
## Completed Branch: `job/slowsel-1392` Head: `8255d108678c58228a8d7d3f8f312ac808da932a` `bench/interaction-audit-1392.mjs` now writes 1,000 valid Note files before server startup, restarts the disposable server, runs the existing reconcile action, and waits with bounded diagnostics for both the Notes Index and the Files Index used by the Notes sidebar. The old `indexed 0/1000` report was a harness counter issue; a focused run reached 1,000/1,000 in both indexes. The filtered Inbox selection audit now starts at the existing `/notifications` deep link. This removes lazy bell restoration from the row-focus setup. No UI source or styles changed. The `origin/dev` merge is committed at `02987bc46`; the exact performance pins changed by that merge were refreshed without changing their scopes, rules, limits or ratchet. ## Mac Chrome audit The run used Mac Chrome for Testing 155.0.8059.39 with the Metal renderer, a 1440 × 900 viewport, the shared release server at `cc1af3c12`, and three interleaved baseline/candidate rounds. Five interactions completed. Notes was excluded after the separate full run stopped at the Notes route. | Interaction | Baseline p50 / p95 ms | Candidate p50 / p95 ms | p50 change | | --- | ---: | ---: | ---: | | Calendar select day | 1307.0 / 1589.4 | 1548.5 / 1709.4 | +18.5% | | Calendar select Event | 1353.0 / 1462.5 | 1456.9 / 1556.8 | +7.7% | | Calendar previous | 1408.9 / 1480.7 | 1465.9 / 2022.8 | +4.0% | | Settings previous | 1008.3 / 1298.9 | 1104.4 / 1133.6 | +9.5% | | Inbox move | 1216.1 / 1307.0 | 1151.5 / 1193.1 | -5.3% | | Notes next | Not measured | Not measured | — | **Decision: REJECT.** Calendar day p50 regressed by 18.5%. Calendar select p95 was 1709.4 ms for day and 1556.8 ms for Event; both exceed the 100 ms limit. The Notes metric is also incomplete. The Notes route finding is filed as #1480. The full Mac run reached `/notes` after the fixture indexes reported 1,000/1,000, but the tree stayed empty and its `/api/v1/files/entries` and `/api/v1/notes` requests remained pending for 20 seconds. No 5xx response was observed. The perf VM confirmation was skipped. `perf-run status` showed it occupied by `searchperf2`; the VM also had no Chrome/Chromium or Node executable available. ## Gates `bun run check` passed. These status lines are verbatim from its output; the guard also printed its scoped `DEBT` records: ```text NOTE Bun skipped 1 lock package(s) not installed in this production environment PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages perf-lint: PASS; 0 violations; 22544 scoped exceptions User browser caches use userStorage; only documented device/public-link exceptions remain. Glass alpha, blur and backdrop-filter roles use packages/ui/src/tokens.css. Text sizes and UI shape values use shared role tokens. Focus policy passes: 50 focus ring rules, 134 DOM focus calls and 3 data-input writers audited. UI transitions and animation options use shared motion tokens or documented exceptions. ==================================== svelte-check found 0 errors and 4 warnings in 2 files ``` `node --check bench/interaction-audit-1392.mjs` passed. `cargo clean` removed 1 file, 356 B. The generated `apps/web/build` directory was removed. ## Gaps and decisions - UX gaps closed: None; this job changed the measurement harness only. - UX gaps left: the Notes tree remains empty while its route list requests are pending; tracked in #1480. - Decision: the Notes sidebar needs readiness from both indexes because it reads the Files Index. - Decision: the selection-only Inbox audit uses the stable `/notifications` deep link to avoid measuring shell restoration. - For the merge round: run `bun run test -- --maxWorkers=2` to prove the full web Vitest suite passes on the merged branch. This suite was deferred by the shared verification policy for ordinary jobs.
Author
Owner

Orchestrator decision: branch job/slowsel-1392 (8255d1086) REJECTED by the pre-set rule. Mac Chrome: Calendar select day p50 +18.5% (1307 -> 1548 ms), Calendar p95 > 100 ms. Not merging. Bigger finding: the BASELINE itself is 1.0-1.4 s per selection on Mac Chrome; a root-cause profiling job follows (selroot). Notes empty tree tracked in #1480.

Orchestrator decision: branch job/slowsel-1392 (8255d1086) REJECTED by the pre-set rule. Mac Chrome: Calendar select day p50 +18.5% (1307 -> 1548 ms), Calendar p95 > 100 ms. Not merging. Bigger finding: the BASELINE itself is 1.0-1.4 s per selection on Mac Chrome; a root-cause profiling job follows (selroot). Notes empty tree tracked in #1480.
Author
Owner

Starting the #1392/#1480 transport investigation. Branch: job/slowsel-1392 at 8255d108678c58228a8d7d3f8f312ac808da932a; base: origin/dev at cc1af3c12b68ad26c8403adde1be39c096b9ddea. I am using this branch's supplied interaction audit harness and the shared production server at that revision. The prior #1480 run observed pending Notes list requests after 20 seconds but did not record protocol, connection IDs, or queueing time. I will capture those details on Mac Chrome and Linux headless Chrome, then repeat Mac baseline measurements over HTTP/2 if the HTTP/1.1 tunnel is the cause. The Mac VM is currently held by intlfmt-1461-820; I will wait for its lock to clear.

Starting the #1392/#1480 transport investigation. Branch: `job/slowsel-1392` at `8255d108678c58228a8d7d3f8f312ac808da932a`; base: `origin/dev` at `cc1af3c12b68ad26c8403adde1be39c096b9ddea`. I am using this branch's supplied interaction audit harness and the shared production server at that revision. The prior #1480 run observed pending Notes list requests after 20 seconds but did not record protocol, connection IDs, or queueing time. I will capture those details on Mac Chrome and Linux headless Chrome, then repeat Mac baseline measurements over HTTP/2 if the HTTP/1.1 tunnel is the cause. The Mac VM is currently held by `intlfmt-1461-820`; I will wait for its lock to clear.
Author
Owner

Linux control finding (harness instrumentation commit 687997abb, origin/dev server cc1af3c12): headless Chromium used http/1.1. Calendar had two open EventSource requests (/api/v1/files/events, connection IDs 46 and 142); Notes had two (/api/v1/files/events and /api/v1/notes/events, IDs 1156 and 1192). The Notes route exposed rows and completed one selection sample; both indexes reached 1,000/1,000. In the Calendar route's NetLog, the calendar/range, calendar/items and calendar/preferences socket-pool waits were 18, 43 and 15 ms. This is one local headless sample, not a clean latency baseline: host load was 28.28 on 12 CPUs and the 1.67 s Calendar action overlapped long tasks of 544, 151 and 97 ms. I will use the Mac run and post-H2 three-round samples for the comparison.

Linux control finding (harness instrumentation commit `687997abb`, origin/dev server `cc1af3c12`): headless Chromium used `http/1.1`. Calendar had two open `EventSource` requests (`/api/v1/files/events`, connection IDs 46 and 142); Notes had two (`/api/v1/files/events` and `/api/v1/notes/events`, IDs 1156 and 1192). The Notes route exposed rows and completed one selection sample; both indexes reached 1,000/1,000. In the Calendar route's NetLog, the `calendar/range`, `calendar/items` and `calendar/preferences` socket-pool waits were 18, 43 and 15 ms. This is one local headless sample, not a clean latency baseline: host load was 28.28 on 12 CPUs and the 1.67 s Calendar action overlapped long tasks of 544, 151 and 97 ms. I will use the Mac run and post-H2 three-round samples for the comparison.
Author
Owner

Linux Notes-route trace finding (commit 6f1cb2361): with both 1,000-row indexes ready, headless Chromium completed the Notes list requests on http/1.1: GET /api/v1/notes returned HTTP 200 with 79 ms and 225 ms to response headers; GET /api/v1/files/entries returned HTTP 200 in 1,815 ms. NetLog showed no socket-pool stall on either list endpoint. The Notes route exposed rows and the selection sample completed in 265 ms. Two open EventSource streams were present (/api/v1/files/events, /api/v1/notes/events). Host load was 30.12 on 12 CPUs, so the selection timing is only a control sample; the endpoint protocol/status evidence is still direct.

Linux Notes-route trace finding (commit `6f1cb2361`): with both 1,000-row indexes ready, headless Chromium completed the Notes list requests on `http/1.1`: `GET /api/v1/notes` returned HTTP 200 with 79 ms and 225 ms to response headers; `GET /api/v1/files/entries` returned HTTP 200 in 1,815 ms. NetLog showed no socket-pool stall on either list endpoint. The Notes route exposed rows and the selection sample completed in 265 ms. Two open EventSource streams were present (`/api/v1/files/events`, `/api/v1/notes/events`). Host load was 30.12 on 12 CPUs, so the selection timing is only a control sample; the endpoint protocol/status evidence is still direct.
Author
Owner

The audit was writing Chromium NetLogs in Default mode. Because the browser setup flow uses an auth URL, those logs could include sensitive URL values. I changed the harness to use HeavilyRedacted capture and removed the four earlier temporary NetLogs without opening or printing them. The new mode keeps timing and protocol metadata while dropping URLs and credentials.

The audit was writing Chromium NetLogs in Default mode. Because the browser setup flow uses an auth URL, those logs could include sensitive URL values. I changed the harness to use HeavilyRedacted capture and removed the four earlier temporary NetLogs without opening or printing them. The new mode keeps timing and protocol metadata while dropping URLs and credentials.
Author
Owner

The first Linux three-route control reported six pending EventSource rows for Notes. They included rows from earlier full-page loads. I added the current top-frame loader ID to the diagnostics and now the same three-route control reports two live streams at the snapshot: /api/v1/files/events and /api/v1/notes/events, both HTTP/1.1 status 200 (CDP connection IDs 1000 and 152). The local host load was 20.36 on 12 CPUs, so these route timings are transport controls, not clean latency baselines.

The first Linux three-route control reported six pending EventSource rows for Notes. They included rows from earlier full-page loads. I added the current top-frame loader ID to the diagnostics and now the same three-route control reports two live streams at the snapshot: `/api/v1/files/events` and `/api/v1/notes/events`, both HTTP/1.1 status 200 (CDP connection IDs 1000 and 152). The local host load was 20.36 on 12 CPUs, so these route timings are transport controls, not clean latency baselines.
Author
Owner

Mac HTTP/1.1 baseline on origin/dev server cc1af3c12, Chrome 154.0.8037.98, via the plain HTTP reverse SSH tunnel. Calendar day selection: p50/p95 1292.9/1385.9 ms across 3 samples. Notes selection: 1039.1/2142.9 ms. /notes reached two real rows in 5968.2 ms (one route sample).

CDP reported HTTP/1.1. At the Notes snapshot, two current-document EventSource streams were open: /api/v1/files/events on connection 1735 and /api/v1/notes/events on 1721. All 12 /api/v1/notes reads returned 200; p95 response time was 525.27 ms. The one /api/v1/files/entries read returned 200 after 4106.83 ms with 0 ms socket-pool stall. NetLog matched all 182 target requests to URL_REQUEST start events within 11 ms. This run did not reproduce the 20-second pending Notes-list requests. Calendar bursts did show socket-pool waits: p95 488 ms for /calendar/range and 487 ms for /calendar/items, with maxima 558 and 595 ms.

The server host load was 15.53 at start and 20.94 at end (12 CPUs); Mac load average after the run was 2.74. Trace and redacted network artifacts are in target/tmp/1392-mac-h1-calendar-trace.json.gz, target/tmp/1392-mac-h1-notes-trace.json.gz, and target/tmp/1392-mac-h1.json. I released the Mac lock before preparing the HTTP/2 phase.

Mac HTTP/1.1 baseline on origin/dev server `cc1af3c12`, Chrome 154.0.8037.98, via the plain HTTP reverse SSH tunnel. Calendar day selection: p50/p95 1292.9/1385.9 ms across 3 samples. Notes selection: 1039.1/2142.9 ms. `/notes` reached two real rows in 5968.2 ms (one route sample). CDP reported HTTP/1.1. At the Notes snapshot, two current-document EventSource streams were open: `/api/v1/files/events` on connection 1735 and `/api/v1/notes/events` on 1721. All 12 `/api/v1/notes` reads returned 200; p95 response time was 525.27 ms. The one `/api/v1/files/entries` read returned 200 after 4106.83 ms with 0 ms socket-pool stall. NetLog matched all 182 target requests to URL_REQUEST start events within 11 ms. This run did not reproduce the 20-second pending Notes-list requests. Calendar bursts did show socket-pool waits: p95 488 ms for `/calendar/range` and 487 ms for `/calendar/items`, with maxima 558 and 595 ms. The server host load was 15.53 at start and 20.94 at end (12 CPUs); Mac load average after the run was 2.74. Trace and redacted network artifacts are in `target/tmp/1392-mac-h1-calendar-trace.json.gz`, `target/tmp/1392-mac-h1-notes-trace.json.gz`, and `target/tmp/1392-mac-h1.json`. I released the Mac lock before preparing the HTTP/2 phase.
Author
Owner

Correction to the load sentence above: the harness captured the Linux host load average once at run start as 15.53/19.79/20.94 (1/5/15 minutes) on 12 CPUs; those are not start/end values. The Mac sysctl vm.loadavg reading after the run was 2.74/2.95/2.66 (1/5/15 minutes).

Correction to the load sentence above: the harness captured the Linux host load average once at run start as 15.53/19.79/20.94 (1/5/15 minutes) on 12 CPUs; those are not start/end values. The Mac `sysctl vm.loadavg` reading after the run was 2.74/2.95/2.66 (1/5/15 minutes).
Author
Owner

Local H2 control against the same origin/dev server cc1af3c12 confirms the TLS proxy path. All captured first-party requests reported h2; NetLog matched 135 target requests and found no socket-pool stalls. Three Calendar day selections measured p50/p95 314.6/333.9 ms; Notes selection measured 114.2/173.5 ms. Notes route readiness was 3755, 4229.2 and 4227.1 ms (p50/p95 4227.1/4229.2). Linux load average at start was 26.94/25.28/22.92 (1/5/15 minutes) on 12 CPUs, so treat these as protocol evidence, not a clean timing comparison. The corresponding redacted summaries are in target/tmp/1392-linux-h2-queues.json and target/tmp/1392-linux-h2-rounds.json.

Local H2 control against the same origin/dev server `cc1af3c12` confirms the TLS proxy path. All captured first-party requests reported `h2`; NetLog matched 135 target requests and found no socket-pool stalls. Three Calendar day selections measured p50/p95 314.6/333.9 ms; Notes selection measured 114.2/173.5 ms. Notes route readiness was 3755, 4229.2 and 4227.1 ms (p50/p95 4227.1/4229.2). Linux load average at start was 26.94/25.28/22.92 (1/5/15 minutes) on 12 CPUs, so treat these as protocol evidence, not a clean timing comparison. The corresponding redacted summaries are in `target/tmp/1392-linux-h2-queues.json` and `target/tmp/1392-linux-h2-rounds.json`.
Author
Owner

Clarification on the Calendar queue figures above: 488 ms (/api/v1/calendar/range) and 487 ms (/api/v1/calendar/items) are p95 among requests that had a socket-pool stall event. Including unstalled requests at 0 ms, the per-path queue p95 is 459 ms and 276 ms.

Clarification on the Calendar queue figures above: 488 ms (`/api/v1/calendar/range`) and 487 ms (`/api/v1/calendar/items`) are p95 among requests that had a socket-pool stall event. Including unstalled requests at 0 ms, the per-path queue p95 is 459 ms and 276 ms.
Author
Owner

Mac Chrome 154 H2 baseline (embedded origin/dev web build; release server cc1af3c12) ran three rounds through a local TLS/HTTP/2 proxy over the same reverse SSH tunnel. Calendar day selection p50/p95 was 1175.1/1227.6 ms; Notes next-selection was 1009.8/1080.8 ms. Notes route opened in 2832.1, 3026.1 and 3126.3 ms to two real rows. This is a large reduction from the single H1 Notes route sample at 5968.2 ms, but selection remains slow.

All 138 matched first-party requests used h2; the two Notes EventSources shared connection 522. The heavily redacted NetLog matched 138 target requests and reported zero target socket-pool stalls. Queue p50/p95/max was 0 ms for /api/v1/calendar/range, /api/v1/calendar/items, /api/v1/notes, and /api/v1/files/entries. All 36 /api/v1/notes reads completed with status 200. Five of seven /api/v1/files/entries reads completed (longest response 3677.93 ms); two were still pending at the end-of-route snapshot, but the Notes route had already reached its real-row readiness check. NetLog contained no URL values.

The Calendar trace records 70.63 ms of FunctionCall time (top call 51.08 ms), 30.22 ms UpdateLayoutTree, 16.10 ms Layout and 15.11 ms Paint. The six requests begun by the measured Calendar selection returned in 155–226 ms and had no socket queueing. This does not explain the full 1175 ms interaction sample, so I am separating browser work from the remote CDP measurement round trips with a focused Notes trace and a direct CDP RTT sample before filing causes.

Mac Chrome 154 H2 baseline (embedded origin/dev web build; release server cc1af3c12) ran three rounds through a local TLS/HTTP/2 proxy over the same reverse SSH tunnel. Calendar day selection p50/p95 was 1175.1/1227.6 ms; Notes next-selection was 1009.8/1080.8 ms. Notes route opened in 2832.1, 3026.1 and 3126.3 ms to two real rows. This is a large reduction from the single H1 Notes route sample at 5968.2 ms, but selection remains slow. All 138 matched first-party requests used `h2`; the two Notes EventSources shared connection 522. The heavily redacted NetLog matched 138 target requests and reported zero target socket-pool stalls. Queue p50/p95/max was 0 ms for `/api/v1/calendar/range`, `/api/v1/calendar/items`, `/api/v1/notes`, and `/api/v1/files/entries`. All 36 `/api/v1/notes` reads completed with status 200. Five of seven `/api/v1/files/entries` reads completed (longest response 3677.93 ms); two were still pending at the end-of-route snapshot, but the Notes route had already reached its real-row readiness check. NetLog contained no URL values. The Calendar trace records 70.63 ms of FunctionCall time (top call 51.08 ms), 30.22 ms UpdateLayoutTree, 16.10 ms Layout and 15.11 ms Paint. The six requests begun by the measured Calendar selection returned in 155–226 ms and had no socket queueing. This does not explain the full 1175 ms interaction sample, so I am separating browser work from the remote CDP measurement round trips with a focused Notes trace and a direct CDP RTT sample before filing causes.
Author
Owner

Measurement correction: the earlier Mac H1/H2 interaction values (Calendar 1292.9 / 1175.1 ms p50; Notes 1039.1 / 1009.8 ms p50) were sampled only after the harness had waited for visibility and then made additional CDP calls. The remote Chrome DevTools Protocol uses the same SSH tunnel, so these are host-observed timings and include control-channel delay; do not treat them as the final User-visible p50/p95.

I committed a harness correction in 3718eb7da and 417c7ae93. It pre-arms a browser-side probe before the trusted input, records the selected state after two browser frames, and reports input_to_observed_ms separately. A one-sample Linux H2 check measured Notes at 139.5 ms browser-visible versus 219.3 ms host-observed. This is a diagnostic only, not a stable baseline. I will rerun Mac H1 and H2 three-round baselines with the corrected timing after the shared Mac VM lock becomes available.

Measurement correction: the earlier Mac H1/H2 interaction values (Calendar 1292.9 / 1175.1 ms p50; Notes 1039.1 / 1009.8 ms p50) were sampled only after the harness had waited for visibility and then made additional CDP calls. The remote Chrome DevTools Protocol uses the same SSH tunnel, so these are host-observed timings and include control-channel delay; do not treat them as the final User-visible p50/p95. I committed a harness correction in `3718eb7da` and `417c7ae93`. It pre-arms a browser-side probe before the trusted input, records the selected state after two browser frames, and reports `input_to_observed_ms` separately. A one-sample Linux H2 check measured Notes at 139.5 ms browser-visible versus 219.3 ms host-observed. This is a diagnostic only, not a stable baseline. I will rerun Mac H1 and H2 three-round baselines with the corrected timing after the shared Mac VM lock becomes available.
Author
Owner

Linux headless control with the pre-armed browser probe (same shared server rev cc1af3c12 and 1,000 real Notes fixtures) is complete. H1 p50/p95 was Calendar day 386.4/394.0 ms and Notes next selection 120.9/192.6 ms. Local TLS/H2 p50/p95 was Calendar 305.1/345.3 ms and Notes 139.7/144.1 ms. The Notes H2 selection samples all used the browser clock; none started /api/v1/notes during the action. Host load averages were 15.32/13.64/14.83 for H1 and 20.15/15.60/15.42 for H2 on 12 CPUs, so treat this as a comparison control rather than a clean host benchmark.

The corrected Linux numbers confirm the earlier ~1 s Notes selection result was a remote-control measurement artifact. Notes selection is around 120–140 ms on this local headless run. Calendar day selection remains over 200 ms locally, so I will profile its actual browser and request costs in the corrected Mac H1/H2 runs. The Mac is currently reserved by notifread-1387; no Mac session is running from this job.

Linux headless control with the pre-armed browser probe (same shared server rev cc1af3c12 and 1,000 real Notes fixtures) is complete. H1 p50/p95 was Calendar day 386.4/394.0 ms and Notes next selection 120.9/192.6 ms. Local TLS/H2 p50/p95 was Calendar 305.1/345.3 ms and Notes 139.7/144.1 ms. The Notes H2 selection samples all used the browser clock; none started `/api/v1/notes` during the action. Host load averages were 15.32/13.64/14.83 for H1 and 20.15/15.60/15.42 for H2 on 12 CPUs, so treat this as a comparison control rather than a clean host benchmark. The corrected Linux numbers confirm the earlier ~1 s Notes selection result was a remote-control measurement artifact. Notes selection is around 120–140 ms on this local headless run. Calendar day selection remains over 200 ms locally, so I will profile its actual browser and request costs in the corrected Mac H1/H2 runs. The Mac is currently reserved by `notifread-1387`; no Mac session is running from this job.
Author
Owner

Follow-up correction to the Linux comparison: I removed one extra animation frame from the browser probe. The earlier corrected H2 Calendar value of 305.1/345.3 ms was still one frame late and is superseded. With the final two-frame probe, local Linux H2 Calendar selection is 203.2/204.7 ms p50/p95 (three samples; host load 7.93/12.53/14.29 on 12 CPUs). The six first-party API reads started by the action completed in 3–48 ms; there were no long tasks and the largest frame gap was 102 ms. The trace records 54 ms FunctionCall, 14 ms UpdateLayoutTree, 15 ms Layout and 3 ms Paint. H2 Notes selection remains 139.7/144.1 ms p50/p95, all samples using browser-side marks.

This shows the local Calendar route itself is close to the 200 ms threshold. The Mac H1/H2 comparison still needs the same final probe to separate real route time from tunnel and control-channel delay.

Follow-up correction to the Linux comparison: I removed one extra animation frame from the browser probe. The earlier corrected H2 Calendar value of 305.1/345.3 ms was still one frame late and is superseded. With the final two-frame probe, local Linux H2 Calendar selection is 203.2/204.7 ms p50/p95 (three samples; host load 7.93/12.53/14.29 on 12 CPUs). The six first-party API reads started by the action completed in 3–48 ms; there were no long tasks and the largest frame gap was 102 ms. The trace records 54 ms FunctionCall, 14 ms UpdateLayoutTree, 15 ms Layout and 3 ms Paint. H2 Notes selection remains 139.7/144.1 ms p50/p95, all samples using browser-side marks. This shows the local Calendar route itself is close to the 200 ms threshold. The Mac H1/H2 comparison still needs the same final probe to separate real route time from tunnel and control-channel delay.
Author
Owner

Final Linux control correction after reducing the browser probe to exactly two frames. The previous H1/H2 Calendar results in my interim comments included one extra frame and are superseded. With three samples per interaction, local Linux H1 is Calendar 246.5/255.5 ms and Notes 115.6/162.6 ms p50/p95. Local Linux H2 is Calendar 203.2/204.7 ms and Notes 139.7/144.1 ms. H1 and H2 host load averages were 7.37/11.79/13.96 and 7.93/12.53/14.29 on 12 CPUs. The H1 NetLog matched 165 requests, contained no URL values, and showed queue p95/max of 26/54 ms for Calendar range and 30/61 ms for Calendar items. I will use these final two-frame values in the report.

Final Linux control correction after reducing the browser probe to exactly two frames. The previous H1/H2 Calendar results in my interim comments included one extra frame and are superseded. With three samples per interaction, local Linux H1 is Calendar 246.5/255.5 ms and Notes 115.6/162.6 ms p50/p95. Local Linux H2 is Calendar 203.2/204.7 ms and Notes 139.7/144.1 ms. H1 and H2 host load averages were 7.37/11.79/13.96 and 7.93/12.53/14.29 on 12 CPUs. The H1 NetLog matched 165 requests, contained no URL values, and showed queue p95/max of 26/54 ms for Calendar range and 30/61 ms for Calendar items. I will use these final two-frame values in the report.
Author
Owner

New measurement finding: the browser probe listened at document capture, after any app window capture handler. In the Mac H2 Notes trace, the first keyboard EventDispatch lasted 541.6 ms; a 501.9 ms function from BIPLcumV.js began at the same trace timestamp, while the probe had reported 63.1 ms to visible selection. The probe therefore excluded app work that ran before its listener. I changed it to listen at window capture in commit 677d351e3. The prior post-fix Mac and Linux selection timings are withdrawn until I rerun both protocols with this ordering.

New measurement finding: the browser probe listened at `document` capture, after any app `window` capture handler. In the Mac H2 Notes trace, the first keyboard `EventDispatch` lasted 541.6 ms; a 501.9 ms function from `BIPLcumV.js` began at the same trace timestamp, while the probe had reported 63.1 ms to visible selection. The probe therefore excluded app work that ran before its listener. I changed it to listen at `window` capture in commit `677d351e3`. The prior post-fix Mac and Linux selection timings are withdrawn until I rerun both protocols with this ordering.
Author
Owner

Correction: I created /tmp/mac-bench.lock on the Linux host, not on the Mac VM. The Mac lock remained held by notifread-1387 during those Mac captures. I stopped the dedicated Chrome profile and all Mac tunnel sessions; all prior Mac timings and NetLog are withdrawn as potentially contaminated. The Linux controls below use commit 677d351e3. I will rerun Mac only after the remote lock-acquisition session owns the Mac VM lock.

Correction: I created `/tmp/mac-bench.lock` on the Linux host, not on the Mac VM. The Mac lock remained held by `notifread-1387` during those Mac captures. I stopped the dedicated Chrome profile and all Mac tunnel sessions; all prior Mac timings and NetLog are withdrawn as potentially contaminated. The Linux controls below use commit `677d351e3`. I will rerun Mac only after the remote lock-acquisition session owns the Mac VM lock.
Author
Owner

Linux control with the current event-time probe: on the real 1,000-Note fixture, H2 Notes ArrowDown measured 115.1 ms p50 / 395.2 ms p95 across three samples. The 395.2 ms sample had no critical HTTP request and a 306 ms long task during keydown. Chrome EventTiming recorded timeStamp 6192.812, processingStart 6193.516, and processingEnd 6499.416; the trace recorded 74.57 ms in BIPLcumV.js and 42.21 ms of UpdateLayoutTree. H1 Notes measured 130.0 / 145.6 ms in its three-round run. Linux host load was 10.23 / 11.65 / 13.15 on 12 CPUs. This is Linux-only evidence under a loaded host; the Mac result is still pending the VM lock.

Linux control with the current event-time probe: on the real 1,000-Note fixture, H2 Notes ArrowDown measured 115.1 ms p50 / 395.2 ms p95 across three samples. The 395.2 ms sample had no critical HTTP request and a 306 ms long task during `keydown`. Chrome EventTiming recorded `timeStamp` 6192.812, `processingStart` 6193.516, and `processingEnd` 6499.416; the trace recorded 74.57 ms in `BIPLcumV.js` and 42.21 ms of `UpdateLayoutTree`. H1 Notes measured 130.0 / 145.6 ms in its three-round run. Linux host load was 10.23 / 11.65 / 13.15 on 12 CPUs. This is Linux-only evidence under a loaded host; the Mac result is still pending the VM lock.
Author
Owner

Investigation report — Forgejo #1392

Outcome

The root cause is not established. The corrected Mac rerun could not start because the actual Mac VM lock remained held by another job. I stopped the wait at the four-hour job limit. All earlier Mac timings from the invalid-lock/probe runs are withdrawn. There is no valid Mac H1/H2 before-and-after pair, protocol capture, queueing measurement, or route trace from this job.

The dev-embedded Linux control on the 1,000-row Notes fixture measured Calendar select day at 249.4/300.8 ms p50/p95 over three H1 samples. Notes selection measured 130.0/145.6 ms on H1 and 115.1/395.2 ms on H2, each over three samples. The H2 395.2 ms sample had a 306 ms main-thread long task during keydown and no critical request holding the interaction. The Linux host load was roughly 10–13 on 12 CPUs, so these are diagnostic controls, not Mac substitutes.

On Linux, the dev Notes route exposed two real rows in all three H1 route visits (5.2813 s, 4.6135 s, 3.1718 s); one H2 route visit exposed two rows in 6.0231 s. The H2 network snapshot showed two live long-lived requests and one pending /api/v1/files/entries request. This does not establish whether the Mac report was HTTP/1.1 connection-slot starvation.

Steps

  1. Mac Chrome trace and NetLog: not obtained under the correct lock.
  2. HTTPS/H2 Mac rerun: not run. No valid Mac before/after numbers.
  3. Product profiling and issue filing: not triggered because there is no valid post-harness-fix Mac p50. No product root cause is established and no new issue was filed.

I commented on #1480 with the Linux control evidence. Its original report remains candidate-specific and is not confirmed against a valid Mac dev baseline; it is not established as harness-only. Leave #1480 open.

Mac timing preamble for the next run

Acquire the lock on the Mac VM itself before any browser or benchmark session. Run baseline H1 and H2 through HTTPS TLS proxies behind distinct SSH reverse forwards; verify each negotiated protocol. Use --ignore-certificate-errors only in the disposable Chrome profile. Capture HeavilyRedacted NetLog and report protocol, connection ID, socket-pool queue/stall time, and open long-lived request count. Time trusted event.timeStamp to the in-page visible mark after two frames, and report host-observed CDP duration separately. Compare the same dev build and fixture on H1 and H2.

Harness and decisions

Changed only bench/interaction-audit-1392.mjs. It now measures input-to-visible timing in the page, starts from the trusted event timestamp, observes before window handlers, records observer/frame delays separately, and captures content-free transport fields. This is harness instrumentation only; no product code changed.

Decision not specified in DESIGN: the measured interaction begins at the trusted browser event timestamp and ends at the in-page two-frame visible mark. This includes earlier input-handler work and avoids remote CDP round-trip time inflating the browser measurement.

Head and gates

Head: b416b2bb26. The worktree is clean. The required origin/dev fetch and merge completed; Git reported: Already up to date.

Gate output:

cargo fmt --check — exit 0, no output.
node --check bench/interaction-audit-1392.mjs — exit 0, no output.
git diff --check — exit 0, no output.
cargo clean output: Removed 1 file, 356B total

No Rust crate or web application source changed, so crate clippy/test and bun check/test gates were not applicable.

Known gap: the lock-protected Mac H1/H2 comparison, traces, and per-request queue/stall evidence remain outstanding. No UI screenshots apply to this harness-only change.

Investigation report — Forgejo #1392 ## Outcome The root cause is not established. The corrected Mac rerun could not start because the actual Mac VM lock remained held by another job. I stopped the wait at the four-hour job limit. All earlier Mac timings from the invalid-lock/probe runs are withdrawn. There is no valid Mac H1/H2 before-and-after pair, protocol capture, queueing measurement, or route trace from this job. The dev-embedded Linux control on the 1,000-row Notes fixture measured Calendar select day at 249.4/300.8 ms p50/p95 over three H1 samples. Notes selection measured 130.0/145.6 ms on H1 and 115.1/395.2 ms on H2, each over three samples. The H2 395.2 ms sample had a 306 ms main-thread long task during keydown and no critical request holding the interaction. The Linux host load was roughly 10–13 on 12 CPUs, so these are diagnostic controls, not Mac substitutes. On Linux, the dev Notes route exposed two real rows in all three H1 route visits (5.2813 s, 4.6135 s, 3.1718 s); one H2 route visit exposed two rows in 6.0231 s. The H2 network snapshot showed two live long-lived requests and one pending /api/v1/files/entries request. This does not establish whether the Mac report was HTTP/1.1 connection-slot starvation. ## Steps 1. Mac Chrome trace and NetLog: not obtained under the correct lock. 2. HTTPS/H2 Mac rerun: not run. No valid Mac before/after numbers. 3. Product profiling and issue filing: not triggered because there is no valid post-harness-fix Mac p50. No product root cause is established and no new issue was filed. I commented on #1480 with the Linux control evidence. Its original report remains candidate-specific and is not confirmed against a valid Mac dev baseline; it is not established as harness-only. Leave #1480 open. ## Mac timing preamble for the next run Acquire the lock on the Mac VM itself before any browser or benchmark session. Run baseline H1 and H2 through HTTPS TLS proxies behind distinct SSH reverse forwards; verify each negotiated protocol. Use --ignore-certificate-errors only in the disposable Chrome profile. Capture HeavilyRedacted NetLog and report protocol, connection ID, socket-pool queue/stall time, and open long-lived request count. Time trusted event.timeStamp to the in-page visible mark after two frames, and report host-observed CDP duration separately. Compare the same dev build and fixture on H1 and H2. ## Harness and decisions Changed only bench/interaction-audit-1392.mjs. It now measures input-to-visible timing in the page, starts from the trusted event timestamp, observes before window handlers, records observer/frame delays separately, and captures content-free transport fields. This is harness instrumentation only; no product code changed. Decision not specified in DESIGN: the measured interaction begins at the trusted browser event timestamp and ends at the in-page two-frame visible mark. This includes earlier input-handler work and avoids remote CDP round-trip time inflating the browser measurement. ## Head and gates Head: b416b2bb26515e11e8714f2997200155b7be78ba. The worktree is clean. The required origin/dev fetch and merge completed; Git reported: Already up to date. Gate output: cargo fmt --check — exit 0, no output. node --check bench/interaction-audit-1392.mjs — exit 0, no output. git diff --check — exit 0, no output. cargo clean output: Removed 1 file, 356B total No Rust crate or web application source changed, so crate clippy/test and bun check/test gates were not applicable. Known gap: the lock-protected Mac H1/H2 comparison, traces, and per-request queue/stall evidence remain outstanding. No UI screenshots apply to this harness-only change.
Author
Owner

Cleanup note: the stopped Chrome profile from the invalid Mac attempt remains at ~/calternal-bench/selroot1392/corrected2. I could not remove it because the shared Mac VM lock remained occupied. The local H1/H2 proxies are stopped, the local raw NetLogs and invalid Mac artifacts are deleted, and this job has no Mac browser or tunnel process running. Remove that profile during the next lock-held Mac session.

Cleanup note: the stopped Chrome profile from the invalid Mac attempt remains at ~/calternal-bench/selroot1392/corrected2. I could not remove it because the shared Mac VM lock remained occupied. The local H1/H2 proxies are stopped, the local raw NetLogs and invalid Mac artifacts are deleted, and this job has no Mac browser or tunnel process running. Remove that profile during the next lock-held Mac session.
Author
Owner

Starting the Linux trace investigation on job/slowsel-1392 at b416b2bb26515e11e8714f2997200155b7be78ba (base origin/dev merge-base cc1af3c12b68ad26c8403adde1be39c096b9ddea). I read the required guidance and prior selroot result. I will trace 10 Calendar day selections and 10 Notes selections against origin/dev, then file one issue per measured root cause. I will check the Mac lock after Linux and run the Mac samples only if it frees within 60 minutes. No product changes are planned.

Starting the Linux trace investigation on `job/slowsel-1392` at `b416b2bb26515e11e8714f2997200155b7be78ba` (base `origin/dev` merge-base `cc1af3c12b68ad26c8403adde1be39c096b9ddea`). I read the required guidance and prior `selroot` result. I will trace 10 Calendar day selections and 10 Notes selections against `origin/dev`, then file one issue per measured root cause. I will check the Mac lock after Linux and run the Mac samples only if it frees within 60 minutes. No product changes are planned.
Author
Owner

Linux trace phase complete against the shared server built from origin/dev revision cc1af3c12b68ad26c8403adde1be39c096b9ddea (10 samples per action, trusted event to visible marker, 1440x900):

  • Calendar day selection: 265.3/471.9 ms p50/p95. The trace shows 56.13 ms p50 in FunctionCall, 31.61 ms p50 combined style/layout, and five API reads started during each selection. The slowest request per input was 20/83 ms p50/p95. The local Linux host had 12 CPUs and load around 12–14.
  • Notes selection with 1,000 Notes and 1,000 Note files indexed: 108.5/315.7 ms p50/p95. The keydown EventDispatch was 64.7/273.7 ms p50/p95; style/layout/paint was 1.94 ms p50. No request completed before the visible marker.

Filed root-cause issues: #1491 (Calendar reads), #1492 (Calendar script/layout), and #1493 (Notes keydown). The trace-all support is committed on this branch as 0caadeed9.

The Mac VM check has not started: the shared local macvm.lock was busy on the first probe. I will retry within the one-hour window.

Linux trace phase complete against the shared server built from `origin/dev` revision `cc1af3c12b68ad26c8403adde1be39c096b9ddea` (10 samples per action, trusted event to visible marker, 1440x900): - Calendar day selection: **265.3/471.9 ms p50/p95**. The trace shows **56.13 ms p50** in `FunctionCall`, **31.61 ms p50** combined style/layout, and five API reads started during each selection. The slowest request per input was **20/83 ms p50/p95**. The local Linux host had 12 CPUs and load around 12–14. - Notes selection with 1,000 Notes and 1,000 Note files indexed: **108.5/315.7 ms p50/p95**. The `keydown` EventDispatch was **64.7/273.7 ms p50/p95**; style/layout/paint was **1.94 ms p50**. No request completed before the visible marker. Filed root-cause issues: #1491 (Calendar reads), #1492 (Calendar script/layout), and #1493 (Notes keydown). The trace-all support is committed on this branch as `0caadeed9`. The Mac VM check has not started: the shared local `macvm.lock` was busy on the first probe. I will retry within the one-hour window.
Author
Owner

Progress update: the Linux traces produced issues #1491, #1492, and #1493. The benchmark harness now records per-request protocol and browser-clock fetchStart → requestStart timing for both Calendar selections and /notes route opens. This is a queueing proxy; it does not prove socket starvation on its own. The instrumentation is in commits 1f9e19eb7 and 5947a09e4; no product code changed.

The local macvm.lock is still held. I have not started an SSH session or inspected/acquired /tmp/mac-bench.lock; I am continuing to retry within the requested 60-minute window.

Progress update: the Linux traces produced issues #1491, #1492, and #1493. The benchmark harness now records per-request protocol and browser-clock `fetchStart → requestStart` timing for both Calendar selections and `/notes` route opens. This is a queueing proxy; it does not prove socket starvation on its own. The instrumentation is in commits `1f9e19eb7` and `5947a09e4`; no product code changed. The local `macvm.lock` is still held. I have not started an SSH session or inspected/acquired `/tmp/mac-bench.lock`; I am continuing to retry within the requested 60-minute window.
Author
Owner

Completed #1392 follow-up

No product code changed. The baseline used the origin/dev embedded server at cc1af3c12b68ad26c8403adde1be39c096b9ddea, Linux headless Chrome, 10 Calendar day selections and 10 Notes selections. The Notes fixture had 1,000 indexed Notes and 1,000 Note files; Calendar had 84 indexed Events and 84 calendar Tasks. Both interaction medians exceed the DESIGN §59 accepted-action budget of 150 ms.

Surface Top measured costs per selection (p50 / p95)
Calendar day selection Script: FunctionCall 56.13 / 150.26 ms; the largest recurring frame was an anonymous function in minified BIPLcumV.js at 44.97 / 99.61 ms, not source-mapped to a component. Style and layout: UpdateLayoutTree plus Layout 31.61 / 48.63 ms. Requests: 4–5 GETs started on each pick; the slowest completed request was 20 / 83 ms, with /api/v1/notes/tasks/day at the same 20 / 83 ms over nine responses. These requests overlap; the last Tasks request remained pending at visibility in one sample.
Notes Explorer selection Keydown JavaScript: FunctionCall 65.72 / 274.09 ms; the longest Long Task was 66 / 274 ms. Trusted input to audit capture listener: 61.4 / 226.7 ms; this overlaps the keydown dispatch and indicates main-thread delay before capture. Style and paint: 1.94 / 39.55 ms together; no Layout event was recorded. No request completed before visibility; /api/v1/files/entries was pending in sample 9.

Overall trusted input to visible selection was Calendar 265.3 / 471.9 ms and Notes 108.5 / 315.7 ms p50/p95. These category timings overlap and should not be added. The trace does not isolate the Notes application function responsible for the long task; NotesExplorer.svelte onKeydown filters and scans rows, then updates the focused row. The production Calendar chunks have no source map.

I filed one issue per root cause, each with evidence, the 150 ms budget and a suggested direction: #1491 repeated Calendar API reads, #1492 Calendar script and layout work, and #1493 Notes keydown long task.

Linux traces and network captures remain under target/tmp/1392-calendar-audit.json, target/tmp/1392-calendar-netlog.json, target/tmp/1392-notes-audit.json, target/tmp/1392-notes-netlog.json, and the ten per-sample gzip traces for each interaction. The host had 12 CPUs and load averages of 14.38 before and 14.05 during the Calendar capture; these are local measurements.

Mac verdict: skipped (lock busy). The remote /tmp/mac-bench.lock stayed occupied through the 60-minute window. I did not access the Mac GUI, remove the prior ~/calternal-bench/selroot1392 directory, or infer a Mac protocol/connection-slot result. H1/H2 starvation over the tunnel remains unresolved.

Changes and decisions

Only bench/interaction-audit-1392.mjs changed. It retains one Chrome performance trace per requested sample and records URL-redacted nextHopProtocol, pre-request, server-wait and response timing for selections and /notes route opens. Commit 0caadeed9 retains all sample traces; 1f9e19eb7 adds per-request timing; 5947a09e4 adds /notes route timing. Head: 5947a09e4cc559b7833013552d82be57bb409f7b.

DESIGN §59 does not define the browser timing boundary or a queue metric. The audit uses trusted event timestamp to the in-page visible marker after two animation frames, and reports requestStart - fetchStart as a queueing proxy only; that value alone does not prove socket starvation.

Gates and cleanup

Verbatim check output (both commands exited 0 with no output):

$ node --check bench/interaction-audit-1392.mjs
$ git diff --check

No Rust or web application source changed, so Rust clippy/test and Bun check/test were not run. Cargo cleanup output:

     Removed 1 file, 356B total

The worktree is clean. The generated web build and local H2 proxy test key were removed. No Mac-side cleanup was done because the remote lock stayed occupied.

## Completed #1392 follow-up No product code changed. The baseline used the origin/dev embedded server at `cc1af3c12b68ad26c8403adde1be39c096b9ddea`, Linux headless Chrome, 10 Calendar day selections and 10 Notes selections. The Notes fixture had 1,000 indexed Notes and 1,000 Note files; Calendar had 84 indexed Events and 84 calendar Tasks. Both interaction medians exceed the DESIGN §59 accepted-action budget of 150 ms. | Surface | Top measured costs per selection (p50 / p95) | | --- | --- | | Calendar day selection | **Script:** `FunctionCall` 56.13 / 150.26 ms; the largest recurring frame was an anonymous function in minified `BIPLcumV.js` at 44.97 / 99.61 ms, not source-mapped to a component. **Style and layout:** `UpdateLayoutTree` plus `Layout` 31.61 / 48.63 ms. **Requests:** 4–5 GETs started on each pick; the slowest completed request was 20 / 83 ms, with `/api/v1/notes/tasks/day` at the same 20 / 83 ms over nine responses. These requests overlap; the last Tasks request remained pending at visibility in one sample. | | Notes Explorer selection | **Keydown JavaScript:** `FunctionCall` 65.72 / 274.09 ms; the longest Long Task was 66 / 274 ms. **Trusted input to audit capture listener:** 61.4 / 226.7 ms; this overlaps the keydown dispatch and indicates main-thread delay before capture. **Style and paint:** 1.94 / 39.55 ms together; no Layout event was recorded. No request completed before visibility; `/api/v1/files/entries` was pending in sample 9. | Overall trusted input to visible selection was **Calendar 265.3 / 471.9 ms** and **Notes 108.5 / 315.7 ms** p50/p95. These category timings overlap and should not be added. The trace does not isolate the Notes application function responsible for the long task; `NotesExplorer.svelte` `onKeydown` filters and scans `rows`, then updates the focused row. The production Calendar chunks have no source map. I filed one issue per root cause, each with evidence, the 150 ms budget and a suggested direction: [#1491 repeated Calendar API reads](https://git.kayg.org/kayg/calternal/issues/1491), [#1492 Calendar script and layout work](https://git.kayg.org/kayg/calternal/issues/1492), and [#1493 Notes keydown long task](https://git.kayg.org/kayg/calternal/issues/1493). Linux traces and network captures remain under `target/tmp/1392-calendar-audit.json`, `target/tmp/1392-calendar-netlog.json`, `target/tmp/1392-notes-audit.json`, `target/tmp/1392-notes-netlog.json`, and the ten per-sample gzip traces for each interaction. The host had 12 CPUs and load averages of 14.38 before and 14.05 during the Calendar capture; these are local measurements. **Mac verdict: skipped (lock busy).** The remote `/tmp/mac-bench.lock` stayed occupied through the 60-minute window. I did not access the Mac GUI, remove the prior `~/calternal-bench/selroot1392` directory, or infer a Mac protocol/connection-slot result. H1/H2 starvation over the tunnel remains unresolved. ## Changes and decisions Only `bench/interaction-audit-1392.mjs` changed. It retains one Chrome performance trace per requested sample and records URL-redacted `nextHopProtocol`, pre-request, server-wait and response timing for selections and `/notes` route opens. Commit `0caadeed9` retains all sample traces; `1f9e19eb7` adds per-request timing; `5947a09e4` adds `/notes` route timing. Head: `5947a09e4cc559b7833013552d82be57bb409f7b`. DESIGN §59 does not define the browser timing boundary or a queue metric. The audit uses trusted event timestamp to the in-page visible marker after two animation frames, and reports `requestStart - fetchStart` as a queueing proxy only; that value alone does not prove socket starvation. ## Gates and cleanup Verbatim check output (both commands exited 0 with no output): ```text $ node --check bench/interaction-audit-1392.mjs $ git diff --check ``` No Rust or web application source changed, so Rust clippy/test and Bun check/test were not run. Cargo cleanup output: ```text Removed 1 file, 356B total ``` The worktree is clean. The generated web build and local H2 proxy test key were removed. No Mac-side cleanup was done because the remote lock stayed occupied.
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#1392
No description provided.