Add native content-control operations - #484
Merged
Merged
Conversation
JSv4
force-pushed
the
agent/issue-453-images
branch
3 times, most recently
from
August 14, 2026 11:11
90f7006 to
7664d14
Compare
JSv4
force-pushed
the
agent/issue-453-images
branch
from
August 14, 2026 11:39
7664d14 to
e767c70
Compare
JSv4
force-pushed
the
agent/issue-452-content-controls
branch
from
August 14, 2026 11:39
7c3a722 to
e1e90df
Compare
JSv4
changed the base branch from
agent/issue-453-images
to
agent/issue-455-structural-revisions
August 14, 2026 11:39
…mutation, order rPr Review fixes on the native content-control surface (#452). Repeating-section clones now freshen `w14:paraId` instead of refusing any item that carries one. Word 2013+ stamps a paraId on essentially every `w:p`, so the reject made the feature inert on real templates: HC031 carries 158 of them, while the only fixture exercising the clone path carries zero — which is why no test caught it. The clone mints package-unique values from a new `CommentOps.ParaIdAllocator`, extracted out of `NextParaId` so a detached subtree can mint several ids without aliasing, and kept as the single owner of paraId minting. `w14:textId` is copied verbatim: it is a hash of the paragraph's text rather than an identity, and Word emits the same value for two paragraphs with the same content — which is exactly what a clone is. `ListContentControls` now consults the session's tracked-change mode and the bookmark-removal gate, so `canMutate`/`unsupportedReason` agree with what a mutation actually does. Under `RenderInline` every control reported `canMutate: true` while every fill failed with `tracked_operation_unsupported`; an agent planning off the registry — and the MCP `list` action feeds exactly this — built a batch guaranteed to fail. Both gates are now shared predicates that discovery and `ResolveContentControlForMutation` read, closing two more instances of the divergence class the PR's three Harden commits each closed one of. `SetContentControlChecked` inserted `w:rFonts` at position 0 of a cloned `w:rPr`, which is schema-invalid whenever the glyph run already carries an earlier `CT_RPr` member (`w:ins`, `w:del`, `w:rStyle`, the move markers). Routed through the existing `WordprocessingMLUtil.InsertRPrChildInOrder`. Coverage: CC031-CC035 (paraId freshening with OpenXmlValidator, tracked-mode registry agreement across all nine ops, bookmark gate visible in discovery, rFonts schema slot, header/footer `ScopeIncluded`), an end-to-end `python/tests/test_content_controls.py` over all nine stdio-host route names, and `npm/tests/docx-session-content-controls.spec.ts` for the WASM bridge. Docs: the `[Unreleased]` CHANGELOG entry, the missing `### docxodus_content_controls` section plus the corrected tool count and batch-step list in `docx_agent_server.md`, and two corrected claims in `native_content_controls.md` — the SDT envelope is stricter than `CT_SdtPr` (both `w:sdtPr` and `w:id` are `minOccurs="0"`), and `w:lock` is honoured by content-control operations rather than by the generic anchor-addressed surface.
JSv4
changed the base branch from
agent/issue-455-structural-revisions
to
main
August 15, 2026 04:02
JSv4
marked this pull request as ready for review
August 15, 2026 04:02
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 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.
JSv4
added a commit
that referenced
this pull request
Aug 15, 2026
Brings the native-image branch up to date with main through PR #484 (native content controls). Three conflicts: - npm/src/types.ts and python/src/docx_scalpel/enums.py: additive EditErrorCode collisions, resolved by keeping every code from both sides. All three surfaces (the C# enum in DocxSession.cs, which stays the single owner, plus the two clients) now carry the same 68 codes. - docs/architecture/docx_mutation_api.md: main still described images as "still a v2 op, currently rejected", which this branch supersedes. Kept this branch's InsertImage bullet and main's content-control note. One semantic fixup beyond the textual conflicts: FillContentControlPicture called SweepOrphanedImages with main's three-argument signature. This branch narrowed that helper to a single argument because it no longer whitelists {r:embed, r:link, r:id} but scans every attribute value, so the call site drops the two attribute names. The resulting behaviour is strictly more conservative — it can only keep more media, never delete more.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #452
Summary
CanMutateshare the same repeating-section and picture-topology eligibility rules as mutation, including zero/multiple/linked images and live revision carrierssdtanchor/receipt lifecycleValidation
Stack
Base:
agent/issue-455-structural-revisions/ PR #485. This is the content-control layer immediately before transaction identity/idempotency (#449).