Files
Marcos Paulo 24c2c33dc7
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
docs: spec for comments, suggestions, mentions and the activity feed
Sidecar comments with quote-first anchors, CriticMarkup suggestions through the OT path, per-member feed; first three tasks sized for MiniMax. Written by MiniMax M3.1 Flash (OpenCode Zen free route) with web research notes; three owner questions open.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 18:04:00 -03:00

7.8 KiB

Research: comments, suggestions, mentions, activity feed

Browsed 2026-10-20. Primary sources only; anything unverified is marked.

1. What the repo already gives us (no new dependency needed)

  • OT over plain text: crates/collab (L1) exports TextOp, Component, diff, transform, transform_index, Selection, Client, Room, Presence (crates/collab/src/lib.rs:41).
  • Vault I/O: Vault::{read, write, write_private, list, delete_to_trash} (crates/vault/src/lib.rs:251).
  • Sync: every file in the vault is syncable; only .notescraft, .trash, .git, .obsidian are excluded (crates/sync/src/path.rs:30); a conflict becomes a copy (crates/sync/src/conflict.rs:12); merge3 merges three texts (crates/sync/src/merge.rs:30).
  • Command surface: CommandSpec + ParamSpec (crates/engine/src/spec.rs, crates/engine/src/params.rs:88), write gate crate::commands::writable (crates/engine/src/commands.rs:156), assistant gate lists DELETE_COMMANDS / WRITE_COMMANDS / READ_COMMANDS (crates/engine/src/ai/assistant_gate.rs:21,60,147).
  • Members of a vault: GET /v1/vaults/{vault}/members (apps/notescraft-sync-server/src/api/routes.rs:53), client ShareClient::members returning Member { user_id, name, color, role } (crates/engine/src/share/client.rs:82,244), command share.members (crates/engine/src/share/commands.rs:60).
  • Audit log: GET /v1/audit (apps/notescraft-sync-server/src/api/routes.rs:48), table apps/notescraft-sync-server/src/store/audit.rs.
  • Server-Sent Events: parser SseParser (crates/engine/src/live/sse.rs), server stream apps/notescraft-sync-server/src/live/events.rs; the live route is per note path (.../api/routes.rs:82).
  • Markdown: parse, Document::replace_range, block_ids (crates/markdown/src/lib.rs:242,299,347).
  • UI: right card tabs RightTab (crates/ui-egui/src/panels/right_view.rs:103), phone pages CompactPage (crates/ui-egui/src/shell/compact/mod.rs:29), phone sheet SyncSheet (crates/ui-egui/src/panels/sync_sheet/mod.rs:88).

2. CriticMarkup (the suggestion layer)

3. Obsidian plugins: three real storage shapes

  • Document Comments (kylemcd): comments live inside the Markdown as HTML comments, with an anchor pair <!--c:id-->text<!--/c:id--> and a thread block <!--co:id by:kyle at:… status:open quote:"text"-->. Threaded replies, resolve/reopen, reactions, Markdown in bodies; HTML comments are hidden by Markdown renderers, so Obsidian shows a clean note. https://github.com/kylemcd/obsidian-document-comments
  • Marginalia (Tsuuuuuuun): comments live in sidecar JSON files, one per note, never touching the .md. Anchoring is a 3-stage match (line hint -> context-scored search -> fuzzy match), and comments whose text vanished become orphaned and are shown in their own filter. https://github.com/Tsuuuuuuun/obsidian-marginalia
  • Obelisk: comments in the frontmatter, and the rule that matters: "The quote is the anchor. A line number is never an anchor" - the exact quote is searched in the note, the line number is only a tiebreak between two identical quotes. https://obelisk.typeworks.dev/

4. Notion

5. Google Docs / Drive

6. Word / OOXML

7. Anchor drift

8. Mentions and activity feeds

9. What we take from it

  1. The quote is the anchor; line and offset are hints that may go stale (Obelisk, Google Docs).
  2. A comment that cannot be re-anchored must become orphaned and stay visible with its body (Marginalia, GitHub "outdated").
  3. CriticMarkup is right for suggestions (a pending edit of the text) and wrong for threads (no ids, no resolve state, no overlapping) - so the two layers need different storage.
  4. Mentions store the user id, never the display name (Slack, Notion).
  5. HTML comments inside the .md are the Obsidian-compatible inline trick, but they put the discussion inside the note text, which is exactly what the OT room and the publish projection would have to strip.