get_case_issues: also match issues by [ערר X] title prefix
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s

The original implementation only returned issues with a plugin_state
linkage (legal-case-number key), which was set just on the initial
setup issue. Sub-agents that created follow-up issues during the case
workflow tagged them in the title ("[ערר 1130-25] כתיבת החלטה" etc.)
but didn't write a plugin_state row, so 23 of 24 historical issues
for case 1130-25 were invisible to the agent activity feed.

Widened the lookup to UNION two paths:
  (a) plugin_state.scope_id matches via the legal-case-number key
  (b) issues.title LIKE '%[ערר {case_number}]%' OR '%ערר {case_number}%'

Used DISTINCT ON (i.id) + post-sort by created_at to dedupe and keep
chronological order. The widget on https://legal-ai.../cases/1130-25
will now show the full history (was 1 issue → now 16).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 19:53:20 +00:00
parent 3a1760b4cd
commit 6a47320b9c

View File

@@ -386,22 +386,35 @@ async def create_workflow_issue(case_number: str, title: str) -> dict:
async def get_case_issues(case_number: str) -> list[dict]:
"""Get all Paperclip issues linked to a legal-ai case number."""
"""Get all Paperclip issues linked to a legal-ai case number.
Matches via two paths to avoid missing historical issues:
(a) the original setup linkage in plugin_state (state_key = legal-case-number)
(b) issues whose title contains "[ערר {case_number}]" or "ערר {case_number}"
— that's how sub-agents conventionally tag follow-up issues.
Returns the union of both, deduplicated by issue id, ordered by creation time.
"""
title_patterns = [f"%[ערר {case_number}]%", f"%ערר {case_number}%"]
conn = await asyncpg.connect(PAPERCLIP_DB_URL)
try:
rows = await conn.fetch(
"""SELECT i.id, i.title, i.status, i.identifier, i.priority,
"""SELECT DISTINCT ON (i.id)
i.id, i.title, i.status, i.identifier, i.priority,
i.assignee_agent_id, a.name AS assignee_name,
i.started_at, i.completed_at, i.created_at, i.company_id
FROM issues i
JOIN plugin_state ps ON ps.scope_id = i.id::text
LEFT JOIN agents a ON i.assignee_agent_id = a.id
WHERE ps.plugin_id = $1::uuid
AND ps.state_key = 'legal-case-number'
AND ps.value_json = $2::jsonb
ORDER BY i.created_at""",
PLUGIN_ID, json.dumps(case_number),
LEFT JOIN plugin_state ps ON ps.scope_id = i.id::text
AND ps.plugin_id = $1::uuid
AND ps.state_key = 'legal-case-number'
AND ps.value_json = $2::jsonb
WHERE ps.scope_id IS NOT NULL
OR i.title LIKE ANY($3::text[])
ORDER BY i.id, i.created_at""",
PLUGIN_ID, json.dumps(case_number), title_patterns,
)
# Sort by created_at after dedup
sorted_rows = sorted(rows, key=lambda r: r["created_at"])
return [
{
"id": str(r["id"]),
@@ -415,7 +428,7 @@ async def get_case_issues(case_number: str) -> list[dict]:
"created_at": r["created_at"].isoformat() if r["created_at"] else None,
"company_id": str(r["company_id"]),
}
for r in rows
for r in sorted_rows
]
finally:
await conn.close()