Repository navigation
Conversation
`replace --track` / `edit --at pN:S-E --track` whose match lay inside another author's pending <w:ins> left the replacement bare inside that insertion, attributing it to the other author. When the insertion also held a nested <w:del> before the match, the span was rebuilt from the first overlapping run's offset instead of the container's start, so the wrong text was deleted while 1 replacement was still reported. The span engine (replaceSpanInParagraph, shared by replace, replace --batch and edit spans) now splits a revision wrapper that wholly holds a tracked span within its own container, the way Word does: the cut's <w:del> nests in the surrounding insertion, the replacement gets the editor's own <w:ins>, and the trailing half of the insertion keeps its author/date with a freshly allocated w:id. Container offsets come from where the container starts. Nested revisions and markers that can't be sliced move whole to the side they fall on, never into the new <w:del>. Related span-engine fixes found in review: - a tracked replacement inside a hyperlink/smartTag gets its own <w:ins> - cut link/field text stays a <w:del> instead of being dropped untracked - an empty span (pN:S-S) lands at the point, tracked, instead of being appended untracked at the paragraph end - no empty phantom <w:del> for an insertion point inside a run The multi-tracked fixture gains a cross-author paragraph (p3). Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GoxD36rnNfg6uArkVxtJL
|
🤖 docx-cli triage Opened this for #13. A tracked replace inside another author's pending insertion now splits that insertion and puts the new text in the editor's own Action needed: approve/merge — decide the same-author split question and whether the three gaps become follow-up issues, then merge #15. Generated by Claude Code |
Same author merges: a tracked span replace/edit wholly inside the
tracked author's OWN pending <w:ins>/<w:moveTo> (exact w:author match,
nearest enclosing revision) no longer splits it into del + new ins. As
in Word, the cut is removed outright and the replacement joins the
existing insertion (same w:id/date); an insertion emptied by the edit
is dropped. Spans crossing out of it, and other authors, keep the split
shape unchanged.
Views follow the whole revision chain: the reader classified a run by
its innermost wrapper only, so a deletion nested in another author's
insertion showed in read --baseline although rejecting that insertion
removes it. TrackedChange gains `within` (the enclosing revision) and
one predicate, isRevisionVisible, decides visibility for text.ts/wc,
find, iterateBlocks({view}) and the markdown renderer, matching the XML
offset walkers. read --baseline/--accepted now equal reject/accept --all.
Found in review: nested links/smart tags/insertions inside a split
wrapper are split recursively (the match inside them was left behind);
an untracked insertion point at another author's revision edge lands
beside it; revN pairs only same-author del+ins; fenced code blocks
honor the accepted/baseline view; RunRevision schema covers break/tab/
text-box runs.
Triage-Routine: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GoxD36rnNfg6uArkVxtJL
Review fixes for the cross-author span replace: - The span engine (now src/core/find/replace-span/: target, level, rebuild, split, replacement-runs) records each run's FULL wrapper path and rebuilds at the container above the OUTERMOST revision on the span's path under tracking, splitting every wrapper below. The editor's <w:ins> is never a descendant of another revision: a span in a link/smart tag inside another author's insertion, or in an insertion nested in a deletion, no longer lands inside it. A link the span sat in is re-created around the replacement. - Cut text under ANY enclosing deletion stays as is (the flag now propagates through nested wrappers) instead of being re-deleted. - Same-author merge keys on the nearest revision on the shared path, so a span crossing a link inside the own insertion still merges. - An insertion point between two runs of a link lands inside the link with its style; at a link's edge it still lands outside. - set/clear formatting, hyperlinks add and note references skip a wrapper by its accepted-view VISIBLE length (a deletion nested in an insertion counted, so edit --bold after a cross-author replace hit the wrong text); hyperlink/note placement now uses the accepted-view offsets find reports. - One view rule: parser/revision-view.ts (isRevisionKindVisible, isWrapperVisibleInView, sumVisibleTextLength) — the AST's isRevisionVisible and every XML walker derive from it; the private copies in comments/markers.tsx are gone. - read --baseline/--accepted filter breaks, tabs and images by the revision chain too (a break in a rejected insertion still rendered). - track-changes list: a record's text includes nested revisions' text, so another author's insertion emptied by an edit lists as "withhold" (contains tc1) instead of "¶ paragraph mark" (now only a real paragraph-mark marker); --json drops the reader's within chain. revN pairs only a same-author, same-date del+ins neither nested in the other. Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GoxD36rnNfg6uArkVxtJL
…dation Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GoxD36rnNfg6uArkVxtJL
Triage-Routine: true Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GoxD36rnNfg6uArkVxtJL
|
🤖 docx-cli triage Closing at the maintainer's request. Three review rounds on this ~1,300-line rewrite each surfaced ~15 further issues, several of them regressions against Generated by Claude Code |
🤖 docx-cli triage
What was wrong
replace "disputed amount" "disputed sum" --track --author Editoron text inside Reviewer A's pending<w:ins>left "disputed sum" bare inside A's insertion, so it was credited to A. When A's insertion also held Reviewer B's nested<w:del>, offsets slid 24 characters: "The Client may " was deleted instead of the match. Both cases exited 0 with1 occurrence replaced.edit --at pN:S-E --trackshares the same code path (replaceSpanInParagraph) and had the same bug.What changed
<w:del>stays nested in A's insertion, "disputed sum" goes in Editor's own<w:ins>between the halves, and the trailing half keeps A's author/date with a new uniquew:id, which is how Word records it. Offsets count from the wrapper's start, and B's deletion is moved whole, untouched.<w:ins>, and cut link text becomes a tracked<w:del>.pN:S-S) used to be appended untracked at the end of the paragraph. They now land at the point, tracked.src/core/find/replace-span.tsx(a ~595-line rewrite), a newisTrackedChangeWrapperhelper insrc/core/parser/run-ops.ts, a filter option onwrapContiguousTrackable, and a notes section insrc/core/track-changes/CLAUDE.md.multi-tracked.docxgets a cross-author p3 (Editor edits inside Reviewer A's still-pending insertion). Two tracked-change count assertions intrack-changes.test.tswent up to match; tc0–tc5 are unchanged.How it works
flowchart TD A[match span in paragraph] --> B{tracked and span inside one ins/del wrapper?} B -- no --> C[paragraph-level del + ins, as before] B -- yes --> D[split wrapper at span end; new w:id on tail half] D --> E[wrap matched plain runs in Editor w:del, still inside head half] E --> F[insert Editor w:ins between halves]Follow-up commit (maintainer decisions)
<w:ins>/<w:moveTo>, a span wholly inside it is edited in place: the cut text is removed, and the new text joins that insertion (samew:id). This matches Word editing your own pending insertion. Different authors keep the split shape.--baseline= all changes rejected: runs now record their enclosing revision (withinin the AST), and a run is hidden in a view if any revision around it is. Read,wc,find, the markdown renderer anditerateBlocks({view})all shareisRevisionVisible.revNpairing: a del and an ins now pair only when both are by the same author.--accepted/--baseline.Verification
<w:ins 1 A>may withhold a disputed sum.</w:ins>, with no del and one revision.read --baseline→ "The Client ", identical totrack-changes reject --all;read --accepted→ "The Client may withhold a disputed sum."--baseline→ "Fees are due. ", identical toreject --all;--accepted→ "Fees are due. The Client may withhold disputed sum."read --baseline≡reject --allandread --accepted≡accept --allfor del-in-ins and ins-in-del docs, pluswc/find --baseline.bun run check✅bun run test:unit✅ (1733 tests).Decisions (settled)
--baselineshows the document with every change rejected.w:idand corrupts text) and Whole-paragraph edit --text --track silently resolves other authors' pending changes #17 (whole-paragraphedit --text --trackresolves other authors' changes).Not verified
<w:moveTo>(a tracked move) hasn't been checked against Word.Fixes #13
🤖 Generated with Claude Code
https://claude.ai/code/session_012GoxD36rnNfg6uArkVxtJL
Generated by Claude Code