# ContractEditor Operation Ordering: Add Clauses Before Renumbering

## 2026-07-13 练塘硬件购销合同教训

### Problem
When interleaving `add_clause_before()` and `tracked_replace()` for renumbering, lxml throws:
```
ValueError: Element is not a child of this node.
```
in `tracked_replace()` → `parent.remove(runs[idx])`.

### Root Cause
`add_clause_before()` / `add_clause()` mutate the XML tree (insert new `<w:p>` elements). After insertion, previously-found element references held by later `tracked_replace()` calls may point to nodes whose parent relationship has shifted. The `parent.remove()` call inside `tracked_replace` fails because the run's parent `<w:p>` is no longer the same object the code expects.

### Fix: Two-Phase Approach (铁律)

**Phase 1 — All structural additions:**
- `add_clause()` / `add_clause_before()` for new clauses
- Content-level `tracked_replace()` that don't touch clause titles being renumbered

**Phase 2 — Renumbering (after all additions are done):**
- `tracked_replace('第七条不可抗力', '第九条不可抗力')` etc.

### Example (correct order)
```python
# Phase 1: add new clauses
editor.add_clause_before('第七条 转包与分包\n...', before_search='第七条不可抗力')
editor.add_clause_before('第八条 第三方侵权\n...', before_search='第七条不可抗力')

# Phase 2: renumber old clauses (all additions done)
editor.tracked_replace('第七条不可抗力', '第九条不可抗力')
editor.tracked_replace('第八条争议解决', '第十条争议解决')
```

### WPS/DOC File Handling
WPS `.wps` and `.doc` files must be converted to `.docx` before ContractEditor can process them:
```bash
libreoffice --headless --convert-to docx input.wps --outdir /tmp/contract-review/
```
Then copy to a simple ASCII filename to avoid python-docx path issues with Chinese characters.
