# How the Docx Skill Processes Tracked Changes at the XML Level

> Discover how the docx skill processes tracked changes. Learn XML parsing for ins and del elements, stripping changes, and comparing plain text for equivalence.

- Repository: [Anthropic/skills](https://github.com/anthropics/skills)
- Tags: internals
- Published: 2026-02-16

---

**The docx skill validates tracked changes by parsing [`word/document.xml`](https://github.com/anthropics/skills/blob/main/word/document.xml) to detect author-specific `<w:ins>` and `<w:del>` elements, stripping those changes from both the original and modified XML trees, and comparing the resulting plain text for equivalence.**

The **anthropics/skills** repository provides a specialized **docx** skill that ensures all modifications to Word documents are properly tracked at the XML level. When Microsoft Word's Track Changes feature is enabled, insertions and deletions are stored as specific XML elements within [`document.xml`](https://github.com/anthropics/skills/blob/main/document.xml). The skill's validation logic, implemented in [`skills/docx/scripts/office/validators/redlining.py`](https://github.com/anthropics/skills/blob/main/skills/docx/scripts/office/validators/redlining.py), processes these elements to verify that changes attributed to a specific author (defaulting to `Claude`) are correctly recorded and do not corrupt the document structure.

## Understanding Tracked Changes in Word XML

Microsoft Word stores revision markup using the WordprocessingML namespace (`w:`). The docx skill targets two primary element types when processing tracked changes at the XML level.

### Insertions and Deletions Structure

- **Insertions** are wrapped in `<w:ins>` elements with an author attribute: `<w:ins w:author="Claude">...</w:ins>`
- **Deletions** are wrapped in `<w:del>` elements and contain `<w:delText>` nodes instead of standard `<w:t>` text nodes: `<w:del w:author="Claude"><w:delText>deleted text</w:delText></w:del>`

The `RedliningValidator` class specifically searches for these patterns using XPath queries with the `w` namespace to isolate changes made by the configured author.

## The Redlining Validation Pipeline

The validation process follows a strict pipeline defined in the `validate()` method of `RedliningValidator`. This method orchestrates the comparison between an original baseline document and a modified version.

### Parsing the Modified Document

The validator first locates the modified [`document.xml`](https://github.com/anthropics/skills/blob/main/document.xml) within the unpacked `.docx` directory structure:

```python
modified_file = self.unpacked_dir / "word" / "document.xml"
tree = ET.parse(modified_file)
root = tree.getroot()

```

It then identifies all tracked change elements and filters for the target author:

```python
del_elements = root.findall(".//w:del", self.namespaces)
ins_elements = root.findall(".//w:ins", self.namespaces)

author_del_elements = [e for e in del_elements
    if e.get(f"{{{self.namespaces['w']}}}author") == self.author]
author_ins_elements = [e for e in ins_elements
    if e.get(f"{{{self.namespaces['w']}}}author") == self.author]

```

If no elements match the author, validation passes immediately.

### Loading the Original Baseline

To establish a comparison baseline, the validator unpacks the original `.docx` file into a temporary directory:

```python
with tempfile.TemporaryDirectory() as temp_dir:
    with zipfile.ZipFile(self.original_docx, "r") as zip_ref:
        zip_ref.extractall(temp_path)
    original_file = temp_path / "word" / "document.xml"
    original_tree = ET.parse(original_file)
    original_root = original_tree.getroot()

```

This provides the pre-modification XML tree for comparison.

### Removing Author-Specific Markup

The core validation logic removes the author's tracked changes from **both** the original and modified trees. This ensures that if the author made identical changes to both documents (or if the changes are properly isolated), the resulting text will match.

```python
self._remove_author_tracked_changes(original_root)
self._remove_author_tracked_changes(modified_root)

```

## XML Transformation Logic

The `_remove_author_tracked_changes()` method implements specific rules for handling insertions versus deletions at the XML level.

### Handling Insertions

Insertions by the target author are removed entirely from the tree. The method identifies `<w:ins>` elements with matching author attributes and removes them from their parent nodes:

```python
for parent in root.iter():
    to_remove = []
    for child in parent:
        if child.tag == ins_tag and child.get(author_attr) == self.author:
            to_remove.append(child)
    for elem in to_remove:
        parent.remove(elem)

```

This effectively strips all inserted content by the specified author from the document.

### Normalizing Deletions

Deletions require more complex handling because the deleted text must be preserved as normal content for comparison purposes. The method performs **deletion normalization**:

1. Converts `<w:delText>` elements to standard `<w:t>` elements
2. Lifts the children of the `<w:del>` element into the parent paragraph
3. Removes the empty `<w:del>` wrapper

```python
for del_elem, del_index in reversed(to_process):
    for elem in del_elem.iter():
        if elem.tag == deltext_tag:
            elem.tag = t_tag           # <w:delText> → <w:t>

    for child in reversed(list(del_elem)):
        parent.insert(del_index, child)   # lift children up

    parent.remove(del_elem)

```

This ensures that deleted text appears as regular content in the extracted plain text, allowing the validator to verify that deletions were properly tracked rather than permanently removed.

## Text Extraction and Comparison

After XML transformation, the validator extracts plain text from both trees using `_extract_text_content()`.

```python
def _extract_text_content(self, root):
    p_tag = f"{{{self.namespaces['w']}}}p"
    t_tag = f"{{{self.namespaces['w']}}}t"
    paragraphs = []
    for p_elem in root.findall(f".//{p_tag}"):
        text_parts = [t_elem.text for t_elem in p_elem.findall(f".//{t_tag}") if t_elem.text]
        paragraph_text = "".join(text_parts)
        if paragraph_text:
            paragraphs.append(paragraph_text)
    return "\n".join(paragraphs)

```

This method concatenates all `<w:t>` text runs within each `<w:p>` paragraph element, joining paragraphs with newline characters to create a normalized text representation.

The final comparison occurs in `validate()`:

```python
modified_text = self._extract_text_content(modified_root)
original_text = self._extract_text_content(original_root)

if modified_text != original_text:
    error_message = self._generate_detailed_diff(original_text, modified_text)
    print(error_message)
    return False

```

When texts differ, `_generate_detailed_diff()` invokes `_get_git_word_diff()` to produce a word-level diff using Git's `--word-diff=plain` option, highlighting exactly where the untracked modifications occurred.

## Command-Line Usage Examples

The `RedliningValidator` integrates into the docx skill's validation CLI via [`skills/docx/scripts/office/validate.py`](https://github.com/anthropics/skills/blob/main/skills/docx/scripts/office/validate.py).

### Validating Against an Original Document

```bash
python -m skills.docx.scripts.office.validate \
    ./modified_document.docx \
    --original ./original_document.docx \
    --author Claude \
    --verbose

```

This command unpacks both documents, runs the redlining validator to ensure all changes by "Claude" are properly tracked, and outputs a detailed diff if validation fails.

### Programmatic Validation

```python
from pathlib import Path
from skills.docx.scripts.office.validators.redlining import RedliningValidator

validator = RedliningValidator(
    unpacked_dir=Path("./unpacked_modified"),
    original_docx=Path("./original.docx"),
    author="Alice",
    verbose=True
)

if validator.validate():
    print("All tracked changes validated successfully")
else:
    print("Validation failed - see diff output above")

```

## Summary

- The **docx skill** processes tracked changes by parsing [`word/document.xml`](https://github.com/anthropics/skills/blob/main/word/document.xml) to identify `<w:ins>` and `<w:del>` elements attributed to a specific author.
- **Insertion handling** removes `<w:ins>` elements entirely when they match the target author, effectively stripping that content from the comparison text.
- **Deletion normalization** converts `<w:delText>` nodes to standard `<w:t>` elements and lifts them out of `<w:del>` wrappers, preserving deleted text as regular content for comparison.
- The validator compares plain-text extractions from both the original and modified documents after XML transformation, using `git diff --word-diff` to highlight any discrepancies indicating untracked changes.
- All logic resides in [`skills/docx/scripts/office/validators/redlining.py`](https://github.com/anthropics/skills/blob/main/skills/docx/scripts/office/validators/redlining.py) and integrates with the CLI via [`skills/docx/scripts/office/validate.py`](https://github.com/anthropics/skills/blob/main/skills/docx/scripts/office/validate.py).

## Frequently Asked Questions

### How does the docx skill identify which tracked changes belong to a specific author?

The `RedliningValidator` filters XML elements by checking the `w:author` attribute against the configured author name (defaulting to `Claude`). It constructs the full attribute name using the WordprocessingML namespace and compares it to `self.author` for every `<w:ins>` and `<w:del>` element found in [`word/document.xml`](https://github.com/anthropics/skills/blob/main/word/document.xml).

### What happens to deletions when the validator removes tracked changes from the XML?

Rather than removing the content entirely, the validator **normalizes** deletions by converting `<w:delText>` elements to standard `<w:t>` text nodes and lifting them out of the `<w:del>` wrapper into the parent paragraph. This preserves the deleted text as regular content, allowing the validator to verify that the deletion was properly tracked rather than permanently removed from the document.

### Can I use the redlining validator to check changes made by authors other than Claude?

Yes. The validator accepts an `author` parameter that can be set to any string matching the `w:author` attribute in the Word XML. When using the command-line interface, pass `--author "Alice"` or any other name to validate tracked changes attributed to that specific user instead of the default `Claude` value.

### Why does the validator compare plain text instead of comparing the XML directly?

The validator extracts plain text from both the original and modified documents after stripping the target author's tracked changes because Word documents can contain irrelevant XML differences (such as different relationship IDs, timestamps, or formatting variations) that do not affect the actual document content. By normalizing both trees to plain text paragraphs, the validator ensures it is comparing semantic content rather than XML serialization artifacts, using `git diff --word-diff` to pinpoint exactly where untracked modifications occurred.