Add Track Changes architecture for draft revisions (CMP + CMPA)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m29s
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m29s
Fixes critical bug in 1033-25: user-uploaded עריכה-*.docx files were
orphaned on disk while exports kept rebuilding from stale DB blocks.
New architecture:
- User-uploaded DOCX becomes the source of truth (cases.active_draft_path)
- System edits via XML surgery with real Word <w:ins>/<w:del> revisions
- User can Accept/Reject each change from within Word
Components:
- docx_reviser.py: XML surgery for Track Changes (15 tests)
- docx_retrofit.py: retroactive bookmark injection with Hebrew marker
detection + heading heuristic (9 tests)
- docx_exporter.py: emits bookmarks around each of the 12 blocks
- 3 new MCP tools: apply_user_edit, list_bookmarks, revise_draft
- 4 new/updated endpoints: upload (auto-registers active draft),
/exports/revise, /exports/bookmarks, /exports/{filename}/retrofit,
/active-draft
- DB migration: cases.active_draft_path column
- UI: correct banner using real v-numbers, "מקור האמת" badge,
detailed upload toast with bookmarks_added/missing_blocks
- agents: legal-exporter (3 export modes), legal-ceo (stage G for
revision handling), legal-writer (revision mode)
Multi-tenancy:
- Works for both CMP (1xxx cases) and CMPA (8xxx/9xxx cases)
- New revise-draft skill added to both companies
- deploy-track-changes.sh syncs skills CMP ↔ CMPA
- retrofit_case.py: one-off retrofit of existing files
Tests: 34 passing (15 reviser + 9 retrofit + 4 exporter bookmarks + 6 e2e)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,9 @@
|
||||
| `restore-db.sh` | bash | שחזור DB מגיבוי (companion ל-backup-db.sh) | ידני |
|
||||
| `notify.py` | python | שליחת מייל התראה מסוכנים via SMTP (Gmail) | נקרא ע"י סוכנים |
|
||||
| `bidi_table.py` | python | יצירת טבלאות box-drawing עם תמיכה ב-BiDi (עברית+אנגלית) | ספריית עזר |
|
||||
| `convert_decision_template.py` | python | המרת `data/training/טיוטת החלטה.dotx` → `skills/docx/decision_template.docx` לטעינה ב-python-docx | להריץ כשמתעדכנת התבנית |
|
||||
| `deploy-track-changes.sh` | bash | סנכרון skills CMP↔CMPA + בדיקות + הנחיות deploy לארכיטקטורת Track Changes | ידני |
|
||||
| `retrofit_case.py` | python | retrofit רטרואקטיבי — מזריק bookmarks לקובץ קיים של תיק ספציפי ומגדיר אותו כ-active_draft | ידני (חד-פעמי לתיק) |
|
||||
|
||||
## תיקיית `.archive/` — סקריפטים שהושלמו
|
||||
|
||||
|
||||
86
scripts/deploy-track-changes.sh
Executable file
86
scripts/deploy-track-changes.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
# deploy-track-changes.sh — פריסת ארכיטקטורת Track Changes לשתי חברות (CMP + CMPA)
|
||||
#
|
||||
# מה זה עושה:
|
||||
# 1. מוודא ש-skills קיימים ומסונכרנים בשתי החברות
|
||||
# 2. git commit + push (אם יש שינויים)
|
||||
# 3. הודעה להפעלת Coolify deploy
|
||||
#
|
||||
# שימוש:
|
||||
# scripts/deploy-track-changes.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
CMP_DIR="/home/chaim/.paperclip/instances/default/skills/42a7acd0-30c5-4cbd-ac97-7424f65df294"
|
||||
CMPA_DIR="/home/chaim/.paperclip/instances/default/skills/8639e837-4c9d-47fa-a76b-95788d651896"
|
||||
COOLIFY_UUID="gyjo0mtw2c42ej3xxvbz8zio"
|
||||
|
||||
echo "▶ שלב 1: סנכרון skills בין CMP ל-CMPA"
|
||||
|
||||
SKILLS=(legal-docx attach-precedents review-analysis writer-readiness
|
||||
appendix-expert-intern bidi-table-rtl revise-draft)
|
||||
|
||||
mkdir -p "$CMPA_DIR"
|
||||
for skill in "${SKILLS[@]}"; do
|
||||
if [ ! -d "$CMP_DIR/$skill" ]; then
|
||||
echo " ⚠ skill לא קיים ב-CMP: $skill — דילוג"
|
||||
continue
|
||||
fi
|
||||
if [ -d "$CMPA_DIR/$skill" ]; then
|
||||
# Update only — don't delete any CMPA-specific files
|
||||
rsync -av --update "$CMP_DIR/$skill/" "$CMPA_DIR/$skill/" > /dev/null
|
||||
echo " ✓ $skill (עודכן ב-CMPA)"
|
||||
else
|
||||
cp -r "$CMP_DIR/$skill" "$CMPA_DIR/$skill"
|
||||
echo " ✓ $skill (הועתק ל-CMPA)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "▶ שלב 2: בדיקת פיתוח אחרונה"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# Run mcp-server tests
|
||||
if [ -f mcp-server/.venv/bin/pytest ]; then
|
||||
echo " מריץ pytest..."
|
||||
(cd mcp-server && .venv/bin/pytest tests/ -q 2>&1 | tail -5) || {
|
||||
echo " ✗ בדיקות נכשלו — עצירה"
|
||||
exit 1
|
||||
}
|
||||
echo " ✓ כל הבדיקות עברו"
|
||||
fi
|
||||
|
||||
# Run TypeScript check
|
||||
if [ -d web-ui/node_modules ]; then
|
||||
echo " מריץ tsc..."
|
||||
(cd web-ui && npx tsc --noEmit 2>&1 | head -10) || {
|
||||
echo " ✗ שגיאות TypeScript — עצירה"
|
||||
exit 1
|
||||
}
|
||||
echo " ✓ TypeScript נקי"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "▶ שלב 3: סטטוס git"
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo " יש שינויים ב-git — לא מבצע commit אוטומטי (ריצו ידנית)"
|
||||
git status --short
|
||||
echo ""
|
||||
echo " הפקודה להרצה:"
|
||||
echo " git add -A"
|
||||
echo " git commit -m \"Add Track Changes support for draft revisions (CMP + CMPA)\""
|
||||
echo " git push origin main"
|
||||
else
|
||||
echo " ✓ אין שינויים לא שמורים"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "▶ שלב 4: Coolify deploy"
|
||||
echo " לאחר push, הריצו:"
|
||||
echo " mcp__coolify__deploy עם UUID=$COOLIFY_UUID"
|
||||
echo " או דרך UI: https://coolify.nautilus.marcusgroup.org"
|
||||
echo ""
|
||||
echo "✓ הסקריפט הסתיים"
|
||||
84
scripts/retrofit_case.py
Executable file
84
scripts/retrofit_case.py
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""retrofit_case.py — הזרקת bookmarks רטרואקטיבית לקובץ קיים בתיק.
|
||||
|
||||
שימוש:
|
||||
python scripts/retrofit_case.py <case_number> <filename>
|
||||
|
||||
דוגמה:
|
||||
python scripts/retrofit_case.py 1033-25 עריכה-v1.docx
|
||||
|
||||
פעולה:
|
||||
1. מזהה את הקובץ ב-data/cases/{case_number}/exports/
|
||||
2. מזריק bookmarks ב-12 הבלוקים (heuristic)
|
||||
3. שומר backup כ-{filename}.pre-retrofit.docx
|
||||
4. מדפיס summary — אילו בלוקים זוהו, אילו חסרים
|
||||
|
||||
לתיק 1033-25 — הריצו פעם אחת על עריכה-v1.docx הקיים. אחרי זה תוכלו
|
||||
להריץ revise_draft דרך ה-CEO.
|
||||
|
||||
הערה: השירות הזה נקרא גם אוטומטית דרך apply_user_edit tool ב-MCP,
|
||||
אז אחרי deploy אין צורך להריץ ידנית. זה לגיבוי/ניפוי.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Make mcp-server importable when run from repo root
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "mcp-server" / "src"))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 3:
|
||||
print(__doc__)
|
||||
return 2
|
||||
|
||||
case_number = sys.argv[1]
|
||||
filename = sys.argv[2]
|
||||
|
||||
from legal_mcp.services import docx_retrofit, docx_reviser
|
||||
|
||||
case_dir = REPO_ROOT / "data" / "cases" / case_number / "exports"
|
||||
file_path = case_dir / filename
|
||||
|
||||
if not file_path.exists():
|
||||
print(f"✗ קובץ לא נמצא: {file_path}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print(f"מעבד: {file_path}")
|
||||
print(f" גודל: {file_path.stat().st_size:,} בייט")
|
||||
|
||||
# Existing bookmarks
|
||||
before = docx_reviser.list_bookmarks(file_path)
|
||||
print(f" bookmarks קיימים: {before or '(ריק)'}")
|
||||
|
||||
result = docx_retrofit.retrofit_bookmarks(file_path)
|
||||
print()
|
||||
print("תוצאה:")
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
|
||||
# Verify post-state
|
||||
after = docx_reviser.list_bookmarks(file_path)
|
||||
print()
|
||||
print(f"bookmarks אחרי: {len(after)} — {after}")
|
||||
|
||||
backup = file_path.with_suffix(".pre-retrofit.docx")
|
||||
if backup.exists():
|
||||
print(f"גיבוי נשמר: {backup}")
|
||||
|
||||
# Build an MCP-callable invocation hint
|
||||
rel = file_path.relative_to(REPO_ROOT)
|
||||
print()
|
||||
print("השלב הבא: לעדכן active_draft_path ב-DB. הפקודה:")
|
||||
print(f' mcp__legal-ai__apply_user_edit case_number="{case_number}" '
|
||||
f'edit_filename="{filename}"')
|
||||
print()
|
||||
print(f"(זה ירוץ retrofit שוב idempotent ואז יעדכן את DB)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user