diff --git a/src/components/Findings.tsx b/src/components/Findings.tsx
index b5a89f6..2d2692b 100644
--- a/src/components/Findings.tsx
+++ b/src/components/Findings.tsx
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { AlertTriangle, CheckCircle2, ChevronDown, FileSearch } from 'lucide-react';
import { Badge, Card, Facts } from './core';
-import type { AvaTraceStep, Row } from '../api/types';
+import type { AvaDecision, AvaTraceStep, Row } from '../api/types';
import {
firstDeadline,
headline,
@@ -159,9 +159,14 @@ export function FullStory({ row }: { row: Row }) {
* paste 16 full queries into a form field was what blew the tool call past
* the output cap and lost a whole investigation.
*/
-export function SqlEvidence({ row, steps }: { row: Row; steps: AvaTraceStep[] }) {
+export function SqlEvidence({ row, steps, decisions }: { row: Row; steps: AvaTraceStep[]; decisions: AvaDecision[] }) {
const [open, setOpen] = useState(false);
- const queries = steps.filter((s) => s.type === 'api' && s.detail);
+ // Only the attempt that produced the findings. A case can carry an earlier
+ // attempt that failed to submit (its queries are still in the trace); showing
+ // both would double the count and make her look twice as busy as she was.
+ const submitted = decisions.filter((d) => d.status === 'submitted').slice(-1)[0];
+ const scoped = submitted ? steps.filter((s) => s.decision_id == null || String(s.decision_id) === String(submitted.id)) : steps;
+ const queries = scoped.filter((s) => s.type === 'api' && s.detail);
const notes = sqlSteps(row.sql_log);
if (queries.length === 0 && notes.length === 0) return null;
diff --git a/src/format.ts b/src/format.ts
index dacbf78..9ac217d 100644
--- a/src/format.ts
+++ b/src/format.ts
@@ -101,9 +101,17 @@ export function recommendations(v: unknown): string[] {
/** "1. APPEALS — FILE RETRO-AUTH ($1,091,000 …)" → title + body. */
export function splitRecommendation(block: string): { title: string; body: string } {
- const m = block.match(/^\s*(\d+)\.\s*([^.\n]+?)(?:\.|\n|$)([\s\S]*)$/);
- if (!m) return { title: '', body: block };
- return { title: m[2].trim(), body: (m[3] ?? '').trim() };
+ // Strip the leading "N." and take the title up to the first sentence break —
+ // a period FOLLOWED BY A SPACE, a colon, or a newline. Never a bare period:
+ // "$1.56M" and "CO-197" must survive intact.
+ const rest = block.replace(/^\s*\d+\.\s*/, '');
+ const m = rest.match(/^([\s\S]*?)(?:\.\s|:\s|\n|$)([\s\S]*)$/);
+ if (!m) return { title: '', body: rest };
+ const title = m[1].trim();
+ const body = (m[2] ?? '').trim();
+ // A "title" that swallowed the whole block means there was no break — show it as body.
+ if (title.length > 140) return { title: '', body: rest };
+ return { title, body };
}
/** Ava's SQL log is one statement per block, each prefixed with its step. */
diff --git a/src/screens/CaseScreen.tsx b/src/screens/CaseScreen.tsx
index 9aa51f9..a088267 100644
--- a/src/screens/CaseScreen.tsx
+++ b/src/screens/CaseScreen.tsx
@@ -198,7 +198,7 @@ export function CaseScreen() {
{hasFindings && (
<>
-
+
{!aiOwned && (
-
+
setShowWork(!showWork)}