Collaboration
A review workspace for any document on the mesh. Comments and suggested edits are satellite
nodes stored beside the document ({doc}/_Comment/{id} and {doc}/_Tracking/{id}) — the document
text is never modified by commenting or suggesting; only an explicit Accept (or Restore)
writes to it. This plugin ships one dynamic NodeType, Collaboration/Review, plus the shared
Review Workspace instance.
The Review area (default)
Open /Collaboration/Workspace?doc=Space/Path (or pick a document on the page):
- Suggestions (legacy) — pending
_Trackingsatellites created by older flows (e.g. the AISuggestEdittool): author, kind, the proposed old → new text. Accept applies the suggestion to the live document — the range is re-resolved inside the owner's update lambda at accept time, so a document that moved since the suggestion was made can never be corrupted by a stale offset; a range that no longer exists refuses loudly instead of silently no-oping. Reject drops the satellite; the document is untouched. Accept needs Update permission on the document; Reject needs Update or authorship. - Comments — every
_Commentsatellite with Resolve / Reopen (Comment permission), Delete (Delete permission or own comment — the button only shows when the click can succeed), and an add-comment form. Comments are written as the signed-in user; the framework's satellite permission delegation is the authorization. - Satellites whose content cannot be read are shown as unreadable rows, never silently dropped
(content arrives typed, as
JsonElement, or from a foreign assembly — all three shapes are handled).
Track changes against a chosen version — computed from history, never stored
Tracked changes are a view model: they are recomputed from the version history every time the view renders, and nothing about them is ever persisted. The version history is already the authoritative record of every change, so there is no satellite state to drift, no anchors to go stale, and nothing to clean up. The TrackChanges area is the answer to "what changed since the version I care about?":
- Pick any historical version as the baseline — the picker lists the document's version
history (number, date, author);
?base=Ndeep-links a baseline. - See everything that changed since — an inline redline (insertions/deletions highlighted, fully HTML-encoded), a side-by-side Monaco diff, and one card per change attributed to the version (and author) that introduced it. Attribution walks the intermediate versions (bounded, the bound stated in the UI) and matches each change's text to the step that inserted or removed it; a change touched by several authors says "several edits" instead of guessing.
- Act on it:
- Revert (per change) — relocates the change by content against the live document at click time and puts the baseline text back. The revert is a normal versioned write, so it lands in the version history like any other edit — reverting every change reproduces the baseline exactly (pinned by the test suite), and a change that no longer exists refuses loudly instead of splicing the wrong range.
- Restore v — asks the document's hub to roll back to the baseline (core re-stamps a fresh top version; history is never rewritten).
Layout areas
| Area | Purpose |
|---|---|
Review (default) |
Suggestions + comments of the target document |
TrackChanges |
Baseline-version picker; redline + Monaco diff + attributed, individually revertible changes — all computed from version history |
Tests |
Runs the plugin's test suite and renders the pass/fail table (asserted in CI) |
Design notes
- Tracked changes are a view model. They are always computable from the version history, so
they are never stored. Only comments — genuinely additional data — persist as satellites.
(
_Trackingsatellites remain readable/actionable for legacy flows that still create them.) - Server-honest writes. Every document mutation (accepting a legacy suggestion, reverting a
change) resolves inside
GetMeshNodeStream(doc).Update(live => …), against the node the owner hands us — never against a render-time snapshot; reverts relocate their hunk by content. - Shape-tolerant reads everywhere. Every
MeshNode.Contentread goes throughContentAs<T>or an explicit typed/JsonElementswitch — never a bareispattern match (the silent-drop failure mode of core issues #189/#225). - No silent caps. The version picker shows at most 100 rows and says so; attribution walks at most 20 intermediate versions and says so; the diff degrades to a single whole-range hunk above 3000 tokens per side and the tests pin that it still reconstructs.