# 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 `` 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 `` 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.