Skip to content

fix(docx): report LibreOffice timeout as an error and verify the output - #1792

Open
TINGyu123644 wants to merge 2 commits into
anthropics:mainfrom
TINGyu123644:fix/docx-accept-changes-timeout
Open

TINGyu123644 wants to merge 2 commits into
anthropics:mainfrom
TINGyu123644:fix/docx-accept-changes-timeout

Conversation

@TINGyu123644

Copy link
Copy Markdown

What changed

skills/docx/scripts/accept_changes.py now returns an Error when soffice times out instead of reporting success, and only claims success after checking that the output DOCX no longer carries revision marks (w:ins / w:del / w:moveFrom / w:moveTo) in word/*.xml parts.

Why

Defect 1 in #1770: soffice operates in place on a copy and the macro stores/closes the document itself, so a timeout leaves the output in an unknown state (possibly untouched, possibly partly processed) while the caller was told "Successfully accepted all tracked changes". __main__ keys on "Error" in message, so the process also exited 0.

How verified

LibreOffice is not installed in this environment, so the fix is verified with mocked subprocess.run plus real DOCX fixtures:

  • Timeout (subprocess.TimeoutExpired): returns an Error mentioning the timeout, never "Successfully".
  • returncode == 0 but the output still contains <w:ins>: returns "tracked changes remain in output document".
  • returncode == 0 with a clean output DOCX: still reports success.
  • _docx_still_has_tracked_changes: True for each revision marker (w:ins, w:del, w:moveFrom, w:moveTo) and for a non-zip file, False for a clean DOCX.
  • python -m py_compile clean; no new third-party imports (zipfile is stdlib).

The macro-setup path (_setup_libreoffice_macro) is untouched.

accept_changes returned "Successfully accepted all tracked changes" when
soffice timed out, leaving the output in an unknown state. Timeouts now
return an Error, and success is only claimed after checking that the
output DOCX no longer carries revision marks. Addresses defect 1 in anthropics#1770.
@98zc5g5jyw-arch

Copy link
Copy Markdown

Reviewed at head ad42dbab (base 34040c9). LibreOffice is not installed here either, so I re-ran the mocked-subprocess.run two-state checks with locally generated DOCX fixtures:

  • ✅ Timeout no longer reports success: with subprocess.run patched to raise TimeoutExpired, base returns "Successfully accepted all tracked changes …" (and since __main__ keys on "Error" in message, it also exits 0 — defect 1 of docx/pdf/xlsx: six defects in the document skills (verified at 34040c9) #1770), while head returns "Error: LibreOffice timed out while accepting tracked changes…".
  • ✅ Postcondition catches the intended case: a stub that converts nothing leaves the output carrying <w:ins>; head reports "Error: tracked changes remain in output document", base still claims success.
  • ✅ A genuinely clean output still reports success. python -m py_compile clean; the diff is single-purpose (+24/−1, zipfile reads only — no new network/subprocess surface).

One fix needed — the marker match is prefix-based and flags clean documents. "<w:ins" is a substring of <w:insideH, <w:insideV and <w:instrText, so any converted document that contains table inside borders or field codes (TOC / PAGE) is reported as still carrying revision marks:

word/document.xml: …<w:tblBorders><w:insideH w:val="single" w:sz="4"/>…</w:tblBorders>   (no revisions)
_docx_still_has_tracked_changes(...)   → True   # expected False
full flow (soffice stubbed, rc=0)      → "Error: tracked changes remain in output document"   # expected success

Same with <w:instrText xml:space="preserve"> PAGE </w:instrText>. ("<w:del" is benign here — w:delText only appears inside deletions.) Suggested: match the element boundary instead of the bare prefix, e.g. import re + re.search(r"<w:(ins|del|moveFrom|moveTo)[\s/>]", data). I ran that against the same four fixtures: <w:ins w:id=…> still matches; w:insideH / w:instrText / clean do not. A regression test for both states (marker present → error; insideH/instrText only → success) would pin it.

Otherwise the direction is right — fail on timeout, verify the postcondition before claiming success.

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is tied to the literal w: prefix, but OOXML namespace prefixes aren't semantic. A valid revision serialized as <x:ins ...> with the WordprocessingML namespace will be missed and the output reported clean. Could this parse the XML and match namespace URI + local name instead of raw prefixes?

@98zc5g5jyw-arch

Copy link
Copy Markdown

Cross-check on the tightening suggestions for _docx_still_has_tracked_changes, run at head ad42dbab against five small DOCX fixtures (clean / table <w:insideH> borders / <w:instrText> field code / literal <w:ins> / namespace-aliased <x:ins xmlns:x="…wordprocessingml…">):

  • Current marker check: correct on clean and <w:ins>, but false positive on <w:insideH> / <w:instrText> (clean documents) and false negative on the aliased <x:ins>.
  • Boundary regex (from my earlier comment): fixes both false positives; still misses the aliased case.
  • Namespace-aware parse — match {ns}ins|del|moveFrom|moveTo over each word/*.xml part, e.g. via defusedxml.ElementTree (already used by comment.py in this skill): correct on all five.

So the parse approach raised in the latest review covers both failure classes. If you take it, please keep the current fail-closed default (except → True, so an unreadable/unparseable output still errors instead of passing) and pin both cases in tests: clean insideH/instrText → success; literal or aliased revision marker → error.

Match revision marks (ins/del/moveFrom/moveTo) by WordprocessingML
namespace URI + local name instead of the literal w: prefix, so
documents that bind the namespace to a different prefix are still
detected as containing tracked changes.
@TINGyu123644

Copy link
Copy Markdown
Author

Thanks for the review! I've updated _docx_still_has_tracked_changes to match revision marks by WordprocessingML namespace URI + local name instead of the literal w: prefix.

It now parses each word/*.xml part with xml.etree.ElementTree and looks for ins/del/moveFrom/moveTo in the http://schemas.openxmlformats.org/wordprocessingml/2006/main namespace, so a document that serializes the same namespace with a different prefix (e.g. <x:ins>) is still detected. Verified locally against six fixtures: clean docs, standard w:ins, non-w: prefixed x:ins/x:moveFrom, a clean doc with a non-w: prefix, and an unrelated-namespace del (all pass).

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rechecked current 54b1981. The namespace-prefix issue I raised is resolved: tracked-change elements are now parsed by WordprocessingML namespace URI plus local name, so aliased prefixes are detected while clean elements such as insideH/instrText are no longer false positives. Parse failures still fail closed. No remaining blocker from my review.

@TINGyu123644

Copy link
Copy Markdown
Author

Hi @sylvesterkaczmarek — just a quick check-in: the namespace-URI rewrite has been approved and the branch is up to date with main. Is there anything else needed before this can be merged (e.g. a rebase or an additional review)? Happy to do whatever is required.

@sylvesterkaczmarek

Copy link
Copy Markdown

Nothing else is needed from my review. My approval on 54b19810 still stands: the namespace-aware tracked-change detection resolves the issue I raised, including aliased prefixes and the clean insideH / instrText cases, while parse failures remain fail-closed. The branch is mergeable; any remaining gate is the repository's normal maintainer review/merge process.

@TINGyu123644

Copy link
Copy Markdown
Author

Thanks for re-confirming the approval — nothing further needed on my end. Happy for this to be merged whenever you have a moment.

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.

3 participants