PERF: opening a note from a link (tag page, search, attachment, wiki link) is slow; titles not paths on tag pages; prefetch + cached render #639

Open
opened 2026-10-01 15:48:29 +00:00 by kayg · 30 comments
Owner

Owner report (2026-10-01)

"Clicking that note takes too long to open. Please optimise tab switching and going from one link to another."
Context: the tag page (/tag/ai-aside-browser-generated) lists a note by its file path (Notes/20261001-case-stuck-delete-….md) and opening it is slow.
Do:

  1. Show titles, not paths, on the tag page (and every list of items): the note title, with the path as secondary text only if needed (the shared item row; #586's display-name helper).
  2. Fast link navigation: measure the click → note fully painted for: a tag page → note, a search result → note, a Calendar attachment → note, a note → linked note. Target warm ≤ 100 ms p95, cold ≤ 400 ms on the HDD emulation (the #549 harness and budgets).
    • Prefetch on hover/focus (the note body + its card layout data), with the same intent rule as #608.
    • Render the cached note instantly and revalidate (the owner rule).
    • Make sure opening a note does not wait for: backlinks, unlinked mentions, the collab connection, or editor plugin init. Those load after first paint without layout shift.
  3. Add these routes to bench/tab-switch.mjs (the #549 harness) so the weekly perf run tracks them.
    Before/after table on this issue. Web gates (+ notes crate if the API changes).
## Owner report (2026-10-01) "Clicking that note takes too long to open. Please optimise tab switching and going from one link to another." Context: the tag page (`/tag/ai-aside-browser-generated`) lists a note by its **file path** (`Notes/20261001-case-stuck-delete-….md`) and opening it is slow. **Do:** 1. **Show titles, not paths**, on the tag page (and every list of items): the note title, with the path as secondary text only if needed (the shared item row; #586's display-name helper). 2. **Fast link navigation:** measure the click → note fully painted for: a tag page → note, a search result → note, a Calendar attachment → note, a note → linked note. Target warm ≤ 100 ms p95, cold ≤ 400 ms on the HDD emulation (the #549 harness and budgets). - Prefetch on hover/focus (the note body + its card layout data), with the same intent rule as #608. - Render the cached note instantly and revalidate (the owner rule). - Make sure opening a note does not wait for: backlinks, unlinked mentions, the collab connection, or editor plugin init. Those load after first paint without layout shift. 3. Add these routes to `bench/tab-switch.mjs` (the #549 harness) so the weekly perf run tracks them. Before/after table on this issue. Web gates (+ notes crate if the API changes).
Author
Owner

Starting #639 on job/linknav-639 from base SHA 3f258302a0 (the current origin/dev merge base). I have read CLAUDE.md, CONTEXT.md and DESIGN.md §33–34; tracing the existing Note row, navigation, prefetch and tab-switch benchmark paths now.

Starting #639 on job/linknav-639 from base SHA 3f258302a0f2d6418ff60c9ce22cbb33e008ca99 (the current origin/dev merge base). I have read CLAUDE.md, CONTEXT.md and DESIGN.md §33–34; tracing the existing Note row, navigation, prefetch and tab-switch benchmark paths now.
Author
Owner

Finding (base c4a61e8cf): TagItem carries only id/kind/path although the Notes index already stores the visible title; the Tag page therefore renders Notes by path. Stable /n/ currently runs a second client navigation to /notes/, and NoteView waits for /api/v1/auth/me alongside the Note body before it paints; the live editor then holds the card on a skeleton until collaboration sync. I’m removing those waits, reusing Note summaries for display names, and adding note-link scenarios to the #549 harness.

Finding (base c4a61e8cf): TagItem carries only id/kind/path although the Notes index already stores the visible title; the Tag page therefore renders Notes by path. Stable /n/<id> currently runs a second client navigation to /notes/<id>, and NoteView waits for /api/v1/auth/me alongside the Note body before it paints; the live editor then holds the card on a skeleton until collaboration sync. I’m removing those waits, reusing Note summaries for display names, and adding note-link scenarios to the #549 harness.
Author
Owner

Test finding:
running 12 tests
test source::tests::malformed_xml_is_rejected_before_xmp_tag_extraction ... ok
test source::tests::valid_xmp_packet_is_still_readable ... ok
test tests::hidden_folder_metadata_path_is_recognized_only_by_final_component ... ok
test tests::tagged_log_links_use_the_day_and_block_deep_link ... ok
test tests::tags_are_bounded_and_nested_without_empty_segments ... ok
test tests::nested_queries_stop_at_tag_segment_boundaries ... ok
test tests::hidden_sources_do_not_create_tag_suggestions ... ok
test tests::markdown_index_updates_use_the_callers_transaction ... ok
test tests::tagged_note_rows_include_the_notes_index_title ... ok
test tests::tag_search_returns_typed_hits_with_stable_item_links ... ok
test tests::file_assignments_page_groups_tags_and_stops_at_folder_boundaries ... ok
test tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags ... FAILED

failures:

---- tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags stdout ----

thread '' (111341) panicked at crates/calternal-tags/src/lib.rs:1390:73:
called Result::unwrap() on an Err value: Database(Database(SqliteError { code: 1, message: "no such table: note_items" }))
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

thread 'tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags' (111336) panicked at crates/calternal-tags/src/lib.rs:1290:14:
called Result::unwrap() on an Err value: Any { .. }

failures:
tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags

test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.17s reached the existing full-source rebuild test, whose isolated SQLite fixture has Tags migrations but not the Notes-owned table. The production startup applies Notes migrations before Tags. I’m aligning that fixture and rerunning the gate.

Test finding: running 12 tests test source::tests::malformed_xml_is_rejected_before_xmp_tag_extraction ... ok test source::tests::valid_xmp_packet_is_still_readable ... ok test tests::hidden_folder_metadata_path_is_recognized_only_by_final_component ... ok test tests::tagged_log_links_use_the_day_and_block_deep_link ... ok test tests::tags_are_bounded_and_nested_without_empty_segments ... ok test tests::nested_queries_stop_at_tag_segment_boundaries ... ok test tests::hidden_sources_do_not_create_tag_suggestions ... ok test tests::markdown_index_updates_use_the_callers_transaction ... ok test tests::tagged_note_rows_include_the_notes_index_title ... ok test tests::tag_search_returns_typed_hits_with_stable_item_links ... ok test tests::file_assignments_page_groups_tags_and_stops_at_folder_boundaries ... ok test tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags ... FAILED failures: ---- tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags stdout ---- thread '<unnamed>' (111341) panicked at crates/calternal-tags/src/lib.rs:1390:73: called `Result::unwrap()` on an `Err` value: Database(Database(SqliteError { code: 1, message: "no such table: note_items" })) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags' (111336) panicked at crates/calternal-tags/src/lib.rs:1290:14: called `Result::unwrap()` on an `Err` value: Any { .. } failures: tests::rebuilds_all_tag_sources_after_index_loss_and_renames_nested_tags test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.17s reached the existing full-source rebuild test, whose isolated SQLite fixture has Tags migrations but not the Notes-owned table. The production startup applies Notes migrations before Tags. I’m aligning that fixture and rerunning the gate.
Author
Owner

Closed the first API slice in bf6b5347d6: TagItem now includes the Notes index title for note and bookmark rows. The title lookup is one bound query for the page. I also added the Notes-owned note_items table to the isolated full-source-rebuild fixture after its first run showed no such table: note_items.

Verification:

  • cargo fmt --check: exit 0, no output.
  • cargo clippy -p calternal-tags --all-targets -- -D warnings: Checking calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/linknav-639/crates/calternal-tags); Finished dev profile [unoptimized + debuginfo] target(s) in 14.60s.
  • cargo test -p calternal-tags: test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.46s; doc tests: test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s.
Closed the first API slice in bf6b5347d6d1788eaf1ec159243d3402e14083fc: TagItem now includes the Notes index title for note and bookmark rows. The title lookup is one bound query for the page. I also added the Notes-owned note_items table to the isolated full-source-rebuild fixture after its first run showed `no such table: note_items`. Verification: - `cargo fmt --check`: exit 0, no output. - `cargo clippy -p calternal-tags --all-targets -- -D warnings`: `Checking calternal-tags v0.0.1 (/home/kayg/Developer/calternal-wt/linknav-639/crates/calternal-tags)`; `Finished dev profile [unoptimized + debuginfo] target(s) in 14.60s`. - `cargo test -p calternal-tags`: `test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 5.46s`; doc tests: `test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s`.
Author
Owner

Additional navigation finding: NoteView.followLink awaits noteIndex.ensure() to resolve one relative Markdown target. ensure() pages the full Notes list in batches of 100 before returning, so opening a linked Note can wait on unrelated Notes. I’m adding an exact path filter to the existing authenticated Notes list query and will use it for link/search prefetch and the click fallback. The query will bind both the user ID and exact path; it does not access the filesystem.

Additional navigation finding: `NoteView.followLink` awaits `noteIndex.ensure()` to resolve one relative Markdown target. `ensure()` pages the full Notes list in batches of 100 before returning, so opening a linked Note can wait on unrelated Notes. I’m adding an exact path filter to the existing authenticated Notes list query and will use it for link/search prefetch and the click fallback. The query will bind both the user ID and exact path; it does not access the filesystem.
Author
Owner

Starting resumed work on branch job/linknav-639. Base: c4a61e8cf0 (origin/dev). Existing work is intact; I will continue from commit bf6b5347d and report new findings and gates here.

Starting resumed work on branch job/linknav-639. Base: c4a61e8cf090170f35b1bed3350d9de20c83ecd5 (origin/dev). Existing work is intact; I will continue from commit bf6b5347d and report new findings and gates here.
Author
Owner

Additional navigation finding: Search hits use a search-index key and /notes?path=<Home-relative path>, not a Note ID. apps/web/src/routes/notes/+page.svelte then awaited noteIndex.ensure(), paging every Note before opening the target. Search prefetch must resolve result.path with the exact-path API; treating result.id as a Note ID returns 404. I’m removing that full-index wait for both warm and cold search opens.

Additional navigation finding: Search hits use a search-index key and `/notes?path=<Home-relative path>`, not a Note ID. `apps/web/src/routes/notes/+page.svelte` then awaited `noteIndex.ensure()`, paging every Note before opening the target. Search prefetch must resolve `result.path` with the exact-path API; treating `result.id` as a Note ID returns 404. I’m removing that full-index wait for both warm and cold search opens.
Author
Owner

The Notes gate found two failures in the existing crate suite while the new #639 exact-path lookup regression passed. cargo test -p calternal-plugin-notes completed with 166 passed and 2 failed: tests::daily_and_composer_preserve_unrelated_bytes returned 404 where its existing assertion expects 200 (crates/plugins/notes/src/lib.rs:9442), and tests::journal_patch_storm_finishes_with_one_winner_and_stale_preconditions timed out waiting for completion (:10756). I did not change either expectation. cargo fmt --check and cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings passed. I’ll include exact command output in the final report.

The Notes gate found two failures in the existing crate suite while the new #639 exact-path lookup regression passed. `cargo test -p calternal-plugin-notes` completed with 166 passed and 2 failed: `tests::daily_and_composer_preserve_unrelated_bytes` returned 404 where its existing assertion expects 200 (`crates/plugins/notes/src/lib.rs:9442`), and `tests::journal_patch_storm_finishes_with_one_winner_and_stale_preconditions` timed out waiting for completion (`:10756`). I did not change either expectation. `cargo fmt --check` and `cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings` passed. I’ll include exact command output in the final report.
Author
Owner

LinkNav #639 final report

Built

  • Tag rows show Note titles first and keep the path as secondary context. Shared Note rows use the same display-name helper.
  • Notes GET /api/v1/notes?path= resolves one exact, user-scoped Note. Tag titles come from the Notes index in one bound query.
  • Stable Note links warm route code and Note body after a shared 180 ms pointer/focus intent. NoteView paints cached bodies first and loads collaboration, reminders, backlinks, and unlinked mentions after first paint.
  • /n/:id renders NoteView directly. Calendar, Search, and Note links use exact-path lookup instead of paging the full Notes index.
  • Added the four requested routes and linknav fixture to bench/tab-switch.mjs; added a focused real-server hostile-path and concurrency probe.

Files

crates/calternal-tags/src/{index.rs,lib.rs}, crates/plugins/notes/src/lib.rs, contracts/openapi.json, packages/api-client/src/generated.ts; apps/web/src/lib/{api/notes.ts,calendar/journal.ts,itemDisplayName.ts,itemDisplayName.test.ts,notes/api.ts,notes/notePrefetch.ts,notes/NoteView.svelte,components/NoteList.svelte,components/search-dialog.svelte,search/SearchResultRow.svelte}; apps/web/src/routes/{calendar/[view]/[date]/+page.svelte,n/[id]/+page.svelte,notes/+page.svelte,tag/[tag]/+page.svelte}; packages/ui/src/{intent.ts,index.ts,components/calendar/AgendaList.svelte,components/calendar/AttachmentDeck.svelte,components/calendar/ItemPreview.svelte,components/notes/NoteMentionsCard.svelte}; bench/tab-switch.mjs, bench/tab-switch-seed.py, tests/adversarial/{notes_path_lookup.py,run.sh,setup.mjs}.

Before/after performance

Source route Warm p95 before → after Cold p95 before → after
Tag → Note Not captured → not captured Not captured → not captured
Search → Note Not captured → not captured Not captured → not captured
Calendar attachment → Note Not captured → not captured Not captured → not captured
Note → linked Note Not captured → not captured Not captured → not captured

The release build did not finish within the job window; it remained in the native xmp_toolkit build. A local smoke attempt stopped before fixture readiness. SQLite reported VACUUM INTO at 2.814 s and the files_index check at 4.021 s, then the harness reported Error: perf server did not become ready. No route timing samples or screenshots were produced. The perf VM lock was available, but the shared release binary predates this API change.

Gates

  • cargo fmt --check: exit 0, no output.
  • bun run check:
    User browser caches use userStorage; only documented device/public-link exceptions remain.
    Text sizes and UI shape values use shared role tokens.
    UI transitions and animation options use shared motion tokens or documented exceptions.
    Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/linknav-639/apps/web
    Getting Svelte diagnostics...
    
    svelte-check found 0 errors and 0 warnings
    
  • Earlier cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings passed. Earlier cargo test -p calternal-plugin-notes: 166 passed, 2 failed: daily_and_composer_preserve_unrelated_bytes returned 404 where the existing test expects 200; journal_patch_storm_finishes_with_one_winner_and_stale_preconditions timed out. The new exact-path test passed. Existing expectations were not changed.
  • Focused adversarial output:
    Focused Notes path fixture ready
    Notes exact-path lookup: 8 hostile paths, 16 concurrent reads, 0 findings
    
  • bun run test started, but Vitest had not reported results at the four-hour job cap. Final tags/server clippy and test gates were not completed.
  • cargo clean: Removed 19141 files, 9.0GiB total. apps/web/build was deleted.

UX gaps closed

Title-first Note rows, stable Copy link destinations, intent prefetch on pointer and keyboard focus, exact path resolution, cached first paint, and post-paint secondary Note services.

UX gaps left

The production screenshot matrix and click-to-paint samples could not be captured because server startup stalled on host filesystem I/O. Touch/keyboard flows were not visually verified across the full matrix in this run.

Mac checks pending

The Mac VM is offline. When it returns, rerun the production screenshot profile with macOS emulation at 390, 820, and 1440 px in light and dark, then review Tag, Search, Calendar attachment, and linked-Note navigation and their shortcut glyphs.

Decisions

  • Use a shared 180 ms intent rest to filter pointer flyovers and warm deliberate focus/hover.
  • Bound the Note body cache to 24 entries, 4 MiB per body, and 16 MiB total.
  • Start Note mention panels collapsed so later backlink data does not shift first paint.

Head: 8eac784887d1dafa78a2cc33687b9c6dbd0137dc.

## LinkNav #639 final report ### Built - Tag rows show Note titles first and keep the path as secondary context. Shared Note rows use the same display-name helper. - Notes `GET /api/v1/notes?path=` resolves one exact, user-scoped Note. Tag titles come from the Notes index in one bound query. - Stable Note links warm route code and Note body after a shared 180 ms pointer/focus intent. NoteView paints cached bodies first and loads collaboration, reminders, backlinks, and unlinked mentions after first paint. - `/n/:id` renders NoteView directly. Calendar, Search, and Note links use exact-path lookup instead of paging the full Notes index. - Added the four requested routes and linknav fixture to `bench/tab-switch.mjs`; added a focused real-server hostile-path and concurrency probe. ### Files `crates/calternal-tags/src/{index.rs,lib.rs}`, `crates/plugins/notes/src/lib.rs`, `contracts/openapi.json`, `packages/api-client/src/generated.ts`; `apps/web/src/lib/{api/notes.ts,calendar/journal.ts,itemDisplayName.ts,itemDisplayName.test.ts,notes/api.ts,notes/notePrefetch.ts,notes/NoteView.svelte,components/NoteList.svelte,components/search-dialog.svelte,search/SearchResultRow.svelte}`; `apps/web/src/routes/{calendar/[view]/[date]/+page.svelte,n/[id]/+page.svelte,notes/+page.svelte,tag/[tag]/+page.svelte}`; `packages/ui/src/{intent.ts,index.ts,components/calendar/AgendaList.svelte,components/calendar/AttachmentDeck.svelte,components/calendar/ItemPreview.svelte,components/notes/NoteMentionsCard.svelte}`; `bench/tab-switch.mjs`, `bench/tab-switch-seed.py`, `tests/adversarial/{notes_path_lookup.py,run.sh,setup.mjs}`. ### Before/after performance | Source route | Warm p95 before → after | Cold p95 before → after | |---|---:|---:| | Tag → Note | Not captured → not captured | Not captured → not captured | | Search → Note | Not captured → not captured | Not captured → not captured | | Calendar attachment → Note | Not captured → not captured | Not captured → not captured | | Note → linked Note | Not captured → not captured | Not captured → not captured | The release build did not finish within the job window; it remained in the native `xmp_toolkit` build. A local smoke attempt stopped before fixture readiness. SQLite reported `VACUUM INTO` at 2.814 s and the `files_index` check at 4.021 s, then the harness reported `Error: perf server did not become ready`. No route timing samples or screenshots were produced. The perf VM lock was available, but the shared release binary predates this API change. ### Gates - `cargo fmt --check`: exit 0, no output. - `bun run check`: ```text User browser caches use userStorage; only documented device/public-link exceptions remain. Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/linknav-639/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` - Earlier `cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings` passed. Earlier `cargo test -p calternal-plugin-notes`: 166 passed, 2 failed: `daily_and_composer_preserve_unrelated_bytes` returned 404 where the existing test expects 200; `journal_patch_storm_finishes_with_one_winner_and_stale_preconditions` timed out. The new exact-path test passed. Existing expectations were not changed. - Focused adversarial output: ```text Focused Notes path fixture ready Notes exact-path lookup: 8 hostile paths, 16 concurrent reads, 0 findings ``` - `bun run test` started, but Vitest had not reported results at the four-hour job cap. Final tags/server clippy and test gates were not completed. - `cargo clean`: `Removed 19141 files, 9.0GiB total`. `apps/web/build` was deleted. ### UX gaps closed Title-first Note rows, stable Copy link destinations, intent prefetch on pointer and keyboard focus, exact path resolution, cached first paint, and post-paint secondary Note services. ### UX gaps left The production screenshot matrix and click-to-paint samples could not be captured because server startup stalled on host filesystem I/O. Touch/keyboard flows were not visually verified across the full matrix in this run. ### Mac checks pending The Mac VM is offline. When it returns, rerun the production screenshot profile with macOS emulation at 390, 820, and 1440 px in light and dark, then review Tag, Search, Calendar attachment, and linked-Note navigation and their shortcut glyphs. ### Decisions - Use a shared 180 ms intent rest to filter pointer flyovers and warm deliberate focus/hover. - Bound the Note body cache to 24 entries, 4 MiB per body, and 16 MiB total. - Start Note mention panels collapsed so later backlink data does not shift first paint. Head: `8eac784887d1dafa78a2cc33687b9c6dbd0137dc`.
Author
Owner

Starting an independent read-only review on branch job/rev2-linknav-639 at base SHA 440e19dce2. Review target: job/linknav-639 at 8eac784887. I am inspecting the issue and design requirements; I will not build or test.

Starting an independent read-only review on branch job/rev2-linknav-639 at base SHA 440e19dce23040ac8ebaae88f0469b6535b1afcb. Review target: job/linknav-639 at 8eac784887d1dafa78a2cc33687b9c6dbd0137dc. I am inspecting the issue and design requirements; I will not build or test.
Author
Owner

Independent review of #639

Target: job/linknav-639 at 8eac784887d1dafa78a2cc33687b9c6dbd0137dc.

Findings, ranked by severity

P2 — The Note body still waits for editor initialization. apps/web/src/lib/notes/NoteView.svelte:1155-1163 renders the cached or fresh body through MarkdownEditor. packages/editor/src/Editor.svelte:23-26 parses Markdown during setup; :32-42 creates TipTap and its extensions in a later animation frame; :142-145 renders the body only after the editor exists. This does not meet #639's requirement to paint the Note before editor plugin initialization.

Fix: Render the cached body with a lightweight read-only renderer first. Mount the full editor after that paint and keep the layout stable.

Test idea: Delay editor creation. Open a warm Note and assert that its body is visible before the editor reports ready, with no layout shift when the editor mounts.

Review checks

The exact path query binds user_id and path and does not use the path for filesystem access. The Tag title lookup binds the User and paths. I found no weakened test expectation in the branch's test-file history.

The diff adds the four route profiles but has no before/after timing results. The latency targets remain unverified. DESIGN §58 is not present in the target file, which ends at §57; I used §§9, 18 and 33.

Gates

git diff --check: exit 0, no output.
Build and test gates: not run. The read-only review contract prohibits builds and tests.

Review report: review-linknav-639.md.
Review branch head: 93d9b05d5.

## Independent review of #639 Target: `job/linknav-639` at `8eac784887d1dafa78a2cc33687b9c6dbd0137dc`. ### Findings, ranked by severity **P2 — The Note body still waits for editor initialization.** `apps/web/src/lib/notes/NoteView.svelte:1155-1163` renders the cached or fresh body through `MarkdownEditor`. `packages/editor/src/Editor.svelte:23-26` parses Markdown during setup; `:32-42` creates TipTap and its extensions in a later animation frame; `:142-145` renders the body only after the editor exists. This does not meet #639's requirement to paint the Note before editor plugin initialization. **Fix:** Render the cached body with a lightweight read-only renderer first. Mount the full editor after that paint and keep the layout stable. **Test idea:** Delay editor creation. Open a warm Note and assert that its body is visible before the editor reports ready, with no layout shift when the editor mounts. ### Review checks The exact path query binds `user_id` and `path` and does not use the path for filesystem access. The Tag title lookup binds the User and paths. I found no weakened test expectation in the branch's test-file history. The diff adds the four route profiles but has no before/after timing results. The latency targets remain unverified. DESIGN §58 is not present in the target file, which ends at §57; I used §§9, 18 and 33. ### Gates `git diff --check`: exit 0, no output. Build and test gates: not run. The read-only review contract prohibits builds and tests. Review report: `review-linknav-639.md`. Review branch head: `93d9b05d5`.
Author
Owner

Round 2 started on job/linknav-639 at 8eac784887. Read the independent P2 review. I am fixing first-paint rendering and tracing the Daily note/Composer 404 without changing its expectation. The latest verification policy defers full web suites and broad adversarial matrices to the merge round.

Round 2 started on job/linknav-639 at 8eac784887d1dafa78a2cc33687b9c6dbd0137dc. Read the independent P2 review. I am fixing first-paint rendering and tracing the Daily note/Composer 404 without changing its expectation. The latest verification policy defers full web suites and broad adversarial matrices to the merge round.
Author
Owner

Finding: Notes test setup assigns every separate Home/Index the same User ID, but USER_LOCKS is process-wide. journal_snapshot uses try_lock_owned and returns 404 when legacy IDs need repair and that shared User lock is busy. This explains the exact /journal/2026-09-24 request in daily_and_composer_preserve_unrelated_bytes, without any call to the new Notes list path lookup. A new regression holds a second fixture Home writer lock and requires the imported Daily note read to return 200; fixture User IDs now differ. The existing status and PATCH-storm expectations remain unchanged.

Finding: Notes test setup assigns every separate Home/Index the same User ID, but USER_LOCKS is process-wide. journal_snapshot uses try_lock_owned and returns 404 when legacy IDs need repair and that shared User lock is busy. This explains the exact /journal/2026-09-24 request in daily_and_composer_preserve_unrelated_bytes, without any call to the new Notes list path lookup. A new regression holds a second fixture Home writer lock and requires the imported Daily note read to return 200; fixture User IDs now differ. The existing status and PATCH-storm expectations remain unchanged.
Author
Owner

Focused web gates found another branch regression: itemDisplayName({path: "Notes/"}, "Untitled note") returned "Notes", while its existing test requires the empty-state fallback. The helper stripped the trailing slash before taking the final component. I kept the expectation and changed the helper to treat a directory-only path as having no filename; added repeated-slash coverage.

Focused web gates found another branch regression: itemDisplayName({path: "Notes/"}, "Untitled note") returned "Notes", while its existing test requires the empty-state fallback. The helper stripped the trailing slash before taking the final component. I kept the expectation and changed the helper to treat a directory-only path as having no filename; added repeated-slash coverage.
Author
Owner

Committed d4d41d765 (directory-only display fallback) and 13bdde5e4 (shared-parser Note preview before editor creation, focus handoff, first-paint benchmark selector). Merged origin/dev once at c4faf184df into dce316bd6, including 440e19dce. No conflict. Final per-crate Rust gates are queued in order, with four build jobs and four test threads; focused web gates and the production build run separately.

Committed d4d41d765 (directory-only display fallback) and 13bdde5e4 (shared-parser Note preview before editor creation, focus handoff, first-paint benchmark selector). Merged origin/dev once at c4faf184df726a9375ae0c13bdfb6018ac2cf57e into dce316bd6, including 440e19dce. No conflict. Final per-crate Rust gates are queued in order, with four build jobs and four test threads; focused web gates and the production build run separately.
Author
Owner

Production capture finding: /n/ showed the Note body with Calendar chrome because modeForPath only recognizes /notes and /notes/. Stable Note routes now map directly to the Notes Tab and Note title; the added navigation regression passes (11 tests in the focused file). Also aligned the Markdown fallback with the shared prose class so the first-paint projection can retain its text layout when that editor starts. The focused browser regression delays both live grammar setup and fallback editor creation.

Production capture finding: /n/<id> showed the Note body with Calendar chrome because modeForPath only recognizes /notes and /notes/. Stable Note routes now map directly to the Notes Tab and Note title; the added navigation regression passes (11 tests in the focused file). Also aligned the Markdown fallback with the shared prose class so the first-paint projection can retain its text layout when that editor starts. The focused browser regression delays both live grammar setup and fallback editor creation.
Author
Owner

Origin comparison: the new independent-Home regression returns 404 (Daily note not indexed) on origin/dev, and the existing PATCH storm with an unrelated fixture writer hits its original 12-second timeout there. Both tests keep the 200/deadline expectations. The test fixtures reuse one process-wide User lock despite separate Homes and Indexes; giving each fixture a unique User isolates them. Full output will be attached with the final evidence. The branch source was restored after the comparison; Notes, Tags and server gates are running.

Origin comparison: the new independent-Home regression returns 404 (Daily note not indexed) on origin/dev, and the existing PATCH storm with an unrelated fixture writer hits its original 12-second timeout there. Both tests keep the 200/deadline expectations. The test fixtures reuse one process-wide User lock despite separate Homes and Indexes; giving each fixture a unique User isolates them. Full output will be attached with the final evidence. The branch source was restored after the comparison; Notes, Tags and server gates are running.
Author
Owner

Committed 56f3d9b50: reserve the existing heading Pill footprint before editor mount, apply the shared prose class to the Markdown fallback, and keep a visible keyboard focus ring on preview anchors. Focused production-build browser regression passed at 390/820/1440 in Light and Dark with macOS emulation: delayed editor creation, cached text while fresh GET is held, body bounds within 1 px after mount, touch or Tab/Enter navigation, and actual clipboard contents for Copy link. This pass uses a compatible prebuilt debug server and exercises the Markdown fallback. Branch-server/live-editor verification remains pending while crate gates compile.

Committed 56f3d9b50: reserve the existing heading Pill footprint before editor mount, apply the shared prose class to the Markdown fallback, and keep a visible keyboard focus ring on preview anchors. Focused production-build browser regression passed at 390/820/1440 in Light and Dark with macOS emulation: delayed editor creation, cached text while fresh GET is held, body bounds within 1 px after mount, touch or Tab/Enter navigation, and actual clipboard contents for Copy link. This pass uses a compatible prebuilt debug server and exercises the Markdown fallback. Branch-server/live-editor verification remains pending while crate gates compile.
Author
Owner

Browser evidence for 56f3d9b50 (macOS emulation, 390/820/1440, Light and Dark):

The ZIP contains all 26 captures and a provenance README. This is the production web build with the compatible prebuilt photopw-849 debug server; the real Markdown fallback is exercised. All six focused browser conditions passed delayed-editor/cached-paint/geometry/input/clipboard assertions. Visual review is for the orchestrator. Branch-server live-editor verification is pending.

Browser evidence for 56f3d9b50 (macOS emulation, 390/820/1440, Light and Dark): - [linknav-639-macos-screenshots.zip](https://git.kayg.org/attachments/3b605819-29c7-4049-a81e-0bb1762d1d37) - [390-light-copy-menu.png](https://git.kayg.org/attachments/fb6fb95e-af48-4712-aff7-95cd0c099f3f) - [820-dark-link-focus.png](https://git.kayg.org/attachments/73fa2c1e-d551-4c0d-8bab-07df6dbd896b) - [1440-dark-link-hover.png](https://git.kayg.org/attachments/be200067-7d78-4728-a949-8be649213615) The ZIP contains all 26 captures and a provenance README. This is the production web build with the compatible prebuilt photopw-849 debug server; the real Markdown fallback is exercised. All six focused browser conditions passed delayed-editor/cached-paint/geometry/input/clipboard assertions. Visual review is for the orchestrator. Branch-server live-editor verification is pending.
Author
Owner

Committed e2b46ab5d: each Notes test Home now has a unique User, with an independent-Home Daily-note ID-repair regression and the original PATCH storm exercised while another fixture writer is held. Notes Clippy passed. Notes unit tests: test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 198.27s. The apple_replay integration test also passed (1 passed, 0 failed). Doc-tests and Tags/server gates continue. No production path matching or test expectation changed for this fix.

Committed e2b46ab5d: each Notes test Home now has a unique User, with an independent-Home Daily-note ID-repair regression and the original PATCH storm exercised while another fixture writer is held. Notes Clippy passed. Notes unit tests: test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 198.27s. The apple_replay integration test also passed (1 passed, 0 failed). Doc-tests and Tags/server gates continue. No production path matching or test expectation changed for this fix.
Author
Owner

Additional UX finding: a focused preview anchor lost focus when the Markdown editor mounted. A new browser assertion reproduced it. EditorContent attaches the anchor DOM after the ready callback flush, so the initial tick-only focus handoff ran too early. The fix schedules the handoff in the next frame and does not reclaim focus if the User chose another control. Web check passed again; the production build and focused browser verification are in progress. Tags Clippy passed; all 12 Tags tests passed.

Additional UX finding: a focused preview anchor lost focus when the Markdown editor mounted. A new browser assertion reproduced it. EditorContent attaches the anchor DOM after the ready callback flush, so the initial tick-only focus handoff ran too early. The fix schedules the handoff in the next frame and does not reclaim focus if the User chose another control. Web check passed again; the production build and focused browser verification are in progress. Tags Clippy passed; all 12 Tags tests passed.
Author
Owner

Server Clippy passed: Finished dev profile [unoptimized + debuginfo] target(s) in 9m 57s. Server tests are compiling. Focus diagnosis corrected: the handoff reaches a connected anchor, but Chromium ignores focus on that editable descendant until it has an explicit tabindex. A test-only probe proved explicit tabindex restores focus. The production fix adds that tab stop during handoff; the regression checks focus retention and Enter after mount. Temporary probe code has been removed.

Server Clippy passed: Finished `dev` profile [unoptimized + debuginfo] target(s) in 9m 57s. Server tests are compiling. Focus diagnosis corrected: the handoff reaches a connected anchor, but Chromium ignores focus on that editable descendant until it has an explicit tabindex. A test-only probe proved explicit tabindex restores focus. The production fix adds that tab stop during handoff; the regression checks focus retention and Enter after mount. Temporary probe code has been removed.
Author
Owner

Focus handoff now passes with an explicit tab stop. The next regression assertion found that TipTap consumes Enter on an anchor inside the fallback editor. The Note article now captures Enter only on native anchors and calls the existing pointer navigation handler before editor bindings run. The focused browser test also compares the saved source body after activation to prove Enter did not edit it. Web check passed; rebuilt browser verification is pending.

Focus handoff now passes with an explicit tab stop. The next regression assertion found that TipTap consumes Enter on an anchor inside the fallback editor. The Note article now captures Enter only on native anchors and calls the existing pointer navigation handler before editor bindings run. The focused browser test also compares the saved source body after activation to prove Enter did not edit it. Web check passed; rebuilt browser verification is pending.
Author
Owner

Committed 65529c0d7: preserve focused preview links across editor mount (explicit fallback tab stop, generation/focus guards), and capture Enter on anchors before TipTap bindings. All six macOS-emulated device/theme browser conditions pass focus handoff and Enter after mount, and the saved source Note body remains unchanged. Additional chrome gap found: app-sidebar still guards NotesExplorer with a legacy /notes prefix, leaving stable /n/ Note links without their explorer. It now uses the shared modeForPath registry. A browser assertion for the selected Note row and the phone sheet is being added in a separate slice.

Committed 65529c0d7: preserve focused preview links across editor mount (explicit fallback tab stop, generation/focus guards), and capture Enter on anchors before TipTap bindings. All six macOS-emulated device/theme browser conditions pass focus handoff and Enter after mount, and the saved source Note body remains unchanged. Additional chrome gap found: app-sidebar still guards NotesExplorer with a legacy /notes prefix, leaving stable /n/ Note links without their explorer. It now uses the shared modeForPath registry. A browser assertion for the selected Note row and the phone sheet is being added in a separate slice.
Author
Owner

Committed 9b15df25f: NotesExplorer now uses the existing route registry for stable /n/ Note links. Web check and 25 focused navigation/chrome tests passed. All six browser conditions pass the selected Notes explorer row; phone touch opens the sheet and Escape closes it. The final screenshot set now has 28 captures (including phone Notes sheets). Server tests continue compiling; Notes/Tags gates and server Clippy are complete.

Committed 9b15df25f: NotesExplorer now uses the existing route registry for stable /n/ Note links. Web check and 25 focused navigation/chrome tests passed. All six browser conditions pass the selected Notes explorer row; phone touch opens the sheet and Escape closes it. The final screenshot set now has 28 captures (including phone Notes sheets). Server tests continue compiling; Notes/Tags gates and server Clippy are complete.
Author
Owner

All Rust gates passed. Server tests: test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 31.57s. Complete Notes/Tags/server Clippy and test logs, web checks and focused test output, and the two deliberate origin/dev negative controls are attached: gate evidence ZIP. Head is 652083141a. The branch debug server is still building for one final focused browser pass; no release build or full suite is repeated in this job.

All Rust gates passed. Server tests: test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 31.57s. Complete Notes/Tags/server Clippy and test logs, web checks and focused test output, and the two deliberate origin/dev negative controls are attached: [gate evidence ZIP](https://git.kayg.org/attachments/da729632-1d26-4e1b-80e2-344a53b523cf). Head is 652083141a8d6a00253d3c521ed6e7c0fc341de9. The branch debug server is still building for one final focused browser pass; no release build or full suite is repeated in this job.
Author
Owner

The production browser walk found another #639 keyboard gap: after live editor mount, Tab skipped the second inline Note link. The focused regression failed at Tab reaches the next inline link after editor mount; it preserves the original Source body assertions.

The fix uses an optional linkHTMLAttributes hook in the shared editor preset and standalone editor. Notes pass tabindex=0 to both hosts. The default and public Guest preset remain unchanged; this is a small public addition, with no schema or stored Markdown change. The app declaration boundary mirrors it. The focused editor test covers both default and opted-in DOM attributes and verifies the published Markdown bytes. Six tests passed. The refreshed web check, production build and macOS-emulated browser matrix are running.

The production browser walk found another #639 keyboard gap: after live editor mount, Tab skipped the second inline Note link. The focused regression failed at `Tab reaches the next inline link after editor mount`; it preserves the original Source body assertions. The fix uses an optional `linkHTMLAttributes` hook in the shared editor preset and standalone editor. Notes pass `tabindex=0` to both hosts. The default and public Guest preset remain unchanged; this is a small public addition, with no schema or stored Markdown change. The app declaration boundary mirrors it. The focused editor test covers both default and opted-in DOM attributes and verifies the published Markdown bytes. Six tests passed. The refreshed web check, production build and macOS-emulated browser matrix are running.
Author
Owner

Head 151d724130 passes the editor unit tests, both web/editor checks and the production build. The refreshed browser regression then failed at the second-link Tab assertion in the live host. An earlier attempt stopped at fixture creation with HTTP 503; its log is retained separately.

The follow-up keeps native Tab at the existing article capture boundary for focused anchors, alongside the existing Enter handling. It stops editor shortcut propagation while preserving browser default focus navigation. A desktop diagnostic and the next production build are in progress. No previous test expectation was weakened. Final screenshots and evidence will use the complete passing production matrix, not this failed run.

Head 151d7241306747fce020d2839102a349c920426b passes the editor unit tests, both web/editor checks and the production build. The refreshed browser regression then failed at the second-link Tab assertion in the live host. An earlier attempt stopped at fixture creation with HTTP 503; its log is retained separately. The follow-up keeps native Tab at the existing article capture boundary for focused anchors, alongside the existing Enter handling. It stops editor shortcut propagation while preserving browser default focus navigation. A desktop diagnostic and the next production build are in progress. No previous test expectation was weakened. Final screenshots and evidence will use the complete passing production matrix, not this failed run.
Author
Owner

The caret fix now has a passing focused regression in editorHost.svelte.test.ts: align editor state and a foreign/detached DOM caret, retain native anchor focus, keep the Note document unchanged, and avoid repeated dispatch when the point already matches. The helper is shared by live/fallback Note focus and preview handoff.

The final production browser attempt passed 390 Light with live source and target editors, including the new two-link Tab regression, touch, Copy link, stable geometry and unchanged Note bytes. Remaining conditions and final check/build are running. The screenshots use the production bundle built with the complete caret guard; the last source refactor moves that same guard into editorHost without changing markup, CSS or behavior.

The independent fresh-account startup 503 is filed as #953, with unchanged [200, 200] and 201 expectations. It is not counted as a link-navigation pass.

The caret fix now has a passing focused regression in `editorHost.svelte.test.ts`: align editor state and a foreign/detached DOM caret, retain native anchor focus, keep the Note document unchanged, and avoid repeated dispatch when the point already matches. The helper is shared by live/fallback Note focus and preview handoff. The final production browser attempt passed 390 Light with live source and target editors, including the new two-link Tab regression, touch, Copy link, stable geometry and unchanged Note bytes. Remaining conditions and final check/build are running. The screenshots use the production bundle built with the complete caret guard; the last source refactor moves that same guard into `editorHost` without changing markup, CSS or behavior. The independent fresh-account startup 503 is filed as #953, with unchanged `[200, 200]` and `201` expectations. It is not counted as a link-navigation pass.
Author
Owner

Round 2 final report

Head: bf74a5831a89392857c7f65c622f8236b6c02b1c. Branch: job/linknav-639. The single authorized origin/dev merge is dce316bd6 (base c4faf184d, including 440e19dce). No push or deploy. The worktree is clean.

Built: readable Note text and links paint before TipTap starts, with stable prose/heading geometry for the covered content. Stable /n/ links retain Notes chrome and the selected explorer row. Native links keep focus across editor creation; the editable caret follows focused links so Chromium Tab reaches successive links and Enter follows without editing the Source body. The benchmark supports genuine pre-linknav artifacts.

The 404 was reproduced with origin/dev Notes source. Parallel test Homes reused one User ID and shared the global writer lock. Each fixture now gets its own User ID. A held independent Home writer reproduces the old 404 and the old storm deadline failure on origin/dev; both pass on this branch. The original status, 12-second deadline and ETag assertions remain unchanged.

Files: crates/plugins/notes/src/lib.rs; apps/web/src/lib/notes/{NoteView.svelte,readPreview.ts,readPreview.test.ts,editorHost.ts,editorHost.svelte.test.ts,editor-types/index.d.ts}; apps/web/src/lib/{itemDisplayName.ts,itemDisplayName.test.ts,navigation.ts,navigation.test.ts}; apps/web/src/lib/components/app-sidebar.svelte; apps/web/e2e/linknav-639.mjs; packages/editor/src/{Editor.svelte,Editor.svelte.test.ts,extensions.ts,source.ts}; bench/tab-switch.mjs.

UX gaps closed: stable Note routes lost Notes chrome/explorer; editor mount lost link focus; Enter could edit the Note; Tab skipped subsequent inline links. The focused production browser regression checks real API data, cached paint while fresh GET is held, delayed editor creation, source and linked-target geometry, native input, Copy link clipboard bytes and unchanged Source bytes.

UX gaps left: before editor creation, Tasks are a read-only projection and images show their alternate text. Rich-media, task and callout handoff geometry is outside the captured plain-prose/heading/link regression. Screenshots use macOS emulation in Chromium; real Safari/Mac interop belongs to the merge round.

Decisions: use the existing shared Markdown parser and fixed escaped HTML projection with shared prose tokens; defer editor creation until after body paint. Add an optional display-only link DOM attribute hook for Notes and align selection-only caret state with native link focus; default editor consumers and public Guest settings remain unchanged. Use one test User per test Home. No dependencies or production API lookup semantics changed.

Known gaps: the caret regression passed once before a type-boundary import adjustment. Its later fork and threads retries failed at worker startup before any test ran. The final web check and build passed. Startup API 503 evidence is filed separately as #953. Before/after cold/warm perf numbers are not available. The available shared release predates #639, and matching origin/dev and branch release artifacts were not supplied. The final verification policy assigns release builds and full suites to the merge round; no local latency figures replace the requested VM measurement.

For the merge round:

Run cd apps/web && bunx vitest run src/lib/notes/editorHost.svelte.test.ts --maxWorkers=2 to confirm the final test import under the merge-round runner. Run cd apps/web && bun run test --maxWorkers=2 to prove the full web suite. Run bash tests/adversarial/run.sh from the root to prove the combined XUser/authz/robustness matrices, including exact Notes path lookup. Full e2e, release builds, staging and Mac interop follow the shared merge-round policy.

Set LINKNAV_BEFORE_SERVER_BIN and LINKNAV_AFTER_SERVER_BIN to release servers that embed the matching web artifacts. From the repo root, run:

TAB_SWITCH_VM_LOCK_WAIT_SECONDS=14400 TAB_SWITCH_BUILD_BIN="$LINKNAV_BEFORE_SERVER_BIN" bun bench/tab-switch.mjs --linknav-only --linknav-before --linknav-runs 11 --conditions warm,cold --engines chromium --viewports 1440 --themes light --json artifacts/linknav-before.json
TAB_SWITCH_VM_LOCK_WAIT_SECONDS=14400 TAB_SWITCH_BUILD_BIN="$LINKNAV_AFTER_SERVER_BIN" bun bench/tab-switch.mjs --linknav-only --linknav-runs 11 --conditions warm,cold --engines chromium --viewports 1440 --themes light --json artifacts/linknav-after.json

The harness runs the HDD profile on the perf VM, holds /root/perf.lock during each measured phase and records load inside the lock. Compare linked-Note cold/warm click-to-painted p50/p95 and route budget. There is no dedicated linknav value in the current baseline.

Evidence: all six macOS-emulated conditions passed at 390/820/1440 in Light/Dark with live source and target editors. The ZIP has all 34 screenshots. Captures use the production bundle with the complete caret guard before its behavior-preserving extraction into editorHost; final extracted source check/build passed, and the helper regression passed before its declaration-boundary test import adjustment. Visual review remains with the orchestrator. Icon/text alignment was checked in the shared explorer/menu captures.

cargo fmt --check exited 0 with no output. Gate output excerpts below are verbatim; complete logs and deliberately failing origin/dev controls are in the archive. The caret first-pass excerpt and later worker-startup failures are clearly separated. The latest caret retries did not execute tests.

notes-clippy

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 10m 56s

tags-clippy

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 22.79s

server-clippy

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 9m 57s

notes-test

    Finished `test` profile [unoptimized + debuginfo] target(s) in 38.94s
test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 198.27s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.44s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

tags-test

    Finished `test` profile [unoptimized + debuginfo] target(s) in 45.11s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 9.64s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

server-test

    Finished `test` profile [unoptimized + debuginfo] target(s) in 22m 32s
test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 31.57s

web-check-final

svelte-check found 0 errors and 0 warnings

web-focused-final

 Test Files  5 passed (5)
      Tests  22 passed (22)

sidebar-focused-test

 Test Files  2 passed (2)
      Tests  25 passed (25)

bench-test

 2 pass
 0 fail

editor-check

svelte-check found 0 errors and 0 warnings

editor-focused-test

 Test Files  1 passed (1)
      Tests  6 passed (6)

editor-host first pass before the declaration-boundary import adjustment; latest retries did not execute tests (worker startup timeout)

 Test Files  1 passed (1)
      Tests  1 passed (1)

Focused production browser output (verbatim):

PASS 390 light (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits
PASS 390 dark (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits
PASS 820 light (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits
PASS 820 dark (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits
PASS 1440 light (source: live; target: live): cached body before revalidation, stable mount, focus handoff, Tab/Enter link, Copy link, Enter after mount without edits
PASS 1440 dark (source: live; target: live): cached body before revalidation, stable mount, focus handoff, Tab/Enter link, Copy link, Enter after mount without edits

Cleanup: cargo clean completed ( Removed 16729 files, 8.8GiB total). Generated web build, SvelteKit output and test temp directories were removed. Review artifacts remain gitignored in the worktree and attached above. The independent setup failure is #953. Matching release artifacts and VM latency measurements remain merge-round work; this report does not claim latency approval.

Round 2 final report Head: `bf74a5831a89392857c7f65c622f8236b6c02b1c`. Branch: `job/linknav-639`. The single authorized origin/dev merge is dce316bd6 (base c4faf184d, including 440e19dce). No push or deploy. The worktree is clean. Built: readable Note text and links paint before TipTap starts, with stable prose/heading geometry for the covered content. Stable `/n/` links retain Notes chrome and the selected explorer row. Native links keep focus across editor creation; the editable caret follows focused links so Chromium Tab reaches successive links and Enter follows without editing the Source body. The benchmark supports genuine pre-linknav artifacts. The 404 was reproduced with origin/dev Notes source. Parallel test Homes reused one User ID and shared the global writer lock. Each fixture now gets its own User ID. A held independent Home writer reproduces the old 404 and the old storm deadline failure on origin/dev; both pass on this branch. The original status, 12-second deadline and ETag assertions remain unchanged. Files: `crates/plugins/notes/src/lib.rs`; `apps/web/src/lib/notes/{NoteView.svelte,readPreview.ts,readPreview.test.ts,editorHost.ts,editorHost.svelte.test.ts,editor-types/index.d.ts}`; `apps/web/src/lib/{itemDisplayName.ts,itemDisplayName.test.ts,navigation.ts,navigation.test.ts}`; `apps/web/src/lib/components/app-sidebar.svelte`; `apps/web/e2e/linknav-639.mjs`; `packages/editor/src/{Editor.svelte,Editor.svelte.test.ts,extensions.ts,source.ts}`; `bench/tab-switch.mjs`. UX gaps closed: stable Note routes lost Notes chrome/explorer; editor mount lost link focus; Enter could edit the Note; Tab skipped subsequent inline links. The focused production browser regression checks real API data, cached paint while fresh GET is held, delayed editor creation, source and linked-target geometry, native input, Copy link clipboard bytes and unchanged Source bytes. UX gaps left: before editor creation, Tasks are a read-only projection and images show their alternate text. Rich-media, task and callout handoff geometry is outside the captured plain-prose/heading/link regression. Screenshots use macOS emulation in Chromium; real Safari/Mac interop belongs to the merge round. Decisions: use the existing shared Markdown parser and fixed escaped HTML projection with shared prose tokens; defer editor creation until after body paint. Add an optional display-only link DOM attribute hook for Notes and align selection-only caret state with native link focus; default editor consumers and public Guest settings remain unchanged. Use one test User per test Home. No dependencies or production API lookup semantics changed. Known gaps: the caret regression passed once before a type-boundary import adjustment. Its later fork and threads retries failed at worker startup before any test ran. The final web check and build passed. Startup API 503 evidence is filed separately as #953. Before/after cold/warm perf numbers are not available. The available shared release predates #639, and matching origin/dev and branch release artifacts were not supplied. The final verification policy assigns release builds and full suites to the merge round; no local latency figures replace the requested VM measurement. For the merge round: Run `cd apps/web && bunx vitest run src/lib/notes/editorHost.svelte.test.ts --maxWorkers=2` to confirm the final test import under the merge-round runner. Run `cd apps/web && bun run test --maxWorkers=2` to prove the full web suite. Run `bash tests/adversarial/run.sh` from the root to prove the combined XUser/authz/robustness matrices, including exact Notes path lookup. Full e2e, release builds, staging and Mac interop follow the shared merge-round policy. Set `LINKNAV_BEFORE_SERVER_BIN` and `LINKNAV_AFTER_SERVER_BIN` to release servers that embed the matching web artifacts. From the repo root, run: ```sh TAB_SWITCH_VM_LOCK_WAIT_SECONDS=14400 TAB_SWITCH_BUILD_BIN="$LINKNAV_BEFORE_SERVER_BIN" bun bench/tab-switch.mjs --linknav-only --linknav-before --linknav-runs 11 --conditions warm,cold --engines chromium --viewports 1440 --themes light --json artifacts/linknav-before.json TAB_SWITCH_VM_LOCK_WAIT_SECONDS=14400 TAB_SWITCH_BUILD_BIN="$LINKNAV_AFTER_SERVER_BIN" bun bench/tab-switch.mjs --linknav-only --linknav-runs 11 --conditions warm,cold --engines chromium --viewports 1440 --themes light --json artifacts/linknav-after.json ``` The harness runs the HDD profile on the perf VM, holds `/root/perf.lock` during each measured phase and records load inside the lock. Compare linked-Note cold/warm click-to-painted p50/p95 and route budget. There is no dedicated linknav value in the current baseline. Evidence: all six macOS-emulated conditions passed at 390/820/1440 in Light/Dark with live source and target editors. The ZIP has all 34 screenshots. Captures use the production bundle with the complete caret guard before its behavior-preserving extraction into editorHost; final extracted source check/build passed, and the helper regression passed before its declaration-boundary test import adjustment. Visual review remains with the orchestrator. Icon/text alignment was checked in the shared explorer/menu captures. - [linknav-639-final-macos-screenshots.zip](https://git.kayg.org/attachments/a711d327-931a-434b-9224-8a4579d04269) - [linknav-639-final-gates.zip](https://git.kayg.org/attachments/ce94377c-394f-4745-9bf8-fa84f6cf5734) - [390-light-copy-menu.png](https://git.kayg.org/attachments/ad835e1b-e0c0-4519-b0f6-f6de77462951) - [820-dark-link-focus.png](https://git.kayg.org/attachments/db9341ad-acb6-463d-bd99-18be7e6be8fe) - [1440-dark-link-hover.png](https://git.kayg.org/attachments/ae728c62-509c-4fbe-ba01-471ff4405437) `cargo fmt --check` exited 0 with no output. Gate output excerpts below are verbatim; complete logs and deliberately failing origin/dev controls are in the archive. The caret first-pass excerpt and later worker-startup failures are clearly separated. The latest caret retries did not execute tests. `notes-clippy` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 10m 56s ``` `tags-clippy` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 22.79s ``` `server-clippy` ```text Finished `dev` profile [unoptimized + debuginfo] target(s) in 9m 57s ``` `notes-test` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 38.94s test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 198.27s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.44s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `tags-test` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 45.11s test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 9.64s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `server-test` ```text Finished `test` profile [unoptimized + debuginfo] target(s) in 22m 32s test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 31.57s ``` `web-check-final` ```text svelte-check found 0 errors and 0 warnings ``` `web-focused-final` ```text Test Files 5 passed (5) Tests 22 passed (22) ``` `sidebar-focused-test` ```text Test Files 2 passed (2) Tests 25 passed (25) ``` `bench-test` ```text 2 pass 0 fail ``` `editor-check` ```text svelte-check found 0 errors and 0 warnings ``` `editor-focused-test` ```text Test Files 1 passed (1) Tests 6 passed (6) ``` `editor-host first pass before the declaration-boundary import adjustment; latest retries did not execute tests (worker startup timeout)` ```text Test Files 1 passed (1) Tests 1 passed (1) ``` Focused production browser output (verbatim): ```text PASS 390 light (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits PASS 390 dark (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits PASS 820 light (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits PASS 820 dark (source: live; target: live): cached body before revalidation, stable mount, focus handoff, touch link, Copy link, Enter after mount without edits PASS 1440 light (source: live; target: live): cached body before revalidation, stable mount, focus handoff, Tab/Enter link, Copy link, Enter after mount without edits PASS 1440 dark (source: live; target: live): cached body before revalidation, stable mount, focus handoff, Tab/Enter link, Copy link, Enter after mount without edits ``` Cleanup: `cargo clean` completed (` Removed 16729 files, 8.8GiB total`). Generated web build, SvelteKit output and test temp directories were removed. Review artifacts remain gitignored in the worktree and attached above. The independent setup failure is [#953](https://git.kayg.org/kayg/calternal/issues/953). Matching release artifacts and VM latency measurements remain merge-round work; this report does not claim latency approval.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
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#639
No description provided.