Notes Explorer arrow navigation has a long task #1493

Closed
opened 2026-10-08 23:30:41 +00:00 by kayg · 24 comments
Owner

Evidence

This reproduces on origin/dev revision cc1af3c12b68ad26c8403adde1be39c096b9ddea with Linux headless Chrome, 1,000 indexed Notes and 1,000 Note files, and 10 ArrowDown selections in the Notes Explorer. Trusted input to the selected-row marker took 108.5 ms p50 / 315.7 ms p95, above the 150 ms Action budget in DESIGN §59.

The performance traces show a keydown EventDispatch of 64.7 ms p50 / 273.7 ms p95 and a Long Task of 66 ms p50 / 274 ms p95. The trusted event to the audit capture listener took 61.4/226.7 ms p50/p95, which means much of the delay happens before the listener can observe the key. Style, layout and paint together were only 1.94 ms p50 (39.55 ms p95 due to one outlier); no Layout event was recorded. No request completed before the selected-row marker; one /api/v1/files/entries request remained pending in sample 9.

The source path to inspect is NotesExplorer.svelte onKeydown: each arrow filters the full rows array and finds the focused row, then changes focusedPath and calls focusRow. Each rendered row derives its tabindex from that focus state. The trace has no source map, so it does not prove which expression accounts for the long task.

Trace output: target/tmp/1392-notes-audit.json and target/tmp/1392-interaction-trace-notes-move-selection-next-{1..10}.json.gz in worktree slowsel-1392.

Expected result

Moving the Notes Explorer selection takes no more than 150 ms and updates only the old and new rows, as DESIGN §59 requires.

Suggested direction

Keep the highlighted row as an index. Avoid filtering and scanning the full row list for every arrow. Narrow the focus and tabindex update to the old and new rows, then add a 1,000-Note regression measurement that fails if the Action exceeds 150 ms.

## Evidence This reproduces on `origin/dev` revision `cc1af3c12b68ad26c8403adde1be39c096b9ddea` with Linux headless Chrome, 1,000 indexed Notes and 1,000 Note files, and 10 ArrowDown selections in the Notes Explorer. Trusted input to the selected-row marker took **108.5 ms p50 / 315.7 ms p95**, above the **150 ms Action budget** in DESIGN §59. The performance traces show a `keydown` EventDispatch of **64.7 ms p50 / 273.7 ms p95** and a Long Task of **66 ms p50 / 274 ms p95**. The trusted event to the audit capture listener took 61.4/226.7 ms p50/p95, which means much of the delay happens before the listener can observe the key. Style, layout and paint together were only **1.94 ms p50** (39.55 ms p95 due to one outlier); no Layout event was recorded. No request completed before the selected-row marker; one `/api/v1/files/entries` request remained pending in sample 9. The source path to inspect is `NotesExplorer.svelte` `onKeydown`: each arrow filters the full `rows` array and finds the focused row, then changes `focusedPath` and calls `focusRow`. Each rendered row derives its `tabindex` from that focus state. The trace has no source map, so it does not prove which expression accounts for the long task. Trace output: `target/tmp/1392-notes-audit.json` and `target/tmp/1392-interaction-trace-notes-move-selection-next-{1..10}.json.gz` in worktree `slowsel-1392`. ## Expected result Moving the Notes Explorer selection takes no more than 150 ms and updates only the old and new rows, as DESIGN §59 requires. ## Suggested direction Keep the highlighted row as an index. Avoid filtering and scanning the full row list for every arrow. Narrow the focus and `tabindex` update to the old and new rows, then add a 1,000-Note regression measurement that fails if the Action exceeds 150 ms.
Author
Owner

Starting #1493 on branch job/notesel-1493, based on origin/dev at cc1af3c12b68ad26c8403adde1be39c096b9ddea.

Initial source inspection confirms NotesExplorer.svelte filters and scans all rendered rows on every tree keydown and derives a shared tab stop from a row scan. I am tracing the production build with the supplied interaction audit, then I will move row lookup off the key path and update only the old and new tree items. Notes open only on Enter/click, per DESIGN §59 and the Notes Explorer tree key model.

Starting #1493 on branch `job/notesel-1493`, based on `origin/dev` at `cc1af3c12b68ad26c8403adde1be39c096b9ddea`. Initial source inspection confirms `NotesExplorer.svelte` filters and scans all rendered rows on every tree keydown and derives a shared tab stop from a row scan. I am tracing the production build with the supplied interaction audit, then I will move row lookup off the key path and update only the old and new tree items. Notes open only on Enter/click, per DESIGN §59 and the Notes Explorer tree key model.
Author
Owner

The source-mapped baseline confirms the full-row update in the selection path. In 20 sequential ArrowDown samples with 1,000 indexed Notes, traced latency was 142.8 ms p50 / 299.0 ms p95 (max 331.5 ms). The top long task was 287 ms. Source maps attribute recurring work to Svelte each.js row reconciliation and attributes.js attribute updates; NotesExplorer.svelte focusRow also appears in the trace. This supports replacing the shared focused-path dependency with per-row keyed focus state and caching row indexes outside keydown.

The source-mapped baseline confirms the full-row update in the selection path. In 20 sequential ArrowDown samples with 1,000 indexed Notes, traced latency was 142.8 ms p50 / 299.0 ms p95 (max 331.5 ms). The top long task was 287 ms. Source maps attribute recurring work to Svelte `each.js` row reconciliation and `attributes.js` attribute updates; `NotesExplorer.svelte` `focusRow` also appears in the trace. This supports replacing the shared focused-path dependency with per-row keyed focus state and caching row indexes outside keydown.
Author
Owner

The first A/B attempt did not produce selection timings: /notes can leave the Notes Explorer in a hidden sidebar, and the screenshot helper found the second treeitem hidden. I updated the audit to open the active sidebar surface before routing or capturing Notes, using the existing #chrome-sidebar-toggle control. The attempt is discarded; I am rerunning with the visible tree asserted.

The first A/B attempt did not produce selection timings: `/notes` can leave the Notes Explorer in a hidden sidebar, and the screenshot helper found the second treeitem hidden. I updated the audit to open the active sidebar surface before routing or capturing Notes, using the existing `#chrome-sidebar-toggle` control. The attempt is discarded; I am rerunning with the visible tree asserted.
Author
Owner

The rerun confirmed that phone width mounts both the hidden desktop Notes tree and the visible sidebar sheet. Playwright was choosing the hidden desktop copy even after the sheet opened. The audit now scopes the Notes row locator to visible treeitems; the previous run is discarded and I am rerunning.

The rerun confirmed that phone width mounts both the hidden desktop Notes tree and the visible sidebar sheet. Playwright was choosing the hidden desktop copy even after the sheet opened. The audit now scopes the Notes row locator to visible treeitems; the previous run is discarded and I am rerunning.
Author
Owner

The next rerun reached screenshot validation but stopped because the app mounts both desktop and sheet Notes Explorer sections, so the generic .notes-explorer visibility assertion matched two elements. The capture now asserts visibility on the first visible treeitem while still capturing the full production viewport. Selection samples from that attempt were not written; I am rerunning the full interleaved profile.

The next rerun reached screenshot validation but stopped because the app mounts both desktop and sheet Notes Explorer sections, so the generic `.notes-explorer` visibility assertion matched two elements. The capture now asserts visibility on the first visible treeitem while still capturing the full production viewport. Selection samples from that attempt were not written; I am rerunning the full interleaved profile.
Author
Owner

The 1,000 Note A/B run completed five interleaved samples per side. Baseline was 207.7/305.0 ms and candidate was 286.8/410.3 ms p50/p95 on this Linux host. The candidate had no observed Long Tasks and each sample changed two row tab stops; frame gaps reached 144.4 ms. The host load average was 15.9 and the audit process tree averaged 1.65 GB RSS. One candidate sample saw two GET /api/v1/notes collection refreshes (the baseline also saw one); these are not Note-open requests. I tightened the audit to keep /notes selected and flag UUID detail/body reads, while retaining the raw request evidence. The local timing is load-limited and misses the 50 ms p95 target; I will report the 5,000 Note run separately.

The 1,000 Note A/B run completed five interleaved samples per side. Baseline was 207.7/305.0 ms and candidate was 286.8/410.3 ms p50/p95 on this Linux host. The candidate had no observed Long Tasks and each sample changed two row tab stops; frame gaps reached 144.4 ms. The host load average was 15.9 and the audit process tree averaged 1.65 GB RSS. One candidate sample saw two `GET /api/v1/notes` collection refreshes (the baseline also saw one); these are not Note-open requests. I tightened the audit to keep `/notes` selected and flag UUID detail/body reads, while retaining the raw request evidence. The local timing is load-limited and misses the 50 ms p95 target; I will report the 5,000 Note run separately.
Author
Owner

The 5,000 Note setup reached 5,000/5,000 in the Notes Index but the overall fixture phase hit its 180,000 ms timeout before the Notes Explorer Files index was confirmed. The old verifier walked up to 50 Notes list pages serially and retried every 250 ms. I changed it to check the static cursor pages with bounded concurrency and to poll index readiness once per second. This keeps setup work bounded and avoids repeated page scans.

The 5,000 Note setup reached 5,000/5,000 in the Notes Index but the overall fixture phase hit its 180,000 ms timeout before the Notes Explorer Files index was confirmed. The old verifier walked up to 50 Notes list pages serially and retried every 250 ms. I changed it to check the static cursor pages with bounded concurrency and to poll index readiness once per second. This keeps setup work bounded and avoids repeated page scans.
Author
Owner

The 5,000 Note fixture did not reach an accepted count before 420,000 ms. The earlier verifier still had to read every Notes page and every Files page, although the Notes Explorer renders a bounded first page. I changed setup verification to check stable first and last Note titles plus the Files Index total and first two real Notes rows. This verifies the full requested fixture count without walking every page on the shared host.

The 5,000 Note fixture did not reach an accepted count before 420,000 ms. The earlier verifier still had to read every Notes page and every Files page, although the Notes Explorer renders a bounded first page. I changed setup verification to check stable first and last Note titles plus the Files Index total and first two real Notes rows. This verifies the full requested fixture count without walking every page on the shared host.
Author
Owner

Finished — #1493

Branch: job/notesel-1493
Base: cc1af3c12b68ad26c8403adde1be39c096b9ddea (origin/dev)
Head: 3b05c0d0abac3c1276d01ca1f7a8f557e5c3c765

Change

Notes Explorer keyboard movement now uses a cached focusable-row list and path-to-index map. The key handler does no filtering or sorting. It updates the tabindex on the old and new treeitems directly, then focuses the new item. Arrow keys move focus only; Enter or click opens a Note. If a refresh removes the focused row, focus returns to the first remaining treeitem without a keyboard ring.

The 20-selection source-map trace on the 1,000-Note origin/dev build showed Svelte set_attributes() and the keyed each block commit() path that calls reconcile(). The 20 selections measured 142.8/299.0 ms p50/p95, with a 331.5 ms maximum.

Interleaved Linux comparison

Five rounds per side, local Linux, 1,000 and 5,000 indexed Notes. p50/p95 are input-to-visible milliseconds.

Indexed Notes origin/dev p50/p95 Candidate p50/p95
1,000 207.7 / 305.0 286.8 / 410.3
5,000 226.6 / 640.9 260.9 / 366.4

Every candidate sample changed exactly two row tab stops. No Note UUID detail/body request was recorded, and the route stayed at /notes; two collection refresh requests appeared during candidate samples. The host load averages were 15.66/18.05/16.36 at the 1,000-Note run and 13.38/18.42/16.84 at the 5,000-Note run (12 CPUs). The largest candidate frame gap was 190.6 ms.

The 5,000-Note p95 improved, but both candidate p95 results remain above the 150 ms action budget and the 50 ms target. The 1,000-Note p50/p95 regressed in this host-loaded run. The audit capture listener measured about 0.1 ms; that does not isolate the app onKeydown duration. Each sample still observed exactly two tab-stop mutations, while scheduling/frame gaps dominated the measured input-to-visible time. Performance target remains a known gap.

UX gaps closed

  • ArrowDown no longer opens Notes while moving focus. Enter and click still open the focused Note.
  • Treeitem focus and the roving tab stop stay aligned; the audit observed two row tab-stop mutations per selection.
  • The attached pointer captures show no keyboard focus ring after click; ArrowDown captures show the keyboard ring.
  • Actual app captures use the production build and macOS platform emulation at 390, 820 and 1440 px in light and dark themes.

UX gap left: no manual VoiceOver session was run. The treeitem names, roles and state remain exposed through the existing ARIA attributes, and browser keyboard focus was exercised by the audit.

Screenshots

Each screenshot is attached to this issue at its captured size.

Files

  • apps/web/src/lib/notes/NotesExplorer.svelte
  • bench/interaction-audit-1493.mjs
  • contracts/perf/adoption-1058.json and contracts/perf/exceptions.json (refreshed exact hashes; limits and counts are unchanged)

Gate output

bun run check emitted:

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

Focused tests: bunx vitest run src/lib/a11y/inputModality.test.ts src/lib/notes/noteIndex.test.ts --maxWorkers=2

 RUN  v5.0.1 /home/kayg/Developer/calternal-wt/notesel-1493/apps/web

 Test Files  2 passed (2)
      Tests  13 passed (13)
   Start at  03:40:14
   Duration  8.59s (transform 81%, environment 10%, import 8%, tests 1%)

Rust was unchanged. cargo clean output: Removed 1 file, 356B total. The production web build and .svelte-kit/output were removed after capture.

For the merge round

Per the shared verification policy, the full web suite was not run in this job. Run cd apps/web && bun run test -- --maxWorkers=2 on the combined branch and confirm the full Vitest, perf-source and design-rules suites pass.

Decision

DESIGN §9/§59 defines arrows as focus movement and Enter/click as open. The document does not specify focus recovery when a live refresh removes the focused Note; this change moves focus to the first remaining treeitem and suppresses a programmatic focus ring.

## Finished — #1493 Branch: `job/notesel-1493` Base: `cc1af3c12b68ad26c8403adde1be39c096b9ddea` (`origin/dev`) Head: `3b05c0d0abac3c1276d01ca1f7a8f557e5c3c765` ### Change Notes Explorer keyboard movement now uses a cached focusable-row list and path-to-index map. The key handler does no filtering or sorting. It updates the `tabindex` on the old and new treeitems directly, then focuses the new item. Arrow keys move focus only; Enter or click opens a Note. If a refresh removes the focused row, focus returns to the first remaining treeitem without a keyboard ring. The 20-selection source-map trace on the 1,000-Note `origin/dev` build showed Svelte `set_attributes()` and the keyed each block `commit()` path that calls `reconcile()`. The 20 selections measured 142.8/299.0 ms p50/p95, with a 331.5 ms maximum. ### Interleaved Linux comparison Five rounds per side, local Linux, 1,000 and 5,000 indexed Notes. `p50/p95` are input-to-visible milliseconds. | Indexed Notes | `origin/dev` p50/p95 | Candidate p50/p95 | | --- | ---: | ---: | | 1,000 | 207.7 / 305.0 | 286.8 / 410.3 | | 5,000 | 226.6 / 640.9 | 260.9 / 366.4 | Every candidate sample changed exactly two row tab stops. No Note UUID detail/body request was recorded, and the route stayed at `/notes`; two collection refresh requests appeared during candidate samples. The host load averages were 15.66/18.05/16.36 at the 1,000-Note run and 13.38/18.42/16.84 at the 5,000-Note run (12 CPUs). The largest candidate frame gap was 190.6 ms. The 5,000-Note p95 improved, but both candidate p95 results remain above the 150 ms action budget and the 50 ms target. The 1,000-Note p50/p95 regressed in this host-loaded run. The audit capture listener measured about 0.1 ms; that does not isolate the app onKeydown duration. Each sample still observed exactly two tab-stop mutations, while scheduling/frame gaps dominated the measured input-to-visible time. Performance target remains a known gap. ### UX gaps closed - ArrowDown no longer opens Notes while moving focus. Enter and click still open the focused Note. - Treeitem focus and the roving tab stop stay aligned; the audit observed two row tab-stop mutations per selection. - The attached pointer captures show no keyboard focus ring after click; ArrowDown captures show the keyboard ring. - Actual app captures use the production build and macOS platform emulation at 390, 820 and 1440 px in light and dark themes. UX gap left: no manual VoiceOver session was run. The treeitem names, roles and state remain exposed through the existing ARIA attributes, and browser keyboard focus was exercised by the audit. ### Screenshots Each screenshot is attached to this issue at its captured size. - 390 px, light: [after pointer click](https://git.kayg.org/attachments/f5b1d19f-bd0c-44f3-8e18-44e93cf3aada) · [after ArrowDown](https://git.kayg.org/attachments/2699d0c2-0064-41e9-a25e-375a3d4ed955) - 820 px, light: [after pointer click](https://git.kayg.org/attachments/d102be85-c775-4ed2-a728-9e46cb850cab) · [after ArrowDown](https://git.kayg.org/attachments/0e304ac8-edb6-4f10-b1a2-55fcfd950c9b) - 1440 px, light: [after pointer click](https://git.kayg.org/attachments/186d09c4-a034-4033-abba-5bdd11df3150) · [after ArrowDown](https://git.kayg.org/attachments/31311330-7cfb-4c1d-9c3c-997ee7d106fe) - 390 px, dark: [after pointer click](https://git.kayg.org/attachments/f95f7530-c0cf-4679-ba03-0c5d27deb1ca) · [after ArrowDown](https://git.kayg.org/attachments/8eb522f9-19c1-4728-9397-b6e7eb3c882a) - 820 px, dark: [after pointer click](https://git.kayg.org/attachments/1d9ed7ca-bb54-42ff-ad9e-d458ea93725d) · [after ArrowDown](https://git.kayg.org/attachments/2122f56c-e1ca-4edb-a0a7-5fd83522df88) - 1440 px, dark: [after pointer click](https://git.kayg.org/attachments/499458bc-3a22-4b5a-aad2-b884bc4c9269) · [after ArrowDown](https://git.kayg.org/attachments/b8319c85-5b04-4756-9391-3dfd41105b02) ### Files - `apps/web/src/lib/notes/NotesExplorer.svelte` - `bench/interaction-audit-1493.mjs` - `contracts/perf/adoption-1058.json` and `contracts/perf/exceptions.json` (refreshed exact hashes; limits and counts are unchanged) ### Gate output `bun run check` emitted: ```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. 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 ``` Focused tests: `bunx vitest run src/lib/a11y/inputModality.test.ts src/lib/notes/noteIndex.test.ts --maxWorkers=2` ```text RUN v5.0.1 /home/kayg/Developer/calternal-wt/notesel-1493/apps/web Test Files 2 passed (2) Tests 13 passed (13) Start at 03:40:14 Duration 8.59s (transform 81%, environment 10%, import 8%, tests 1%) ``` Rust was unchanged. `cargo clean` output: `Removed 1 file, 356B total`. The production web build and `.svelte-kit/output` were removed after capture. ### For the merge round Per the shared verification policy, the full web suite was not run in this job. Run `cd apps/web && bun run test -- --maxWorkers=2` on the combined branch and confirm the full Vitest, perf-source and design-rules suites pass. ### Decision DESIGN §9/§59 defines arrows as focus movement and Enter/click as open. The document does not specify focus recovery when a live refresh removes the focused Note; this change moves focus to the first remaining treeitem and suppresses a programmatic focus ring.
Author
Owner

Started #1493 on job/notesel-1493, worktree /home/kayg/Developer/calternal-wt/notesel-1493. Branch head is 3b05c0d0abac3c1276d01ca1f7a8f557e5c3c765; base origin/dev is cc1af3c12b68ad26c8403adde1be39c096b9ddea.

Started #1493 on `job/notesel-1493`, worktree `/home/kayg/Developer/calternal-wt/notesel-1493`. Branch head is `3b05c0d0abac3c1276d01ca1f7a8f557e5c3c765`; base `origin/dev` is `cc1af3c12b68ad26c8403adde1be39c096b9ddea`.
Author
Owner

Benchmark finding: the AUDIT_NOTE_COUNT=1000 run captured all 12 pointer/ArrowDown screenshots and completed the A/B interactions, but its JSON report did not flush because Bun propagated rg exit 1 for an empty await … animation search. A Node rerun then hit waitForNoteIndexReadsToSettle()'s hard-coded 30 s timeout while a Notes Index request was still active. The audit's fixture timeout is 180 s, so the Notes settle wait had a shorter unrelated limit. I am aligning that wait with the existing fixture timeout and will rerun the required interleaved measurements.

Benchmark finding: the `AUDIT_NOTE_COUNT=1000` run captured all 12 pointer/ArrowDown screenshots and completed the A/B interactions, but its JSON report did not flush because Bun propagated `rg` exit 1 for an empty `await … animation` search. A Node rerun then hit `waitForNoteIndexReadsToSettle()`'s hard-coded 30 s timeout while a Notes Index request was still active. The audit's fixture timeout is 180 s, so the Notes settle wait had a shorter unrelated limit. I am aligning that wait with the existing fixture timeout and will rerun the required interleaved measurements.
Author
Owner

Correction to my previous benchmark note: the audit was launched from apps/web. Its final motionScan() uses the repo-root-relative path apps/web/src, so rg failed because the working directory was wrong; this was not Bun's handling of an empty match. The Node rerun from the same directory stopped earlier because Note Index reads remained active past the helper's fixed 30 s wait. I extended that wait to use the audit's existing 180 s fixture bound. I will run the audit from the repository root.

Correction to my previous benchmark note: the audit was launched from `apps/web`. Its final `motionScan()` uses the repo-root-relative path `apps/web/src`, so `rg` failed because the working directory was wrong; this was not Bun's handling of an empty match. The Node rerun from the same directory stopped earlier because Note Index reads remained active past the helper's fixed 30 s wait. I extended that wait to use the audit's existing 180 s fixture bound. I will run the audit from the repository root.
Author
Owner

Found the shared invalidation source. Svelte 5.57.1 SvelteMap.get() for a missing path subscribes that row to the map-wide version, and inserting a previously absent path increments that version. During focus movement, this woke rows beyond the old and new focus rows. I changed the focus lookup to a plain path Map of private reactive row state. The 1,000-Note component test passes and observes DOM writes on exactly two treeitems.

The final 5-round interleaved Linux comparisons still fail the requested accept rule:

Notes origin/dev p50 / p95 Candidate p50 / p95 Load average at start → end
1,000 240.6 / 377.1 ms 272.4 / 485.6 ms 16.84 / 20.16 / 17.09 → 22.71 / 21.23 / 17.63
5,000 161.5 / 284.0 ms 189.4 / 466.5 ms 6.02 / 14.69 / 15.88 → 8.06 / 13.53 / 15.40

The candidate recorded two tab-stop mutations in every sample. It started no /api/v1/notes requests during the 5,000-Note samples. The latency decision remains REJECT because candidate p50 and p95 are not lower at either size. Current head: 1f271aab4f2a41e8328c470e3e145670a3542cce.

Found the shared invalidation source. Svelte 5.57.1 `SvelteMap.get()` for a missing path subscribes that row to the map-wide version, and inserting a previously absent path increments that version. During focus movement, this woke rows beyond the old and new focus rows. I changed the focus lookup to a plain path Map of private reactive row state. The 1,000-Note component test passes and observes DOM writes on exactly two treeitems. The final 5-round interleaved Linux comparisons still fail the requested accept rule: | Notes | origin/dev p50 / p95 | Candidate p50 / p95 | Load average at start → end | | --- | ---: | ---: | --- | | 1,000 | 240.6 / 377.1 ms | 272.4 / 485.6 ms | 16.84 / 20.16 / 17.09 → 22.71 / 21.23 / 17.63 | | 5,000 | 161.5 / 284.0 ms | 189.4 / 466.5 ms | 6.02 / 14.69 / 15.88 → 8.06 / 13.53 / 15.40 | The candidate recorded two tab-stop mutations in every sample. It started no `/api/v1/notes` requests during the 5,000-Note samples. The latency decision remains REJECT because candidate p50 and p95 are not lower at either size. Current head: `1f271aab4f2a41e8328c470e3e145670a3542cce`.
Author
Owner

#1493 final report

Decision: REJECT for performance acceptance. Head: 5a62ff4ca571c2fa4e97de311f32639bf2eef0f1 on job/notesel-1493. The interaction test and web gates pass, but the required rule needs both p50 and p95 to beat origin/dev at both sizes. The 5,000-Note run fails that rule; do not treat this branch as accepted.

What changed

  • Notes Explorer focus movement uses a non-reactive path-to-tab-stop map and cached row lookup. It updates the previous and next treeitems directly, so focus movement does not invalidate the keyed rows list.
  • The 1,000-Note regression test instruments treeitem attribute writes and confirms ArrowDown changes only the previous and next treeitems. It also checks roving tabindex and preserves the active Note's aria-selected and aria-current state.
  • The interaction audit waits for Notes identity reads to settle before measuring. The matching NotesExplorer performance pins were updated in the two perf contract files.
  • No visible controls were added; the existing Notes treeitems and shared row actions remain in use.

Interleaved Linux comparison

Five interleaved samples per side; p50 and p95 are event-to-visible latency in ms.

Notes origin/dev p50 / p95 Candidate p50 / p95 Result
1,000 278.3 / 371.3 208.7 / 332.8 Better at both percentiles
5,000 143.2 / 201.9 258.3 / 482.0 Rejected; slower at both percentiles

Load average at start / after: 1,000 Notes [10.7, 9.6, 12.78] / [9.4, 9.59, 12.57]; 5,000 Notes [7.43, 9.1, 12.32] / [10, 9.66, 12.25].

The 5,000-Note candidate had pending background /api/v1/files/entries reads at the visible mark in four of five samples; baseline had none. A trial that waited for all Files reads timed out at the 180-second fixture limit, so this was not used as a readiness condition. This may affect comparison conditions, but it does not satisfy the required acceptance rule.

Files

  • apps/web/src/lib/notes/NotesExplorer.svelte
  • apps/web/src/lib/notes/NotesExplorer.svelte.test.ts
  • bench/interaction-audit-1493.mjs
  • contracts/perf/adoption-1058.json
  • contracts/perf/exceptions.json

Gates

bun run check — exit=0:

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
exit=0

The four Svelte warnings are unused CSS selectors in MailView and the Money route; none are in changed files.

bun run test -- --maxWorkers=2 — exit=0:

 Test Files  302 passed (302)
      Tests  2170 passed (2170)
   Start at  07:36:45
   Duration  241.82s (environment 28%, import 24%, transform 23%, tests 19%, setup 6%)

No Rust source changed, so Rust crate gates were not run. cargo clean completed; apps/web/build was removed.

UX gaps

  • Closed: Arrow-key focus keeps the same roving tabindex and active Note screen-reader state; the selection test confirms two treeitem nodes are affected at 1,000 rendered Notes. Production screenshots cover pointer and ArrowDown focus in light and dark at 390, 820 and 1440 px.
  • Left: the 5,000-Note latency threshold is not met. The candidate also had background Files-index requests at several measurement marks.

Decisions not specified in DESIGN

Used a plain Map and direct tabindex writes to keep selection changes outside Svelte's keyed-list reconciliation. The audit treats 600 ms of Notes identity-read inactivity as settled, covering the existing 400 ms coalesced Notes refresh. The perf comparison remains rejected even with that readiness rule.

Screenshots

All captures use the production web build with macOS platform emulation. Each pair shows pointer focus and ArrowDown focus.

## #1493 final report **Decision: REJECT for performance acceptance.** Head: `5a62ff4ca571c2fa4e97de311f32639bf2eef0f1` on `job/notesel-1493`. The interaction test and web gates pass, but the required rule needs both p50 and p95 to beat `origin/dev` at both sizes. The 5,000-Note run fails that rule; do not treat this branch as accepted. ### What changed - Notes Explorer focus movement uses a non-reactive path-to-tab-stop map and cached row lookup. It updates the previous and next treeitems directly, so focus movement does not invalidate the keyed rows list. - The 1,000-Note regression test instruments treeitem attribute writes and confirms ArrowDown changes only the previous and next treeitems. It also checks roving tabindex and preserves the active Note's `aria-selected` and `aria-current` state. - The interaction audit waits for Notes identity reads to settle before measuring. The matching NotesExplorer performance pins were updated in the two perf contract files. - No visible controls were added; the existing Notes treeitems and shared row actions remain in use. ### Interleaved Linux comparison Five interleaved samples per side; p50 and p95 are event-to-visible latency in ms. | Notes | origin/dev p50 / p95 | Candidate p50 / p95 | Result | | ---: | ---: | ---: | --- | | 1,000 | 278.3 / 371.3 | 208.7 / 332.8 | Better at both percentiles | | 5,000 | 143.2 / 201.9 | 258.3 / 482.0 | Rejected; slower at both percentiles | Load average at start / after: 1,000 Notes `[10.7, 9.6, 12.78]` / `[9.4, 9.59, 12.57]`; 5,000 Notes `[7.43, 9.1, 12.32]` / `[10, 9.66, 12.25]`. The 5,000-Note candidate had pending background `/api/v1/files/entries` reads at the visible mark in four of five samples; baseline had none. A trial that waited for all Files reads timed out at the 180-second fixture limit, so this was not used as a readiness condition. This may affect comparison conditions, but it does not satisfy the required acceptance rule. ### Files - `apps/web/src/lib/notes/NotesExplorer.svelte` - `apps/web/src/lib/notes/NotesExplorer.svelte.test.ts` - `bench/interaction-audit-1493.mjs` - `contracts/perf/adoption-1058.json` - `contracts/perf/exceptions.json` ### Gates `bun run check` — `exit=0`: ``` 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 exit=0 ``` The four Svelte warnings are unused CSS selectors in MailView and the Money route; none are in changed files. `bun run test -- --maxWorkers=2` — `exit=0`: ``` Test Files 302 passed (302) Tests 2170 passed (2170) Start at 07:36:45 Duration 241.82s (environment 28%, import 24%, transform 23%, tests 19%, setup 6%) ``` No Rust source changed, so Rust crate gates were not run. `cargo clean` completed; `apps/web/build` was removed. ### UX gaps - **Closed:** Arrow-key focus keeps the same roving tabindex and active Note screen-reader state; the selection test confirms two treeitem nodes are affected at 1,000 rendered Notes. Production screenshots cover pointer and ArrowDown focus in light and dark at 390, 820 and 1440 px. - **Left:** the 5,000-Note latency threshold is not met. The candidate also had background Files-index requests at several measurement marks. ### Decisions not specified in DESIGN Used a plain Map and direct tabindex writes to keep selection changes outside Svelte's keyed-list reconciliation. The audit treats 600 ms of Notes identity-read inactivity as settled, covering the existing 400 ms coalesced Notes refresh. The perf comparison remains rejected even with that readiness rule. ### Screenshots All captures use the production web build with macOS platform emulation. Each pair shows pointer focus and ArrowDown focus. - **Light:** [390 pointer](https://git.kayg.org/attachments/1025b0d7-f450-43bb-9f30-5da460c8f92d) · [390 ArrowDown](https://git.kayg.org/attachments/3b17b715-53fc-4926-8d18-63db95da6c2c) · [820 pointer](https://git.kayg.org/attachments/9cb0b79f-d43d-447f-add8-33c5a3ebe2af) · [820 ArrowDown](https://git.kayg.org/attachments/db8998a2-f309-4487-8141-92f640ca0561) · [1440 pointer](https://git.kayg.org/attachments/d139285b-0c4f-4f6e-8c15-a0e28680c73b) · [1440 ArrowDown](https://git.kayg.org/attachments/3b17b715-53fc-4926-8d18-63db95da6c2c) - **Dark:** [390 pointer](https://git.kayg.org/attachments/65c4bfb3-db8c-446f-8285-b7a3ac27ee40) · [390 ArrowDown](https://git.kayg.org/attachments/9d194a1d-2cd1-42fd-9eb6-960b6a62d64f) · [820 pointer](https://git.kayg.org/attachments/6b325b83-2497-442c-88c3-fcf6720d14a4) · [820 ArrowDown](https://git.kayg.org/attachments/be7910ea-d087-4513-8e04-2328c54663f7) · [1440 pointer](https://git.kayg.org/attachments/c348a8ad-a8f3-4488-adfd-9897536d70ba) · [1440 ArrowDown](https://git.kayg.org/attachments/215ed503-61bd-47fb-be14-768816aea095)
Author
Owner

Correction to the screenshot list in my final report: the Light 1440 ArrowDown capture is https://git.kayg.org/attachments/fc68f6c5-99bd-4170-bca1-806c54ade94a. The link shown beside Light 1440 ArrowDown in the report duplicated the Light 390 ArrowDown capture.

Correction to the screenshot list in my final report: the **Light 1440 ArrowDown** capture is https://git.kayg.org/attachments/fc68f6c5-99bd-4170-bca1-806c54ade94a. The link shown beside Light 1440 ArrowDown in the report duplicated the Light 390 ArrowDown capture.
Author
Owner

Starting #1493 on job/notesel-1493 at 5a62ff4ca571c2fa4e97de311f32639bf2eef0f1; current origin/dev is 9dc65437e86c74efe92d723f9a99774246e60650. I will compare Files-index request count and initiator stacks per ArrowDown at 5,000 Notes on both sides, then run the requested interleaved measurements with idle-index gating and apply the acceptance rule.

Starting #1493 on `job/notesel-1493` at `5a62ff4ca571c2fa4e97de311f32639bf2eef0f1`; current `origin/dev` is `9dc65437e86c74efe92d723f9a99774246e60650`. I will compare Files-index request count and initiator stacks per ArrowDown at 5,000 Notes on both sides, then run the requested interleaved measurements with idle-index gating and apply the acceptance rule.
Author
Owner

Finding: after merging origin/dev (9dc65437e), the 5,000-Note fixture did not reach interaction measurement. The first completed attempt reported Notes Index readiness timed out after 180000 ms: 0/5000 fixture Notes visible (HTTP 200). A retry with a 300,000 ms fixture limit still had a pending API read after the timeout and had to be stopped; it produced no report or accepted sample. I removed its disposable fixture. The perf VM was reserved by another job, so these were local Linux attempts under concurrent Rust builds. I will keep the failure out of the performance sample set, fix the audit's bounded cleanup, and complete the 1,000-Note run. The 5,000-Note request-cause and performance cells remain unverified unless a fixture reaches readiness.

Finding: after merging `origin/dev` (`9dc65437e`), the 5,000-Note fixture did not reach interaction measurement. The first completed attempt reported `Notes Index readiness timed out after 180000 ms: 0/5000 fixture Notes visible (HTTP 200)`. A retry with a 300,000 ms fixture limit still had a pending API read after the timeout and had to be stopped; it produced no report or accepted sample. I removed its disposable fixture. The perf VM was reserved by another job, so these were local Linux attempts under concurrent Rust builds. I will keep the failure out of the performance sample set, fix the audit's bounded cleanup, and complete the 1,000-Note run. The 5,000-Note request-cause and performance cells remain unverified unless a fixture reaches readiness.
Author
Owner

Finding: the final-rule 1,000-Note run also failed before measurement. Its fixture was still pending after the 180-second limit; at 223 seconds the harness and server were still active. I stopped only that audit process and confirmed its server and temporary fixture directories were gone. It yielded zero valid interaction samples. The retry cannot establish the requested p50 or per-arrow request counts.

Finding: the final-rule 1,000-Note run also failed before measurement. Its fixture was still pending after the 180-second limit; at 223 seconds the harness and server were still active. I stopped only that audit process and confirmed its server and temporary fixture directories were gone. It yielded zero valid interaction samples. The retry cannot establish the requested p50 or per-arrow request counts.
Author
Owner

Final report — Forgejo #1493

Decision: REJECT. The acceptance rule is not met because no valid interleaved samples or per-ArrowDown request counts were captured. This is an evidence failure; it does not establish whether the candidate is slower or causes the Files-index reads.

Head: 67b3f8a55b90922a7eec4078757276c3dc32532d (job/notesel-1493). Merged origin/dev at 9dc65437e before final gates.

Changes

  • bench/interaction-audit-1493.mjs now captures baseline and candidate requests per page, with method, query-free URL path, bounded query-free CDP initiator stack, and pending state.
  • The Notes selection benchmark waits for GET /api/v1/notes and GET /api/v1/files/entries to be quiet for 2,000 ms before each ArrowDown and records requests through the next quiet boundary. The per-round comparison rejects candidate-only requests and applies the requested 5% p50 threshold.
  • Fixture failure output is saved as an incomplete report. Browser, context and server teardown waits are bounded so a stuck index read cannot hold the audit indefinitely.

Measurement table (local Linux)

Notes Target samples per side Valid baseline / candidate samples p50 / p95 Requests per ArrowDown Result
1,000 7 0 / 0 unavailable unavailable Fixture setup remained pending past 223 s; stopped without a report
5,000 7 0 / 0 unavailable unavailable First attempt indexed 0/5,000 Notes and 0/5,000 Files after 180,000 ms. Retry remained pending past 416 s and produced no report

The shared perf VM was occupied. The required candidate-versus-origin/dev request-cause comparison is unresolved. No claim is made about whether ArrowDown causes a Files-index read.

Gates

bun run check; echo exit=$? — final output excerpt, verbatim:

PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages
perf-lint: PASS; 0 violations; 22254 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, 136 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 3 warnings in 1 file
exit=0

bun run test -- --maxWorkers=2 — final output excerpt, verbatim:

 Test Files  306 passed (306)
      Tests  2210 passed (2210)
   Start at  08:25:47
   Duration  272.58s (environment 27%, transform 24%, import 22%, tests 21%, setup 5%)

Environment  |component| jsdom was created 128 times · 109.80s total, 30% of tracked time
             create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files)
             learn more: https://vitest.dev/guide/improving-performance#test-environments

exit=0

bun run check reported three existing unused CSS selector warnings in MailMorphCard.svelte. Vitest emitted jsdom scrollTo, navigation, and CSS parser notices. Both commands exited 0.

Decisions and gaps

  • The 2,000 ms quiet period follows the task instruction and is configurable through AUDIT_NOTE_INDEX_IDLE_MS.
  • Request comparison uses method, redacted path and multiplicity, paired by interleaved round. This makes “no extra requests” measurable without retaining query strings.
  • No design decision was needed. The two requested fixture sizes, seven rounds and 5% threshold were used.
  • The benchmark evidence remains incomplete. The fixture-index setup must complete before the request-cause and latency decision can be made.
  • No UI or API behavior changed, so UI screenshots and API adversarial tests were not applicable.
# Final report — Forgejo #1493 **Decision: REJECT.** The acceptance rule is not met because no valid interleaved samples or per-ArrowDown request counts were captured. This is an evidence failure; it does not establish whether the candidate is slower or causes the Files-index reads. Head: `67b3f8a55b90922a7eec4078757276c3dc32532d` (`job/notesel-1493`). Merged `origin/dev` at `9dc65437e` before final gates. ## Changes - `bench/interaction-audit-1493.mjs` now captures baseline and candidate requests per page, with method, query-free URL path, bounded query-free CDP initiator stack, and pending state. - The Notes selection benchmark waits for GET `/api/v1/notes` and GET `/api/v1/files/entries` to be quiet for 2,000 ms before each ArrowDown and records requests through the next quiet boundary. The per-round comparison rejects candidate-only requests and applies the requested 5% p50 threshold. - Fixture failure output is saved as an incomplete report. Browser, context and server teardown waits are bounded so a stuck index read cannot hold the audit indefinitely. ## Measurement table (local Linux) | Notes | Target samples per side | Valid baseline / candidate samples | p50 / p95 | Requests per ArrowDown | Result | |---:|---:|---:|---|---|---| | 1,000 | 7 | 0 / 0 | unavailable | unavailable | Fixture setup remained pending past 223 s; stopped without a report | | 5,000 | 7 | 0 / 0 | unavailable | unavailable | First attempt indexed 0/5,000 Notes and 0/5,000 Files after 180,000 ms. Retry remained pending past 416 s and produced no report | The shared perf VM was occupied. The required candidate-versus-`origin/dev` request-cause comparison is unresolved. No claim is made about whether ArrowDown causes a Files-index read. ## Gates `bun run check; echo exit=$?` — final output excerpt, verbatim: ```text PASS production dependency licences: 759 locked package releases across apps/web, apps/docs and packages perf-lint: PASS; 0 violations; 22254 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, 136 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 3 warnings in 1 file exit=0 ``` `bun run test -- --maxWorkers=2` — final output excerpt, verbatim: ```text Test Files 306 passed (306) Tests 2210 passed (2210) Start at 08:25:47 Duration 272.58s (environment 27%, transform 24%, import 22%, tests 21%, setup 5%) Environment |component| jsdom was created 128 times · 109.80s total, 30% of tracked time create it once per worker with pool: 'vmThreads' (keeps per-file isolation) or isolate: false (shares it across files) learn more: https://vitest.dev/guide/improving-performance#test-environments exit=0 ``` `bun run check` reported three existing unused CSS selector warnings in `MailMorphCard.svelte`. Vitest emitted jsdom `scrollTo`, navigation, and CSS parser notices. Both commands exited 0. ## Decisions and gaps - The 2,000 ms quiet period follows the task instruction and is configurable through `AUDIT_NOTE_INDEX_IDLE_MS`. - Request comparison uses method, redacted path and multiplicity, paired by interleaved round. This makes “no extra requests” measurable without retaining query strings. - No design decision was needed. The two requested fixture sizes, seven rounds and 5% threshold were used. - The benchmark evidence remains incomplete. The fixture-index setup must complete before the request-cause and latency decision can be made. - No UI or API behavior changed, so UI screenshots and API adversarial tests were not applicable.
Author
Owner

Orchestrator: parking this after three measurement attempts. The candidate (67b3f8a55) is structurally right (tested: ArrowDown touches exactly 2 treeitems), but the Linux harness on the loaded build host gives unstable baselines (1,000 Notes dev p50 208 vs 278 ms between runs) and fixtures that sit at 'indexed 0/N' for minutes. Next step when the host is quiet (or on the perf VM): rerun bench/interaction-audit-1493.mjs with the index-idle wait, decide by the 5% rule. Not merging until then.

Orchestrator: parking this after three measurement attempts. The candidate (67b3f8a55) is structurally right (tested: ArrowDown touches exactly 2 treeitems), but the Linux harness on the loaded build host gives unstable baselines (1,000 Notes dev p50 208 vs 278 ms between runs) and fixtures that sit at 'indexed 0/N' for minutes. Next step when the host is quiet (or on the perf VM): rerun bench/interaction-audit-1493.mjs with the index-idle wait, decide by the 5% rule. Not merging until then.
Author
Owner

Starting the #1493 measurement-only run.

Branch: job/notesel-1493. Candidate HEAD: 67b3f8a55b90922a7eec4078757276c3dc32532d. Branch base: origin/dev at 9dc65437e86c74efe92d723f9a99774246e60650.

The previous run documented fixture indexing at 0/N beyond its deadline (1,000 Notes pending 223 s; 5,000 Notes and Files both 0/5,000 after 180 s, then a retry pending 416 s), so it produced no valid samples. I will first verify setup and collect server/index-job state if setup again stays at 0/N for 60 s.

Starting the #1493 measurement-only run. Branch: `job/notesel-1493`. Candidate HEAD: `67b3f8a55b90922a7eec4078757276c3dc32532d`. Branch base: `origin/dev` at `9dc65437e86c74efe92d723f9a99774246e60650`. The previous run documented fixture indexing at 0/N beyond its deadline (1,000 Notes pending 223 s; 5,000 Notes and Files both 0/5,000 after 180 s, then a retry pending 416 s), so it produced no valid samples. I will first verify setup and collect server/index-job state if setup again stays at 0/N for 60 s.
Author
Owner

Progress update for the measurement run:

  • At 1,000 Notes, the fixture indexed fully (note_items=1000, Files Index rows for the fixture=1000). Seven interleaved samples per side completed. Origin/dev was 188.3 ms p50 / 368.4 ms p95; candidate was 193.2 ms p50 / 270.7 ms p95. The candidate p50 is 2.6% higher, within the 5% limit. All seven paired request comparisons had no candidate-only request.
  • The 5,000-Note run is active under perf-run. A read-only Index snapshot shows note_items=5000 and files_index=5005, so the fixture is not at 0/N. At that snapshot, files.thumbnail had 511 completed jobs, one leased job, and 932 pending jobs. The interaction samples are still running.
  • The 5,000-Note run recorded an in-lock load average of 1m 7.50 / 5m 7.10 / 15m 5.44.
Progress update for the measurement run: - At 1,000 Notes, the fixture indexed fully (`note_items=1000`, Files Index rows for the fixture=1000). Seven interleaved samples per side completed. Origin/dev was 188.3 ms p50 / 368.4 ms p95; candidate was 193.2 ms p50 / 270.7 ms p95. The candidate p50 is 2.6% higher, within the 5% limit. All seven paired request comparisons had no candidate-only request. - The 5,000-Note run is active under `perf-run`. A read-only Index snapshot shows `note_items=5000` and `files_index=5005`, so the fixture is not at 0/N. At that snapshot, `files.thumbnail` had 511 completed jobs, one leased job, and 932 pending jobs. The interaction samples are still running. - The 5,000-Note run recorded an in-lock load average of 1m 7.50 / 5m 7.10 / 15m 5.44.
Author
Owner

Measurement result

Decision: ACCEPT under the requested comparative rule. Candidate p50 is less than 5% slower than origin/dev at both sizes, and the candidate made no extra requests per ArrowDown in any paired sample.

Notes origin/dev event-to-visible p50 / p95 Candidate p50 / p95 origin/dev requests per ArrowDown, median / max Candidate requests per ArrowDown, median / max Candidate-only extra requests
1,000 188.3 / 368.4 ms 193.2 / 270.7 ms (+2.6% p50) 0 / 191 0 / 6 0 of 7 pairs
5,000 194.0 / 314.7 ms 199.4 / 248.0 ms (+2.8% p50) 0 / 0 0 / 0 0 of 7 pairs

Each size has seven interleaved samples per side. The 1,000-Note Index and Files Index both reached 1,000 rows; fixture indexing took 8.4 s. The 5,000-Note Index and Files Index both reached 5,000 fixture rows; fixture indexing took 53.7 s. No index remained at 0/N for 60 seconds, so the server-log snapshot trigger did not fire.

The first 5,000-Note baseline pass then waited 698.6 s for Notes/Files index reads to become idle. A read-only Index snapshot during that phase showed 5,000 note_items and 5,005 files_index rows, with files.thumbnail jobs: 511 completed, 1 leased and 932 pending. A later snapshot showed 1,093 completed, 1 leased and 350 pending. The fixture was indexed; the long wait was in the initial Notes/Files read-idle phase.

The perf VM load average inside the lock was 1m 1.68 / 5m 0.95 / 15m 1.53 for the 1,000-Note run and 1m 7.50 / 5m 7.10 / 15m 5.44 for the 5,000-Note run. The high 5,000-Note load is a limitation when comparing absolute timings. Candidate p50 was still above the 150 ms DESIGN §59 Action budget at both sizes (193.2 ms and 199.4 ms); this ACCEPT reflects the requested candidate-versus-origin/dev rule.

Revisions: baseline server origin/dev 9dc65437e86c74efe92d723f9a99774246e60650 (SHA-256 115b465fff9fd470a66a78872fa4d294b19cf8d5c4c7de385610ee9e83e239cd); candidate 67b3f8a55b90922a7eec4078757276c3dc32532d.

Files: no tracked files changed. Raw reports: target/tmp/1493-notes-1000.json and target/tmp/1493-notes-5000.json in the worktree.

Gates: no Rust or web test gates were run because this job changed no product code. cargo clean output: Removed 1 file, 356B total.

## Measurement result **Decision: ACCEPT** under the requested comparative rule. Candidate p50 is less than 5% slower than `origin/dev` at both sizes, and the candidate made no extra requests per ArrowDown in any paired sample. | Notes | origin/dev event-to-visible p50 / p95 | Candidate p50 / p95 | origin/dev requests per ArrowDown, median / max | Candidate requests per ArrowDown, median / max | Candidate-only extra requests | |---:|---:|---:|---:|---:|---:| | 1,000 | 188.3 / 368.4 ms | 193.2 / 270.7 ms (+2.6% p50) | 0 / 191 | 0 / 6 | 0 of 7 pairs | | 5,000 | 194.0 / 314.7 ms | 199.4 / 248.0 ms (+2.8% p50) | 0 / 0 | 0 / 0 | 0 of 7 pairs | Each size has seven interleaved samples per side. The 1,000-Note Index and Files Index both reached 1,000 rows; fixture indexing took 8.4 s. The 5,000-Note Index and Files Index both reached 5,000 fixture rows; fixture indexing took 53.7 s. No index remained at 0/N for 60 seconds, so the server-log snapshot trigger did not fire. The first 5,000-Note baseline pass then waited 698.6 s for Notes/Files index reads to become idle. A read-only Index snapshot during that phase showed 5,000 `note_items` and 5,005 `files_index` rows, with `files.thumbnail` jobs: 511 completed, 1 leased and 932 pending. A later snapshot showed 1,093 completed, 1 leased and 350 pending. The fixture was indexed; the long wait was in the initial Notes/Files read-idle phase. The perf VM load average inside the lock was 1m 1.68 / 5m 0.95 / 15m 1.53 for the 1,000-Note run and 1m 7.50 / 5m 7.10 / 15m 5.44 for the 5,000-Note run. The high 5,000-Note load is a limitation when comparing absolute timings. Candidate p50 was still above the 150 ms DESIGN §59 Action budget at both sizes (193.2 ms and 199.4 ms); this ACCEPT reflects the requested candidate-versus-origin/dev rule. **Revisions:** baseline server `origin/dev` `9dc65437e86c74efe92d723f9a99774246e60650` (SHA-256 `115b465fff9fd470a66a78872fa4d294b19cf8d5c4c7de385610ee9e83e239cd`); candidate `67b3f8a55b90922a7eec4078757276c3dc32532d`. **Files:** no tracked files changed. Raw reports: `target/tmp/1493-notes-1000.json` and `target/tmp/1493-notes-5000.json` in the worktree. **Gates:** no Rust or web test gates were run because this job changed no product code. `cargo clean` output: `Removed 1 file, 356B total`.
kayg referenced this issue from a commit 2026-10-09 11:32:38 +00:00
Author
Owner

Deployed to calternal.cloud in merge round 36 (build dbd621434, live 2026-10-10 ~14:40 IST). Closing.

Deployed to calternal.cloud in merge round 36 (build dbd621434, live 2026-10-10 ~14:40 IST). Closing.
kayg closed this issue 2026-10-10 09:14:53 +00:00
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal-v0#1493
No description provided.