Problem
Mentioning a person and linking a document currently overlap in ways that make both actions harder to understand. Accounts can exist without home documents, home documents can be mistaken for people, and similarly named accounts can be difficult to distinguish. Account aliases introduce another identity mismatch: the author of an older comment may resolve to a newer account ID, causing relevant people to lose their conversational ranking.
The writer needs suggestions that reflect what they are doing: replying to someone, participating in a thread, or starting a conversation about a document. Raw account IDs and generic search results offer little help. Prioritizing the writer's own account makes reply suggestions less useful.
Contacts, petnames, recent visits, publication/comment activity, and site permissions already provide useful context, but need consistent handling across desktop, web, and mobile web. The old Contacts page is no longer an accessible management surface; the project should make profiles and Following useful without rebuilding that page.
Solution
1. Clear mention types and destinations
Input | Results | Appearance | Destination |
|---|---|---|---|
| Accounts, with or without a home document |
| Account |
| Published documents, home docs included | Document title, no | Latest version |
Prefix account labels in the editor, published/read-only content, comments, and resolved Markdown output. Do not double-prefix a name that already starts with @. The prefix is presentation, not a change to account identity.
A document mention captures a verified current published version and includes the URL's latest flag. Clicking resolves the latest known head rather than pinning navigation to the captured version; it does not rewrite stored content. Deliberately pinned references remain pinned.
Resolve home-document existence explicitly. A profile-only account must not become a document result.
Do not insert an unpublished, deleted, or inaccessible document as an account fallback. If no verified published version is available, keep the picker open with a retryable error. Previously verified cached versions may be used for transient connectivity failures, not to bypass deletion or permission failures.
2. One shared system with distinct context
Extend the existing suggestion plugin and XState menu controller rather than copying the plugin. Share candidate retrieval, ranking, trigger/range handling, and insertion across document editors and comment/reply editors.
Keep these context values separate:
Acting account: the selected identity writing the document or comment. Supplies contacts, petnames, and self-exclusion. Changing identities refreshes personalization.
Target site: the publishing space of the document being edited or discussed. Supplies site relevance and applicable permissions. It is not necessarily the hosting gateway or the mentioned account.
Reply thread: the original comment and its replies, plus the direct reply parent. Supplies participant relevance.
Document authors: the authors credited on the document version being discussed. Supplies document-conversation relevance; this extension is planned, not yet implemented.
If context is unknown, omit that signal rather than invent it. Missing local storage or optional activity data must not prevent otherwise valid suggestions.
3. Text-first, context-aware ranking
Show one ordered list per mode, not fixed Contacts/Recents/Profiles groups.
Match accounts against public names, the acting account's petnames, and identifiers; match documents against titles and paths.
Rank by text quality: exact, prefix, remaining token/substring match, then fuzzy fallback. An empty query has one shared tier. Context never promotes a weaker text match ahead of a better match.
Within each tier, blend local recency, public activity, site relevance, contacts, and conversation context.
Deduplicate accounts by resolved canonical identity and documents by space/path, independent of version. Break remaining ties by display label and canonical identity.
The current baseline score is:
2 × visitRecency + 1.5 × activityRecency + sameSite + issuedContact + threadBoost
Visit recency has a seven-day half-life; activity recency has a fourteen-day half-life.
Missing timestamps contribute zero; future timestamps are clamped to now for scoring.
issuedContact applies only to accounts and reflects a contact issued by the acting account, including legacy contacts.
Account activity includes publications/edits and comments. Document activity includes publication/editing, not merely receiving comments.
The direct reply author receives a thread boost of 5; other participants receive 3. These are alternatives, not cumulative boosts for the same person.
These weights are a transparent starting policy, not an empirically optimal ranking. Strong recency can outweigh a site/contact boost; a participant boost is not an absolute ordering guarantee within a text tier.
Replies: prioritize the conversation, not the writer
Include the thread's original author and accounts that have replied, deduplicated by identity.
Give the direct reply author the strongest conversational boost; boost other participants as well.
Exclude the selected acting account from reply account results, not merely from the boost. This applies when replying to oneself and when participating elsewhere in the same thread. Searching one's own name does not override the exclusion in replies.
Resolve known aliases before matching participation, direct-reply identity, or self-exclusion. Preserve the requested source account ID alongside the resolved candidate ID. Gather these relationships before deduplicating results so arrival order cannot erase thread context.
Aliases do not create separate people in the list. A mention still navigates to the resolved profile. Never merge accounts merely because their names match.
Use already-loaded thread comments where possible; do not fetch the complete conversation on each keystroke.
Thread context affects account suggestions only. It does not promote participant-owned documents or remove the acting account's documents from [[ results.
The implemented self-exclusion is reply-specific. Account mentions outside replies remain unchanged, except for the planned new-thread rule below.
New document conversations: prioritize document authors — planned
Comment context | Additional account relevance | Helper text |
|---|---|---|
New root thread on a document | Boost the document's credited authors |
|
New root thread on a particular block | Apply the same document-author boost initially |
|
Reply in an existing thread | Keep direct-reply and participant boosts stronger than the document-author boost | Prefer |
Use the authors credited on the document version being discussed, not everyone who has ever edited the document. Do not silently substitute the latest version when the conversation concerns a historical version.
Exclude the acting account, including aliases, from these new-thread account suggestions as well.
Authorship is not ownership or permission: a site owner, site editor, or document editor is not automatically a document author. Only show the label when supported by author metadata.
Keep text matching first. Document authorship is a relevance signal, not a restriction on who can be mentioned.
Reuse loaded document author metadata. If it is unavailable, fall back to ordinary ranking without guessing.
Combine authorship with activity when known, for example Published 2h ago · Document author.
The numeric author weight and whether overlapping author/participant boosts stack remain to be settled during this extension. Preserve the agreed hierarchy of conversational boosts and cover it with examples before release.
4. Helpful, factual result subtitles
Account rows show an avatar, preferred name, and a concise second line. Use meaningful activity and relationship information instead of always displaying a shortened ID.
Context | Label | Meaning |
|---|---|---|
Latest publication or comment |
| Known event, not online presence |
Direct reply parent |
| Person being answered |
Original thread author |
| Root-comment author |
Other participant |
| Has replied in this conversation |
Credited document author (planned) |
| Authored the discussed version |
Site-wide writer permission |
| Recursive root permission |
Narrower writer permission |
| Exact or home-document grant |
Site owner |
| Owns the target space |
Site subscription |
| Subscription, not membership |
Contact issued by the acting account |
| Includes legacy contacts; not necessarily following |
Local profile visit |
| This device's history |
Prefer activity followed by the most relevant relationship, for example Replied 2m ago · Replying to or Published 2h ago · Site editor. Prefer conversation labels over generic site/contact labels when applicable.
Use Commented for the thread author's activity; do not describe an initial root comment as a reply. Other participants' comments in that thread can use Replied.
When activity is unknown, show a supported relationship or visit hint. Missing activity is not evidence of inactivity.
Show the public name secondarily when it differs from the acting account's petname; search both.
Use the shortened account ID only when no useful subtitle is available. Retain the full ID as a tooltip for disambiguation. Do not render empty separators.
Document rows identify the title and space/path, with a Home document hint where appropriate and known publication activity when available.
5. Fast, accessible interactions and motion
Desktop uses an anchored autocomplete; mobile uses a dedicated keyboard-aware picker. Both use the same filtering, ordering, identity semantics, and insertion operation. Identical inputs should produce identical ordering; device-local visit histories can legitimately differ.
Recognize triggers from text input/transactions, including IME composition, not only keydown.
Support triggers at the start of a block or after whitespace/opening punctuation; do not activate inside email addresses, existing links, or code.
Activate document mode after the second [ and consume adjacent closing ]] on insertion when present.
Arrow keys navigate, Enter selects a current valid result, and Escape cancels. Loading/empty menus must not cause an accidental comment submission. Preserve the highlighted identity during refresh where possible.
Cancellation preserves typed text. Undo restores the replaced trigger/query in one editing step. Reject stale results and invalid replacement ranges rather than inserting elsewhere.
Mobile also provides separate Mention account and Link document toolbar actions. Use mode-specific headings, placeholders, touch-friendly rows, and loading/error/empty states. Preserve the mapped editor range while searching in the dialog and restore focus after selection or cancellation.
Render cached suggestions promptly and debounce remote text search by 150 ms. Keep usable cached content visible while refreshing. Context changes must not allow an earlier query to overwrite the current results.
Failures stay inside the picker with an explicit Retry action. Do not automatically retry mention searches or stack global error toasts. Clear the previous error while retrying or changing the query.
Keep no-results visible with a useful message rather than closing at a query-length threshold.
Motion, implemented with the existing stack:
Desktop/web: a 180 ms fade, small translation, and scale from approximately 97% to 100%, with subtle overshoot. Exit is a quicker 100 ms fade and reverse movement. Motion follows the actual popover placement.
Mobile: a gentle slide and fade, not a zoom of the full-screen picker.
Animate show/hide, not every keystroke or result refresh. Preserve the final rows while the menu exits instead of flashing an empty state.
Selection and focus restoration must not wait for animation completion. Closing UI must not accept stale selections.
Respect reduced-motion preferences, including preference changes while the app is open.
Use Tippy, CSS, and the existing Radix dialog infrastructure. No new animation dependency is required.
6. Contacts, petnames, and Following
Manage contacts through profiles and Following. Neither restore nor remove the old Contacts page.
For another account followed by the currently selected profile-viewing identity, double-click the name or activate the accessible pencil control to edit its petname inline.
Provide check/save and X/cancel; Enter saves and Escape cancels. Blur does not silently save.
An empty trimmed name clears the petname without unfollowing. Preserve contact identity and subscription settings; do not create duplicate contacts.
Disable repeat submissions while saving. Retain the input on failure with a retryable error. Reset/abort editing when the selected identity changes, and refresh profile, Following, contact, and mention-derived caches after saving.
Explain that petnames are published contact metadata, not private local notes.
Unfollowed profiles offer Follow first. Own-profile naming remains in the existing profile-editing flow.
Following displays the selected viewer's applicable petnames and opens profiles. Viewing another person's Following list must not adopt that person's petnames as the viewer's own.
7. Data, performance, and compatibility
Use the shared MentionCandidates request and existing search, account, contact, capability, activity, and document-info services; do not introduce a new backend index.
Return typed account/document candidates with identity, source identity for aliases, display names, site/contact information, explicit role hints, activity timestamps, and verified published versions for documents.
Use raw search result types to distinguish profiles from documents. Verify document heads with GetDocumentInfo, rather than trusting a historical version from a search hit.
Combine matching accounts with contacts, site-relevant accounts, recent profile IDs, active accounts, and thread participant seeds. The author extension adds credited document authors as a contextual source.
Combine matching documents with recent document IDs, recently published/edited documents, and relevant directory entries. Permission revocation must not continue to confer a role boost.
Cap responses at 100 candidates and metadata enrichment at eight concurrent operations. Cache graph lookups and reuse queries across keystrokes. Do not load entire histories or directories per search.
Seed discovery is currently capped at 20 IDs: direct reply author, thread author, recent participants, then local recents. Larger threads are not exhaustively loaded into the initial picker; additional participants receive boosts if discovered by other sources. Identity-resolution seeds may include the acting account, but reply results must not.
Existing backend capability-list pagination does not enforce the requested page-size bound. Treat this as a known limitation, not a guarantee of bounded backend work.
Keep the existing local recents infrastructure and 20-entry retention policy. Record profile visits separately from document/home-document visits; deduplicate without versions or panel parameters. Preserve clear/delete behavior.
Keep visit timestamps device-local; only bounded entity IDs are sent as seeds. Storage failures are nonfatal.
Persist optional Embed.attributes.mentionKind (account or document) alongside the URL, with matching editor data and rich clipboard HTML. Preserve it through block conversion, publishing, reload, copy/paste, previews, rendering, bookmarks, and exports. No protobuf migration is required for the annotation attribute.
New account mentions use explicit profile references without document versions.
New document mentions use canonical document references, a verified published version, and the latest flag; strip search-result block fragments. Do not apply legacy root-to-profile URL rewriting to document insertion.
Unmarked legacy mentions retain their existing interpretation. Do not infer and persist new discriminators merely by opening an old draft, or rewrite old pinned references.
Preserve existing citations and account-mention notifications. Explicit home-document mentions must not become person notifications. Ranking never automatically inserts mentions or notifies thread participants.
Deploy reader/schema support before enabling new writers. Older readers can discard or reject unfamiliar attributes; verify compatibility rather than assuming it.
Research references retained from the original project: Obsidian internal links and W3C combobox interaction guidance.
Scope
Delivery state and sequence
This is the agreed product specification, not a claim that every requirement has shipped. The local worktree contains implementation of the two modes, reference discriminator, profile petnames, recents, activity/role hints, thread boosts, alias-aware matching, reply self-exclusion, account prefixes, and picker motion. Production rollout and live acceptance remain separate gates.
Phase | Status | Remaining delivery gate |
|---|---|---|
Reference contract and shared editor/candidate foundation | Implemented locally | Verify reader compatibility and end-to-end publishing/navigation |
Contacts, petnames, recents, and activity/role hints | Implemented locally | Verify real account data, permissions, identity switching, and storage failures |
Reply context, alias-aware relevance, and self-exclusion | Implemented locally | Verify actual threads and aliased accounts through the running application |
Account prefixes and picker motion | Implemented locally | Validate real mobile soft keyboards, focus, cancellation, and reduced motion |
Document-author relevance for root threads and replies | Planned extension | Wire version-specific authors, settle scoring, add root/block/reply and self-exclusion tests |
Release | Pending acceptance | Complete integration checks and deploy readers before writers |
No delivery date or remaining-effort estimate has been agreed. Estimate the author extension after verifying the loaded version-specific author metadata; schedule release around the acceptance gates below rather than treating the existing local changes as a completed deployment.
The author extension depends on the shared identity/ranking contract. Author-context plumbing and regression fixtures can proceed alongside independent live/mobile validation. Keep changes scoped to existing modules; this is not a new editor framework or contact-management redesign.
Acceptance criteria
@ never offers documents; [[ never offers profile-only accounts. Home documents render and navigate as documents.
Account mentions show one presentation prefix and open profiles. Captured document versions and latest survive round-trips; a later publication opens on click without changing the stored reference. Old pinned references remain pinned.
Exact matches outrank weaker contextual matches; scores, missing-data behavior, and tie-breaking are deterministic.
Thread authors/participants are recognized across aliases and candidate arrival order. Unrelated same-name accounts remain distinct. The selected account is absent from reply suggestions, including when replying to itself; switching identity updates the exclusion. Its documents remain eligible.
Planned author relevance works for new root comments, block-specific root comments, and replies, using the discussed version's credited authors. Ownership/permissions do not masquerade as authorship. The selected author is excluded from these new-thread suggestions, and conversational boosts remain stronger in replies.
Subtitles accurately distinguish publications, comments, replies, authorship, roles, following, contacts, and visits. Missing hints do not produce dangling separators or imply inactivity.
Keyboard, IME, selection, cancel, undo, and stale-response behavior work across document/comment editors. Mobile insertion and cancellation restore the correct range without submitting the comment accidentally.
Motion runs on open/close only, keeps exit content stable, respects reduced motion, and does not delay interaction.
Petname save, clear, cancel, failure, follow prerequisites, subscription preservation, and identity changes behave as specified. Own-profile editing remains unchanged.
API registration and request serialization are tested through the actual HTTP handler, not only mocked candidates. A stale desktop main process must not be mistaken for a current backend; verify which workspace serves the API and restart it when server/schema changes require it.
Verification and release gates
Run relevant client/shared/editor/UI and application tests, pnpm typecheck, formatting checks, and dependency audit through direnv exec .. Run the frontend Agent CI workflow before pushing, following repository instructions. Report unrelated test failures and audit findings separately; do not describe partially passing checks as fully green.
Use the editor Playwright harness at http://localhost:5180 for controlled document/comment interactions. Check Jean run environments before starting a server. The harness uses mocked candidates: it does not establish live account identity, activity, publication, or routing correctness. Validate those against a real application/daemon, using the local CLI to inspect comments and account resolution when needed.
Manually verify iOS Safari and Android Chrome with real soft keyboards; viewport emulation is not enough. Target cached suggestion availability within 100 ms, measured separately from the 180 ms entrance animation. Measure cold-query latency separately rather than promising a network-dependent bound. Avoid unbounded work per keystroke and ensure stale responses never replace the current results.
The original planning note that direnv was blocked is obsolete: repository commands have since run successfully. The release gates are current verification and deployment compatibility, not that historical environment blocker.
Rabbit Holes
Block-level authorship attribution. A comment on a block initially uses document authors, not inferred authors of particular text ranges.
Exhaustive contributor history, full-thread discovery, or a new backend relevance index. Use bounded retrieval and credited authors on the discussed version first.
Tuning numeric relevance weights without realistic fixtures or observed user problems. Author-weight/stacking policy needs a small explicit decision, not a generalized ranking framework.
Broad account-alias migrations. Preserve identity relationships in candidate matching without rewriting historical comments or overriding canonical profile navigation.
Spring-driven result reordering, animated height measurement, or a new animation library. Show/hide motion is the agreed scope; do not animate every search update.
Rebuilding the Contacts page or introducing another contact-management surface instead of improving profiles and Following.
No Gos
No mixed account/document picker, duplicated autocomplete plugin, or platform-specific ranking policy.
No self-suggestions in reply account results or in the planned document-root comment suggestions; no name-based identity merging. Do not globally remove self-mentions from unrelated document editing.
No account-to-home-document navigation for new account mentions, or home-document-to-profile rewriting for explicit document mentions.
No drafts, document creation inside the picker, heading/block search, or unpublished-document insertion.
No changes to deliberately pinned references, automatic migration of existing content, or unsupported claims of older-client compatibility.
No synchronized browsing history or transmission of local visit timestamps. No claim that published petnames are private notes.
No automatic mentions, participant notifications, or new notification features based on ranking.
No generic “member” label inferred solely from following, no site-wide editor label for an exact document grant, and no authorship label inferred solely from permissions.
No unbounded per-keystroke history traversal, repeated global error toasts, animation-delayed selection, or timeout workarounds for editor focus.
No automatic publication of this Markdown draft or git commit/push as part of updating the project document.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime