Skip to content

List tracked changes and refuse edits that would touch a pending suggestion - #10

Merged
lonetis merged 2 commits into
lonetis:mainfrom
JohannKaspar:wt/tracked-changes
Nov 9, 2026
Merged

lonetis merged 2 commits into
lonetis:mainfrom
JohannKaspar:wt/tracked-changes

Conversation

@JohannKaspar

Copy link
Copy Markdown
Contributor

Why

When an agent edits text that sits inside a collaborator's pending tracked change, the suggestion is silently rewritten or removed, and Overleaf emails its author that the suggestion was rejected. Neither the agent nor the user can see that this is about to happen: the edit tools only see plain text. This is the main thing keeping us from letting an agent work on a shared paper.

joinDoc {encodeRanges:true} already returns ranges.changes; this PR reads them.

What

  • List suggestions: listTrackedChanges (SDK), claudeleaf changes <project> <path> (CLI), overleaf_list_tracked_changes (MCP, read-only). Each entry has kind (insert / delete), text, offset/line/column, author (from the project's owner/members) and timestamp.
  • Guard on every text edit: before sending, submit re-joins the doc to get fresh ranges and checks the op against them (src/trackedChanges.ts, pure). If the op would touch a pending suggestion it throws TrackedChangeConflictError (with .changes) and nothing is sent.
    • Blocked: an insert strictly inside a tracked insertion or exactly on a deletion's anchor; a delete overlapping a tracked insertion or spanning a deletion's anchor.
    • Allowed: edits at the edges, so the neighbouring word stays editable; comment ops.
  • Override: EditOptions.overwriteTrackedChanges (SDK) and --overwrite-tracked-changes (CLI). The MCP tools deliberately have no override, so an agent has to ask a human.
  • The live compile test (break + restore main.tex) skips itself when main.tex has suggestions, since the restore cannot recreate them.

Things you may want to decide differently

  • The guard is on by default, which changes behaviour: edits that touch suggestions now fail, and each edit costs one extra joinDoc round trip. Happy to make it opt-in instead.
  • The edge rule (edits next to a suggestion are fine) is based on how Overleaf's change tracker shifts ranges; I checked it live, see below, but only with one account.

Tested

  • Unit tests for parsing and the conflict rules (test/trackedChanges.test.ts) and for the session paths (refuses and sends nothing, reads fresh ranges, override, comments unaffected).
  • Live on a throwaway premium project: suggestions listed with author and time; an edit inside a suggestion refused; deleting text next to a deletion anchor and replacing the word before an insertion allowed; direct inserts at both edges of a tracked insertion left its text unchanged; an insert inside it refused. npm test against that project: 135 passed, 1 skipped.
  • Not tested: a suggestion made by a second account.

Edits that change text inside a collaborator's pending suggestion silently
rewrite or remove it, and Overleaf mails the author that the suggestion was
rejected. joinDoc already returns ranges.changes; parse them, expose them
(listTrackedChanges, CLI 'changes', MCP overleaf_list_tracked_changes), and
check every text edit against fresh ranges before sending. The check throws
TrackedChangeConflictError; SDK/CLI can skip it with overwriteTrackedChanges,
the MCP server has no override so an agent asks a human.
Restoring the whole document cannot recreate tracked changes, so the test
would drop them; the overwrite guard refuses it. Record the live check of
edge inserts in CLAUDE.md.
@lonetis
lonetis merged commit 827554d into lonetis:main Nov 9, 2026
lonetis pushed a commit that referenced this pull request Nov 9, 2026
Releases what landed since 0.3.0:

- tracked changes: list a document's pending suggestions (SDK
  listTrackedChanges, CLI `changes`, MCP overleaf_list_tracked_changes),
  and refuse by default any edit that would touch one
  (TrackedChangeConflictError; override with overwriteTrackedChanges /
  --overwrite-tracked-changes) (#10)
- security: cookie-carrying REST requests stay on the Overleaf origin;
  @modelcontextprotocol/sdk ^1.32.2 and zod ^4, npm audit clean (#11)

A minor bump rather than 0.3.1: the default edit behaviour changes, and
0.3.1 was published and then unpublished on npm, so that number can
never be reused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016k8nQbcXiTztFcEngHqwxs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants