fix(arguments): validate claim_ids + per-row savepoint in argument_aggregator (#156) #330

Merged
chaim merged 1 commits from worktree-fix-argument-aggregator-savepoint into main 2026-06-24 12:59:33 +00:00
Owner

aggregate_claims_to_arguments נכשל עם current transaction is aborted, commands ignored until end of transaction block בתיקים גדולים (אומת על 1027-04-26, 195 טענות).

שורש: ב-argument_aggregator.py ה-INSERT ל-legal_argument_propositions היה עטוף ב-except Exception רחב ללא savepoint. כש-ה-LLM מחזיר claim_id תקין-תחבירית אך לא-קיים, הפרת ה-FK (legal_argument_propositions_claim_id_fkey) משאירה את טרנזקציית asyncpg במצב aborted. ה-except תפס רק את חריגת ה-INSERT הספציפי אך מעולם לא ביצע ROLLBACK TO SAVEPOINT — ולכן הפקודה הבאה (INSERT legal_arguments של הטיעון הבא) זרקה InFailedSQLTransactionError שלא נתפסה והקריסה את כל הקריאה. בתיק עם הרבה טענות ההסתברות ל-UUID פגום גבוהה → כשל עקבי.

התיקון

  1. ולידציה במקור (G1): כל claim_id נבדק מול קבוצת ה-id-ים של הטענות שכבר נשלפו לתיק (valid_claim_ids) לפני ה-INSERT, כך ש-id הזוי לא מגיע ל-DB. UUID פגום-תחבירית כבר נזרק ב-_normalize_argument; כאן נתפסים התקינים-אך-לא-קיימים.
  2. savepoint per-row (הגנה-בעומק): ה-INSERT עטוף ב-async with conn.transaction() (asyncpg מקנן כ-SAVEPOINT), כך שכל הפרת-constraint בלתי-צפויה מגלגלת לאחור לנקודת-השמירה במקום להרעיל את הטרנזקציה המקיפה.

Invariants

  • G1 — נרמול במקור, לא תפיסת-תסמין בקריאה.
  • G2 — אין מסלול מקביל; אותה לוגיקת-כתיבה.
  • §6 — אין בליעה שקטה: דילוגים נרשמים ב-logger.warning.

בדיקה

python -m py_compile עובר. ניתן לאמת על 1027-04-26 לאחר redeploy של ה-MCP: aggregate_claims_to_arguments אמור לסיים completed עם דילוג מתועד על claim_ids לא-קיימים במקום קריסה.

🤖 Generated with Claude Code

## הבעיה (דווח ב-CMP-186, TaskMaster legal-ai #156) `aggregate_claims_to_arguments` נכשל עם `current transaction is aborted, commands ignored until end of transaction block` בתיקים גדולים (אומת על **1027-04-26, 195 טענות**). **שורש:** ב-`argument_aggregator.py` ה-INSERT ל-`legal_argument_propositions` היה עטוף ב-`except Exception` רחב **ללא savepoint**. כש-ה-LLM מחזיר `claim_id` תקין-תחבירית אך לא-קיים, הפרת ה-FK (`legal_argument_propositions_claim_id_fkey`) משאירה את טרנזקציית asyncpg במצב **aborted**. ה-`except` תפס רק את חריגת ה-INSERT הספציפי אך מעולם לא ביצע `ROLLBACK TO SAVEPOINT` — ולכן הפקודה הבאה (`INSERT legal_arguments` של הטיעון הבא) זרקה `InFailedSQLTransactionError` שלא נתפסה והקריסה את כל הקריאה. בתיק עם הרבה טענות ההסתברות ל-UUID פגום גבוהה → כשל עקבי. ## התיקון 1. **ולידציה במקור (G1):** כל `claim_id` נבדק מול קבוצת ה-id-ים של הטענות שכבר נשלפו לתיק (`valid_claim_ids`) לפני ה-INSERT, כך ש-id הזוי לא מגיע ל-DB. UUID פגום-תחבירית כבר נזרק ב-`_normalize_argument`; כאן נתפסים התקינים-אך-לא-קיימים. 2. **savepoint per-row (הגנה-בעומק):** ה-INSERT עטוף ב-`async with conn.transaction()` (asyncpg מקנן כ-`SAVEPOINT`), כך שכל הפרת-constraint בלתי-צפויה מגלגלת לאחור לנקודת-השמירה במקום להרעיל את הטרנזקציה המקיפה. ## Invariants - **G1** — נרמול במקור, לא תפיסת-תסמין בקריאה. - **G2** — אין מסלול מקביל; אותה לוגיקת-כתיבה. - **§6** — אין בליעה שקטה: דילוגים נרשמים ב-`logger.warning`. ## בדיקה `python -m py_compile` עובר. ניתן לאמת על 1027-04-26 לאחר redeploy של ה-MCP: `aggregate_claims_to_arguments` אמור לסיים `completed` עם דילוג מתועד על claim_ids לא-קיימים במקום קריסה. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-24 12:58:38 +00:00
fix(arguments): validate claim_ids + per-row savepoint in argument_aggregator
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
c9d83431e0
aggregate_claims_to_arguments crashed with "current transaction is aborted,
commands ignored until end of transaction block" on large cases (confirmed on
1027-04-26, 195 claims; reported via CMP-186).

Root cause: the proposition INSERT (legal_argument_propositions) was wrapped in
a broad except Exception with no savepoint. When the LLM echoes a
syntactically-valid-but-nonexistent claim_id, the FK violation
(legal_argument_propositions_claim_id_fkey) puts the asyncpg transaction into
aborted state. The except caught only that INSERT's error but never issued
ROLLBACK TO SAVEPOINT, so the next statement (the following argument's INSERT
into legal_arguments) raised InFailedSQLTransactionError uncaught and crashed
the whole call. With many claims the bad-UUID probability is high -> consistent
failure.

Fix:
- Validate each claim_id against the known set of claim ids fetched for the
  case before INSERT, so a hallucinated id never reaches the DB (G1: fix at
  source). Malformed UUIDs are already dropped in _normalize_argument; this
  catches the valid-but-nonexistent ones.
- Wrap the INSERT in a per-row savepoint (async with conn.transaction()) as
  defense-in-depth, so any unexpected constraint failure rolls back to the
  savepoint instead of poisoning the surrounding transaction.

Invariants: G1 (normalize at source, not symptom-catch on read). No parallel
path (G2). No silent swallow — skips are logged.

TaskMaster: legal-ai #156

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit a9d04c1e9f into main 2026-06-24 12:59:33 +00:00
chaim deleted branch worktree-fix-argument-aggregator-savepoint 2026-06-24 12:59:34 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#330