Files
hermes-skills/skills/legal/contract-editor/references/multi-version-comparison-table.md
T

3.8 KiB

Multi-Version Contract Comparison Table (三版对比表)

When to Use

When Maggie/Doro asks to compare multiple versions of a contract (typically: template / counterparty revision / our revision), produce a structured docx comparison table.

Pattern (2026-07-03 模特合作协议 session)

Document Setup

  • Landscape orientation for 4-5 columns: section.orientation = 1; page_width=Cm(29.7); page_height=Cm(21.0)
  • Narrow margins: 1.2-1.5cm all sides
  • Font size 8.5-9pt for table cells (fits more content)

Table Structure

条款 【模版】 版本A(对方修订) 版本B(我方修订) 双方协商一致

Red Font for Differences

  • Column N is red when its content differs from other versions
  • Use RGBColor(0xFF, 0x00, 0x00) on the run
  • "协商一致" column: red = current text doesn't match consensus → needs modification

Yellow Background for Consensus Column

def set_cell_shading(cell, color):
    tc = cell._element
    tcPr = tc.find(qn('w:tcPr'))
    if tcPr is None:
        tcPr = OxmlElement('w:tcPr')
        tc.insert(0, tcPr)
    shading = OxmlElement('w:shd')
    shading.set(qn('w:fill'), color)  # e.g. 'FFF8E1' for light yellow
    shading.set(qn('w:val'), 'clear')
    tcPr.append(shading)

Header Row Styling

  • Blue background (D9E2F3)
  • Bold, centered, font size 8.5pt

Data Structure in Code

# Each row: (clause_name, col1_text, col2_text, col3_text, col4_text, col2_red, col3_red, col4_red)
rows = [
    ('条款名',
     '模版内容',
     '对方修订内容',
     '我方修订内容', 
     '协商一致内容',
     True,   # col2 red? (differs from others)
     False,  # col3 red?
     True),  # col4 red? (doesn't match consensus)
]

Legend at Bottom

Include a legend explaining what red means in each column:

  • 版本A列红色 = 与模版/我方版不一致(对方的修改)
  • 版本B列红色 = 与模版不一致(我方的修改)
  • 协商一致列红色 = 当前文本与协商一致不符,需要修改

Key Lessons

  1. Read all three files from Nextcloud using sudo find ~/nextcloud/data/data/... path
  2. Extract paragraph text using python-docx: [(i, p.text.strip()) for i, p in enumerate(doc.paragraphs) if p.text.strip()]
  3. Check tables separately: doc.tables — contracts often have signature blocks and SNS account tables
  4. Align comparison by clause semantics, not paragraph index — different versions may have different paragraph counts
  5. Also upload to Nextcloud for viewing in OnlyOffice

Per-Run Precision for Tracked Changes (Maggie's correction)

When applying tracked changes based on comparison results, never replace entire paragraphs. Instead:

  1. Identify the specific runs containing text to change
  2. For each run: create w:del wrapping a deepcopy (converting w:t → w:delText), create w:ins with new text and cloned rPr, swap in place
  3. All surrounding runs remain untouched
# Find specific run by text content
for r in para_element.findall(qn('w:r')):
    text = ''.join(t.text or '' for t in r.findall(qn('w:t')))
    if text == '¥700,000':  # exact match on this run
        r_parent = r.getparent()
        r_idx = list(r_parent).index(r)
        # Create del wrapping copy of this run
        del_elem = make_del_run_from_existing(r)
        # Create ins with new value, same rPr
        ins_elem = make_ins_run('¥600,000', r.find(qn('w:rPr')))
        r_parent.remove(r)
        r_parent.insert(r_idx, ins_elem)
        r_parent.insert(r_idx, del_elem)
        break

This produces clean tracked changes where Word/OnlyOffice shows exactly which characters changed (e.g., 700,000 → 600,000) rather than entire-paragraph replacements.