Skip to content

Add intrinsically isolated mutation batch previews - #481

Merged
JSv4 merged 4 commits into
mainfrom
agent/issue-446-isolated-preview
Aug 15, 2026
Merged

Add intrinsically isolated mutation batch previews#481
JSv4 merged 4 commits into
mainfrom
agent/issue-446-isolated-preview

Conversation

@JSv4

@JSv4 JSv4 commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • run atomic or explicit best-effort previews on a complete isolated session clone, never on the live package or its history
  • return predicted versions, per-step receipts, semantic revision/comment/annotation deltas, deterministic package-content hashes, warnings for generated metadata, and optional scoped/full HTML
  • carry the same preview contract through Ops/JSON, WASM/npm, Python/stdio, and MCP while preserving legacy batch aliases
  • preserve mutable settings, revision/transaction generators, and the original diff baseline without sharing caches or undo/redo state
  • validate isolation with normal and anchor-persisting OPC payload maps, intentionally ignoring ZIP entry timestamps/compression metadata in line with GetPackageContentHash

Validation

  • original relevant .NET suites: 99 passed
  • final preview suite: 8/8
  • adjacent atomic-batch suite: 24/24
  • DS462 repeated isolation stress: 25/25
  • final complete stacked solution suite: 3,708 passed, 3 skipped, 0 failed
  • Python atomic-batch tests: 4 passed
  • npm typecheck passed
  • Release WASM publish passed
  • real Chromium batch tests: 4 passed
  • worktree and diff checks clean

Stacked on the atomic-batch foundation in #479.

Closes #446

@JSv4
JSv4 force-pushed the agent/issue-446-isolated-preview branch from 1f73500 to 4c2a993 Compare August 14, 2026 09:49
@JSv4
JSv4 force-pushed the agent/issue-445-atomic-batches branch 2 times, most recently from 502aba8 to 503f0ed Compare August 14, 2026 10:04
@JSv4
JSv4 force-pushed the agent/issue-446-isolated-preview branch from 4c2a993 to a179037 Compare August 14, 2026 10:04
@JSv4
JSv4 force-pushed the agent/issue-445-atomic-batches branch from 503f0ed to 17b3efe Compare August 14, 2026 11:11
@JSv4
JSv4 force-pushed the agent/issue-446-isolated-preview branch from a179037 to 3151a7a Compare August 14, 2026 11:11
JSv4 added 2 commits August 14, 2026 06:33
… sentinels

Cross-surface review findings on the isolated-preview work (#446).

Preview HTML had two definitions. The typed core, stdio, Python and MCP rendered
through HtmlConversionOps with comments, annotations, notes and headers/footers
on; npm routed its shadow through DocxSessionOps.RenderHtml — the EDITOR's
authoring profile — where comments and annotations are off and headers/footers
follow pagination. The same batch previewed from a browser and from an agent
therefore described materially different documents. The profile now has exactly
one owner, HtmlConversionOps.PreviewDocumentOptions()/PreviewBlockOptions(),
which the core consumes directly and the browser reaches through two new bridge
exports, RenderPreviewHtml and RenderPreviewBlockHtml. Scoped preview diverged
the same way and additionally rendered with tracked changes off — a redline
preview that hid its own redlines.

The npm client raised the annotation execution-metadata warning for any added
annotation while .NET requires a created timestamp; npm now uses the .NET
predicate.

packageHash used "" as its unavailable sentinel on every surface, so a
preview.packageHash == applied.packageHash replay assertion passed vacuously
when neither could be hashed. It is null/None now and serializes as JSON null.

Receipt change sets compared entries with CLR equality while npm compared their
serialized wire objects; both sides now compare the serialized projection, the
shape the transports actually publish.

Docs and enum ripple the feature had not carried: PreviewBatch/previewBatch/
preview_batch in docx_mutation_api.md, npm/README.md and python/README.md
(including its Lifecycle table), and MutationPreviewHtmlMode as a Python enum.

Two costs are documented rather than changed, because both are public-API
decisions rather than defects: receipt enrichment runs unconditionally on the
apply path as well as the preview path, with no opt-out, and a preview pays a
package clone plus a second open package on top of it; and the typed
PreviewBatch overload is isolated only insofar as a callback addresses the
shadow session it is handed, which its doc comment now states.

Tests: DS469 pins revision classification against pre-existing revisions — the
blind spot that made this code hard to reason about — DS470 pins the preview
HTML profile to the facade rather than the editor render, DS471 pins the null
packageHash on the wire, plus Python wire-decode and enum coverage.
@JSv4
JSv4 changed the base branch from agent/issue-445-atomic-batches to main August 15, 2026 02:31
@JSv4
JSv4 marked this pull request as ready for review August 15, 2026 02:31
# Conflicts:
#	python/README.md
#	python/tests/test_atomic_batches.py
#	tools/python-host/Dispatcher.cs
@JSv4
JSv4 merged commit ddb14d9 into main Aug 15, 2026
11 of 13 checks passed
JSv4 added a commit that referenced this pull request Aug 15, 2026
Brings the structural tracked-revision work up to date with the five sibling
PRs from the same stack that landed on main while this branch sat still:
#479 atomic batches, #481 isolated preview, #477 introspection, #480
links/bookmarks and #482 images.

One file conflicted, Docxodus.Tests/McpServerDispatcherTests.cs, and only
because both sides appended tests at the same point. Both sides' tests are
kept in full; none was dropped, weakened or merged together. The sole real
collision was the test number MCP099, claimed independently by main's
MCP099_BatchedTableStep_KeepsTableAnchorMappingInItsReceipt (from d558c00)
and by this branch's tracked-changes batch test. Main's numbering is left
untouched because it is already merged and referenced, so this branch's test
takes the next free number instead:

  MCP099_TrackChangesBatchPreviewIsIsolatedAndAtomicApplyResolvesRevision
  -> MCP146_TrackChangesBatchPreviewIsIsolatedAndAtomicApplyResolvesRevision

MCP102 and MCP103 do not collide with anything on main and keep their
numbers.

The batch changeset comparison converged as intended: this branch's f761e49
and main's 983c5bf made the same serialized-projection change, and the merged
tree holds exactly one such comparison with no record-equality remnant. The
comments part added to OwnedPartRelationships.StoryParts by 96f4c2f survives,
and both sides' CHANGELOG entries accumulated under a single [Unreleased].

Verified on the merged tree: the library and the test project build with 0
errors, the full suite is 3712 passed / 0 failed / 3 skipped, and the Release
builds of tools/mcp-server and tools/python-host are clean. Test-attribute
counts are exactly additive across the merge (Fact 2195, Theory 173,
InlineData 1545 = base plus main's additions plus this branch's), confirming
no test was lost.
JSv4 added a commit that referenced this pull request Aug 15, 2026
Bring the native content-control work up to date with main after PRs #479,
#481, #477, #480, #482 and #485 landed.

CHANGELOG.md was the only conflict: both sides added an entry at the top of
[Unreleased]. Resolved by keeping both, main's #455 structural tracked
revisions first and this branch's #452 native content-control operations
second; the rest of main's list is unchanged.

Two semantic fixes a clean textual merge would have hidden:

- Renumber this branch's MCP content-control tests from MCP146/147/148 to
  MCP147/148/149. Main's #485 independently claimed MCP146 for
  MCP146_TrackChangesBatchPreviewIsIsolatedAndAtomicApplyResolvesRevision in
  the same file, so both sides had defined an MCP146.

- docs/architecture/docx_agent_server.md: correct the tools/list count from 17
  to 19 (the catalog now registers 19 tools, three lifecycle plus sixteen
  read/grouped-intent), add docxodus_track_changes to the docxodus_mutations
  batch-step list so it matches the catalog enum and the dispatcher's accepted
  set, and add images/content-controls to the grouped-intent enumeration.
JSv4 added a commit that referenced this pull request Aug 15, 2026
Brings the native-image branch up to date with main (PRs #479, #481,
#477, #480, #482, #485).

Two conflicts, both additive-on-both-sides EditErrorCode collisions,
resolved by keeping every code from both sides:

- npm/src/types.ts: kept the eight image codes from this branch and
  main's unresolved_structural_revision.
- python/src/docx_scalpel/enums.py: same resolution.

The C# EditErrorCode enum in DocxSession.cs auto-merged and remains the
single owner; all three surfaces now carry the same 58 codes.
JSv4 added a commit that referenced this pull request Aug 15, 2026
Brings the branch up to date with main, which had advanced by 20
commits (PRs #479, #481, #477, #480, #482, #485 and #484).

CHANGELOG.md was the only conflict: both sides appended to the same
[Unreleased] / ### Added region. Resolved by keeping every entry from
both sides, with this branch's idempotent mutation transaction entry
ahead of main's accumulated entries. Main's block is otherwise
unchanged, and the historic "[Unreleased] - .NET 8" heading further
down the file is untouched.

EditErrorCode gained four transaction codes on this branch and image
and revision codes on main; the merged enum retains all of them, and
the npm and Python mirrors carry this branch's four. The enum has no
explicit ordinals and is serialized by name, so the reordering #485
introduced does not affect the wire.

This PR remains MCP-transport-only: DocxSessionOps, DocxSessionJson
and the WASM bridge are byte-identical to main after the merge.
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.

[Agent] Replace apply-and-undo preview with an isolated dry run

1 participant