Skip to content

[Markdown Agent] Apply document edits deterministically - #2967

Merged
George Ng (GeorgeNgMsft) merged 2 commits into
georgengmsft-markdown-workspace-createfrom
georgengmsft-markdown-safe-updates
Sep 4, 2026
Merged

[Markdown Agent] Apply document edits deterministically#2967
George Ng (GeorgeNgMsft) merged 2 commits into
georgengmsft-markdown-workspace-createfrom
georgengmsft-markdown-safe-updates

Conversation

@GeorgeNgMsft

@GeorgeNgMsft George Ng (GeorgeNgMsft) commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What changed

This PR adds the raw-Markdown edit engine that later persistence layers can call. It does not yet change where documents are saved or add browser synchronization.

Before

A model can return several edits whose character offsets were all calculated from the same original Markdown. The previous application path processed those edits sequentially against an already-mutated string, so an early edit could move the text targeted by every later edit.

For example, starting with ABCDE:

  1. insert X at offset 1
  2. delete original range [3, 5) (DE)

Applying those operations in request order produces AXBCDE, then deletes offsets [3, 5) from that changed string (CD), yielding the incorrect result AXBE.

The previous path could also reverse multiple inserts at one position, silently clamp invalid ranges, and apply intersecting edits whose meaning became ambiguous after the first mutation.

After

applyDocumentOperations treats the operations as one edit batch against one original Markdown snapshot:

  • validates every character offset against the original text
  • rejects intersecting operations before changing anything
  • applies non-overlapping edits from the highest offset to the lowest, so edits later in the document cannot shift locations still waiting to be changed
  • preserves request order for multiple inserts at the same position
  • converts structured content into Markdown for insert/replace operations
  • supports adding and removing strong, emphasis, inline-code, and link marks

For the example above, the delete at [3, 5) runs first and removes DE; the insert at 1 then produces the intended AXBC.

This follows the established simultaneous text-edit model used by language tooling. The LSP contract describes text edits against a document range, while Microsoft’s reference Node implementation sorts edits and applies them from the end toward the beginning, rejecting overlap:

Why this is a separate stack layer

The original replacement change combined edit semantics with file persistence, stale-write protection, and browser synchronization. Isolating the operation engine makes its coordinate contract and edge cases reviewable before later layers use it for durable updates.

Deferred upward: durable workspace persistence, revision and binding conflict checks, streaming exactly-once persistence, view-service state, browser synchronization/autosave, same-file editor rebinding, and loopback-only binding.

Validation

  • cached TypeScript 5.4.5 strict no-emit check passed for markdownOperationSchema.ts and documentOperations.ts
  • direct compiled smoke tests passed for base-relative edits, same-position inserts, overlap rejection, mark add/remove, balanced-parenthesis links, and mismatched-link preservation
  • cached Prettier 3.5.3 changed-file check passed
  • git diff --check passed

The normal package build/Jest suite and PR ratchets could not run because this worktree had no node_modules; the single time-boxed restore did not complete while reaching unavailable Azure feed artifacts. The commit hook was bypassed after the equivalent cached Prettier, type, and smoke checks above.

@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-markdown-safe-updates branch from c3b1974 to 2424a5c Compare September 3, 2026 22:41
@GeorgeNgMsft George Ng (GeorgeNgMsft) changed the title fix(markdown): apply document edits deterministically [Markdown Agent] Apply document edits deterministically Sep 4, 2026
@GeorgeNgMsft
George Ng (GeorgeNgMsft) marked this pull request as ready for review September 4, 2026 05:58
Comment thread ts/packages/agents/markdown/src/agent/documentOperations.ts
Comment thread ts/packages/agents/markdown/src/agent/documentOperations.ts
Comment thread ts/packages/agents/markdown/src/agent/documentOperations.ts Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: dd89289c-6602-452d-b8c5-9221494be8f7
@GeorgeNgMsft
George Ng (GeorgeNgMsft) force-pushed the georgengmsft-markdown-safe-updates branch from ef02d96 to 333ee62 Compare September 4, 2026 21:57
@GeorgeNgMsft
George Ng (GeorgeNgMsft) added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit 64464af Sep 4, 2026
25 of 44 checks passed
Hillary Mutisya (hillary-mutisya) pushed a commit to hillary-mutisya/TypeAgent that referenced this pull request Sep 10, 2026
## Stack

Layer 3 of the replacement stack for microsoft#2940.

- Base: `main` (microsoft#2966 and microsoft#2967 are merged)
- Parent layer: microsoft#2967 (merged)

## Included

- Persists headless Markdown updates directly to the authorized
workspace file.
- Makes streaming completion idempotent by recognizing the
already-produced revision instead of applying operations twice.
- Carries immutable binding identity and SHA-256 revision checks across
read/apply.
- Rejects stale tokens, rebound root/path/file identities, revision
conflicts, and roots replaced by symlinks or junctions.
- Adds request IDs to concurrent document read/apply IPC and correlates
responses.
- Keeps the view service server-authoritative for update persistence
even when no browser/SSE client is connected.

## Deferred to browser layer

- Browser `document-manager.ts` rewrite and serializer/autosave
behavior.
- Primary/secondary SSE lifecycle, browser synchronization/promotion UX,
and reopening the same file while preserving browser binding state.
- Broader browser snapshot adoption and loopback binding behavior.

## Validation

Historical results from before the rebase (not rerun as full build/Jest
validation):

- `@typeagent/markdown-agent` TypeScript and frontend build
- Focused Jest suites: 44 tests passed across Markdown persistence,
operation engine, path policy, creation path policy, and action handler
coverage
- Prettier changed-file check
- Git diff check
- Repo lint ratchet

After rebasing onto `f3e4308ea2cd722a2905eceac3f2a1e61d19a420`: 23
focused Node integration smoke checks passed using the actual handler,
persistence module, and operation engine with simulated translator/view
IPC; TypeScript 5.4.5 syntax parsing and Git diff checks passed. Full
build/Jest, semantic typechecking, and repository ratchets were not
rerun: this worktree has no installed dependencies (`fluid-build`
missing). Pinned Prettier 3.5.3 was unavailable offline.

Complete layer diff against the new main base: 992 insertions, 532
deletions (1,524 changed lines), across 6 files.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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