Resolve wiki-style links and embeds in imported Notes #856

Open
opened 2026-10-02 16:08:14 +00:00 by kayg · 9 comments
Owner

Parent: #719

Evidence: In the generated Wiki-link corpus, Guide.md contains note links with aliases, heading and block references, and an image embed. The real-server WebDAV Home indexed all six files and three Notes, but the backlinks API returned zero for Notebook.md and references/Field Notes.md. Opening Guide.md kept its bytes and ETag unchanged, while its inline image did not load (naturalWidth was zero).

Expected: resolve supported Wiki-style note links, aliases, headings, block references and image embeds. Show a clear broken-link state when a target is missing. Keep this work grouped under one resolver change.

Parent: #719 Evidence: In the generated Wiki-link corpus, `Guide.md` contains note links with aliases, heading and block references, and an image embed. The real-server WebDAV Home indexed all six files and three Notes, but the backlinks API returned zero for `Notebook.md` and `references/Field Notes.md`. Opening `Guide.md` kept its bytes and ETag unchanged, while its inline image did not load (`naturalWidth` was zero). Expected: resolve supported Wiki-style note links, aliases, headings, block references and image embeds. Show a clear broken-link state when a target is missing. Keep this work grouped under one resolver change.
Author
Owner

Starting work on job/links-856 at 4a871b383864dad0d6c87bcd22b7f0d589e3506e (local origin/dev is f06679b11cde29cc0b7120fdab5f721389caf695). I read the binding repo docs and issue. I am tracing the shared Notes link scanner/index first, then I will bring over only tests/dropmd_719/ from job/dropmd-719; the Files folder-identity changes remain with #627.

Starting work on `job/links-856` at `4a871b383864dad0d6c87bcd22b7f0d589e3506e` (local `origin/dev` is `f06679b11cde29cc0b7120fdab5f721389caf695`). I read the binding repo docs and issue. I am tracing the shared Notes link scanner/index first, then I will bring over only `tests/dropmd_719/` from `job/dropmd-719`; the Files folder-identity changes remain with #627.
Author
Owner

Finding: store::index already resolves wiki and path references into note_link_refs with anchors and resolution state, but both backlinks_for_path and the linked-Notes queries still read note_links. That older projection only inserts resolvable path destinations, so resolved Wiki links never feed backlinks or “Used in”. The shared Rust path resolver also does not decode %20 before matching an imported filename, while the web path helper does. The editor Markdown reader preserves wiki syntax as literal text, so imported links and wiki image embeds do not render inline. Evidence: the existing route queries in crates/plugins/notes/src/lib.rs, note_link_index_rows_from_note in crates/calternal-notes-core/src/notes_index.rs, and the imported wiki-links corpus.

Finding: `store::index` already resolves wiki and path references into `note_link_refs` with anchors and resolution state, but both `backlinks_for_path` and the linked-Notes queries still read `note_links`. That older projection only inserts resolvable path destinations, so resolved Wiki links never feed backlinks or “Used in”. The shared Rust path resolver also does not decode `%20` before matching an imported filename, while the web path helper does. The editor Markdown reader preserves wiki syntax as literal text, so imported links and wiki image embeds do not render inline. Evidence: the existing route queries in `crates/plugins/notes/src/lib.rs`, `note_link_index_rows_from_note` in `crates/calternal-notes-core/src/notes_index.rs`, and the imported `wiki-links` corpus.
Author
Owner

The first cargo test -p calternal-plugin-notes run exposed a migration registration error: this branch already uses Notes migrations 0016–0024, while the new schema change was named 0016 and was not added to the explicit MigrationSet. Evidence: 58 passed, 111 failed; ordinary Note writes returned the generic 500. I moved the migration to 0025 and registered it. I am rerunning the focused link-index regression before continuing.

The first `cargo test -p calternal-plugin-notes` run exposed a migration registration error: this branch already uses Notes migrations 0016–0024, while the new schema change was named 0016 and was not added to the explicit `MigrationSet`. Evidence: 58 passed, 111 failed; ordinary Note writes returned the generic 500. I moved the migration to 0025 and registered it. I am rerunning the focused link-index regression before continuing.
Author
Owner

After the required merge from origin/dev, the generated API contract was stale: the Notes link-reference operation was absent, and three existing Notes/Mail summaries from origin/dev were missing. CALTERNAL_SERVER_BIN="$CARGO_TARGET_DIR/debug/calternal-server" bash packages/api-client/check-generated.sh regenerated the contract and exited 1 on that diff. I committed the updated OpenAPI and TypeScript output in 5d280a97a; I will rerun the check against the committed output.

After the required merge from origin/dev, the generated API contract was stale: the Notes link-reference operation was absent, and three existing Notes/Mail summaries from origin/dev were missing. `CALTERNAL_SERVER_BIN="$CARGO_TARGET_DIR/debug/calternal-server" bash packages/api-client/check-generated.sh` regenerated the contract and exited 1 on that diff. I committed the updated OpenAPI and TypeScript output in `5d280a97a`; I will rerun the check against the committed output.
Author
Owner

The first real-server browser run exposed a Backlinks gap: after uploading the Wiki-link corpus, the Notebook.md referrer count was 0; the corpus source contains both [[Notebook]] and ![[Notebook]]. The browser run stopped at that assertion before the inline view check. I am inspecting the shared Index response and will fix the resolver or the settling condition based on that evidence.

The first real-server browser run exposed a Backlinks gap: after uploading the Wiki-link corpus, the `Notebook.md` referrer count was 0; the corpus source contains both `[[Notebook]]` and `![[Notebook]]`. The browser run stopped at that assertion before the inline view check. I am inspecting the shared Index response and will fix the resolver or the settling condition based on that evidence.
Author
Owner

Browser e2e finding for #856: three production-browser runs of the generated wiki-links corpus did not produce the expected shared link projection. In the latest run, the Backlinks API returned zero rows for wiki-links/Notebook.md, the Guide link-reference API returned [], and the rendered backlink rows were empty after the runner's index-settling wait. I am checking the Files-to-Notes adoption trace before changing the implementation; the browser case is retained as the regression check.

Browser e2e finding for #856: three production-browser runs of the generated `wiki-links` corpus did not produce the expected shared link projection. In the latest run, the Backlinks API returned zero rows for `wiki-links/Notebook.md`, the Guide link-reference API returned `[]`, and the rendered backlink rows were empty after the runner's index-settling wait. I am checking the Files-to-Notes adoption trace before changing the implementation; the browser case is retained as the regression check.
Author
Owner

Resolved #856 browser finding: the Notes link API resolved both folder-qualified targets, but the production editor rendered only the three Notebook links. The shared Wiki parser returns a heading/block anchor separately from the target name; the UI matcher compared only the target name against the server's full raw reference. The matcher now includes the parsed anchor, with focused tests for heading and block stable routes (ee1d0227b).

Resolved #856 browser finding: the Notes link API resolved both folder-qualified targets, but the production editor rendered only the three Notebook links. The shared Wiki parser returns a heading/block anchor separately from the target name; the UI matcher compared only the target name against the server's full raw reference. The matcher now includes the parsed anchor, with focused tests for heading and block stable routes (`ee1d0227b`).
Author
Owner

Further User-flow finding for #856: the mouse-followed Note embed now reaches the stable /n/<id> route. A keyboard-focused Wiki link did not activate because Enter was handled as an editable-surface newline. The link surface now routes Enter through the same Notes link handler and includes Wiki anchors in the tab order. bun run check and the focused Notes tests pass; the production-browser regression is rerunning.

Further User-flow finding for #856: the mouse-followed Note embed now reaches the stable `/n/<id>` route. A keyboard-focused Wiki link did not activate because Enter was handled as an editable-surface newline. The link surface now routes Enter through the same Notes link handler and includes Wiki anchors in the tab order. `bun run check` and the focused Notes tests pass; the production-browser regression is rerunning.
Author
Owner

#856 complete on job/links-856.

Head: eab58f28b2b0f533e63720ab754bb63bda6957c4.

Built

  • Extended the existing Notes Index with one read-only resolver for Wiki links, Wiki embeds, relative Markdown links, percent-encoded destinations, heading anchors and ^block anchors. Resolution uses source-relative candidates and stable Note or Files identities.
  • Added a per-Note links API. Backlinks and Files “Used in” use the same indexed references, including Note embeds.
  • Added accessible inline rendering for resolved Notes, file links, image embeds and broken links. ProseMirror decorations leave Markdown and Yjs content unchanged. Keyboard Enter uses the same navigation callback as pointer input.
  • Added generated #719 corpus coverage, API contract output, focused adversarial probes and a route profile for a 128-reference Note.

Files

  • Resolver and Index: crates/calternal-notes-core/src/links.rs, notes_index.rs; crates/plugins/notes/src/lib.rs, store.rs, migration 0025_link_path_alternates.sql.
  • UI and API: apps/web/src/lib/notes/{inlineLinks.ts,inlineLinks.test.ts,editorHost.ts,api.ts,NoteEditorSurface.svelte,NoteView.svelte}, contracts/openapi.json, packages/api-client/src/generated.ts.
  • Coverage and profile: tests/dropmd_719/{corpora.py,test_corpora.py,run_intake.mjs}, tests/adversarial/{attack.py,xuser_matrix.py}, apps/web/e2e/route-perf.mjs, docs/perf/README.md.

Gates

Verbatim output excerpts:

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

cargo clippy -p calternal-notes-core --all-targets -- -D warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.20s

cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings
Finished `dev` profile [unoptimized + debuginfo] target(s) in 6m 01s

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

cargo test -p calternal-plugin-notes
 test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 515.34s
 test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.66s

cargo test -p calternal-server
 test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 41.95s

calternal-notes-core tests passed: 522 unit tests, 19 log_rewrite, 5 parser_properties, 7 unicode_titles, 12 vector_parity; no failures. Corpus generator tests: 3 passed.

bun run check ended with:

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.
svelte-check found 0 errors and 0 warnings

Focused UI tests:

Test Files  2 passed (2)
     Tests  10 passed (10)

bun run build succeeded (✓ built in 30.89s; adapter-static wrote the site to build). It printed existing Rolldown use client directive warnings.

Production real-server E2E output:

wiki-links webdav: {"collections":3,"collectionFailures":[],"files":6,"failedFiles":[]}; 6 Files, 3 Notes, 1 Tasks
Wrote 1 real-server cases to /home/kayg/Developer/calternal-wt/links-856/artifacts/links-856/report.json

The User flow confirmed resolved Note and folder-heading links, a visible broken link, a Note embed, a loaded image, an encoded PDF link, Backlinks and Used in, and unchanged Note bytes and ETag. The screenshots emulate macOS. Six Note screenshots cover 390, 820 and 1440 px in light and dark:

cargo clean removed 16,483 files (9.0 GiB); apps/web/build and generated Python cache were removed. The working tree is clean.

UX gaps closed

  • Resolved, broken and embedded links now have working primary actions and stable destinations.
  • Keyboard activation follows the same navigation path as pointer activation; visible targets are sized for touch.
  • Opening and indexing imported links do not write to the source Note.

UX gaps left / known gap

  • One browser folder-upload E2E run returned HTTP 412 for Guide.md while the destination changed. The #856 production flow was verified through WebDAV import, then exercised as a User in the browser. The folder-picker race is outside this link resolver change.
  • Full bun run test, the merge-round e2e set and the full adversarial matrices were not run in this job, per the verification policy. The focused #856 production E2E and API probes are included above.
  • No performance measurement was run because #856 is not a performance issue. The route profile was extended for the merge-round/perf review.

Decisions

  • Relative links first resolve from the source Note’s folder; alternate candidates handle title/path collisions without changing the source text.
  • Markdown percent escapes are decoded for lookup only. Stored bytes stay as imported.
  • Read-only decorations render references so normal Note viewing never triggers a write.
  • The generated corpus was imported by WebDAV for the strict #856 browser proof after the separate folder-picker 412.

For the merge round

  • cargo clippy --all-targets -- -D warnings and cargo test from the workspace root: prove combined-workspace gates after all branches merge.
  • cd apps/web && bun run test: prove the full web unit suite on the combined branch.
  • bash tests/adversarial/run.sh: run the full XUser, authorization and robustness round, including the new Notes links endpoint probes.
  • Run the merge-round browser E2E set against the combined production build; #856’s focused real-server User flow already passed here.
#856 complete on `job/links-856`. Head: `eab58f28b2b0f533e63720ab754bb63bda6957c4`. ## Built - Extended the existing Notes Index with one read-only resolver for Wiki links, Wiki embeds, relative Markdown links, percent-encoded destinations, heading anchors and `^block` anchors. Resolution uses source-relative candidates and stable Note or Files identities. - Added a per-Note links API. Backlinks and Files “Used in” use the same indexed references, including Note embeds. - Added accessible inline rendering for resolved Notes, file links, image embeds and broken links. ProseMirror decorations leave Markdown and Yjs content unchanged. Keyboard Enter uses the same navigation callback as pointer input. - Added generated #719 corpus coverage, API contract output, focused adversarial probes and a route profile for a 128-reference Note. ## Files - Resolver and Index: `crates/calternal-notes-core/src/links.rs`, `notes_index.rs`; `crates/plugins/notes/src/lib.rs`, `store.rs`, migration `0025_link_path_alternates.sql`. - UI and API: `apps/web/src/lib/notes/{inlineLinks.ts,inlineLinks.test.ts,editorHost.ts,api.ts,NoteEditorSurface.svelte,NoteView.svelte}`, `contracts/openapi.json`, `packages/api-client/src/generated.ts`. - Coverage and profile: `tests/dropmd_719/{corpora.py,test_corpora.py,run_intake.mjs}`, `tests/adversarial/{attack.py,xuser_matrix.py}`, `apps/web/e2e/route-perf.mjs`, `docs/perf/README.md`. ## Gates Verbatim output excerpts: ```text cargo fmt --check (exit 0; no output) cargo clippy -p calternal-notes-core --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 14.20s cargo clippy -p calternal-plugin-notes --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 6m 01s cargo clippy -p calternal-server --all-targets -- -D warnings Finished `dev` profile [unoptimized + debuginfo] target(s) in 26m 27s cargo test -p calternal-plugin-notes test result: ok. 169 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 515.34s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.66s cargo test -p calternal-server test result: ok. 107 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 41.95s ``` `calternal-notes-core` tests passed: 522 unit tests, 19 `log_rewrite`, 5 `parser_properties`, 7 `unicode_titles`, 12 `vector_parity`; no failures. Corpus generator tests: 3 passed. `bun run check` ended with: ```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. svelte-check found 0 errors and 0 warnings ``` Focused UI tests: ```text Test Files 2 passed (2) Tests 10 passed (10) ``` `bun run build` succeeded (`✓ built in 30.89s`; adapter-static wrote the site to `build`). It printed existing Rolldown `use client` directive warnings. Production real-server E2E output: ```text wiki-links webdav: {"collections":3,"collectionFailures":[],"files":6,"failedFiles":[]}; 6 Files, 3 Notes, 1 Tasks Wrote 1 real-server cases to /home/kayg/Developer/calternal-wt/links-856/artifacts/links-856/report.json ``` The User flow confirmed resolved Note and folder-heading links, a visible broken link, a Note embed, a loaded image, an encoded PDF link, Backlinks and Used in, and unchanged Note bytes and ETag. The screenshots emulate macOS. Six Note screenshots cover 390, 820 and 1440 px in light and dark: - [390 px, light](https://git.kayg.org/attachments/bc0b268c-aed6-456b-b1ee-deaf2309a1db) · [390 px, dark](https://git.kayg.org/attachments/73d47d6b-872d-424c-a16c-89521c288a5d) - [820 px, light](https://git.kayg.org/attachments/e564f7a1-da09-4ffe-9662-2373cef892d9) · [820 px, dark](https://git.kayg.org/attachments/5a0ddaed-e41c-4923-a331-1bc4b0ed626e) - [1440 px, light](https://git.kayg.org/attachments/2094f48e-fcef-4376-ac3f-2d8192562a2b) · [1440 px, dark](https://git.kayg.org/attachments/dcaceca3-496e-4cc5-aa13-ece26201515d) `cargo clean` removed 16,483 files (9.0 GiB); `apps/web/build` and generated Python cache were removed. The working tree is clean. ## UX gaps closed - Resolved, broken and embedded links now have working primary actions and stable destinations. - Keyboard activation follows the same navigation path as pointer activation; visible targets are sized for touch. - Opening and indexing imported links do not write to the source Note. ## UX gaps left / known gap - One browser folder-upload E2E run returned HTTP 412 for `Guide.md` while the destination changed. The #856 production flow was verified through WebDAV import, then exercised as a User in the browser. The folder-picker race is outside this link resolver change. - Full `bun run test`, the merge-round e2e set and the full adversarial matrices were not run in this job, per the verification policy. The focused #856 production E2E and API probes are included above. - No performance measurement was run because #856 is not a performance issue. The route profile was extended for the merge-round/perf review. ## Decisions - Relative links first resolve from the source Note’s folder; alternate candidates handle title/path collisions without changing the source text. - Markdown percent escapes are decoded for lookup only. Stored bytes stay as imported. - Read-only decorations render references so normal Note viewing never triggers a write. - The generated corpus was imported by WebDAV for the strict #856 browser proof after the separate folder-picker 412. ## For the merge round - `cargo clippy --all-targets -- -D warnings` and `cargo test` from the workspace root: prove combined-workspace gates after all branches merge. - `cd apps/web && bun run test`: prove the full web unit suite on the combined branch. - `bash tests/adversarial/run.sh`: run the full XUser, authorization and robustness round, including the new Notes links endpoint probes. - Run the merge-round browser E2E set against the combined production build; #856’s focused real-server User flow already passed here.
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#856
No description provided.