From 903fb4d140892932416fe42e55a53b0a0faf3690 Mon Sep 17 00:00:00 2001 From: Chaim Date: Thu, 30 Apr 2026 14:44:44 +0000 Subject: [PATCH] db: add missing delete_case (cases_tools.case_delete was calling a ghost) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The case_delete tool in tools/cases.py and the DELETE /api/cases endpoint in web/app.py both invoke await db.delete_case(case_id), but no such function existed in services/db.py — every call returned 500 with an AttributeError. Discovered while wiping case 8174-24 for a clean rerun. Implementation is straightforward because the FK graph already does the work: 7 dependent tables CASCADE on cases.id (documents, document_chunks, claims, appraiser_facts, decisions, qa_results, case_precedents) and 2 SET NULL (audit_log, chair_feedback). A single DELETE FROM cases is enough — no manual ordering needed. Documented in the docstring that this only touches the legal-ai DB — Paperclip projects/issues and Gitea repos for the case are separate systems and must be cleaned up by the caller. Co-Authored-By: Claude Opus 4.7 (1M context) --- mcp-server/src/legal_mcp/services/db.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mcp-server/src/legal_mcp/services/db.py b/mcp-server/src/legal_mcp/services/db.py index 9cab861..9b45773 100644 --- a/mcp-server/src/legal_mcp/services/db.py +++ b/mcp-server/src/legal_mcp/services/db.py @@ -681,6 +681,26 @@ async def restore_case(case_id: UUID) -> dict | None: return _row_to_case(row) if row else None +async def delete_case(case_id: UUID) -> bool: + """Delete a case row. Returns True if a row was actually removed. + + All dependent rows are removed automatically by FK constraints: + • CASCADE: documents, document_chunks, claims, appraiser_facts, + decisions, qa_results, case_precedents + • SET NULL: audit_log.case_id, chair_feedback.case_id + + NOTE: this only touches the legal-ai database. The Paperclip project + (issues, comments, runs) and Gitea repo for the case live in other + systems and are NOT cleaned up here — call sites that need a full + reset must handle those separately. + """ + pool = await get_pool() + async with pool.acquire() as conn: + result = await conn.execute("DELETE FROM cases WHERE id = $1", case_id) + # asyncpg execute returns "DELETE " — extract count. + return int(result.split()[-1]) > 0 + + # ── Document CRUD ─────────────────────────────────────────────────── async def create_document(