Skip to content

Commit 6de73ca

Browse files
Merge branch 'ftr/mobile-view' into test-deployment
2 parents 73683cb + 7c3993c commit 6de73ca

File tree

141 files changed

+25205
-1802
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+25205
-1802
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,5 @@ CLAUDE.md
267267
.claude
268268
OTHER
269269
.goose
270-
.codex
270+
.codex
271+
DOCS/reference/epstein-network-ui/Epstein-doc-explorer

.planning/MINOR_ISSUES.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Minor Issues
2+
3+
Deferred issues that are non-blocking but should be addressed in future work.
4+
5+
---
6+
7+
## MI-001: Triage/Orchestrator execution lookups use vulnerable re-query pattern
8+
9+
- **Files**: `backend/app/services/pipeline.py` lines ~260-269 (triage), ~342-351 (orchestrator)
10+
- **Pattern**: `WHERE workflow_id = X AND agent_name = 'triage' ORDER BY created_at DESC LIMIT 1`
11+
- **Risk**: Same antipattern that caused the domain agent execution ID misattribution bug. Safe today because triage and orchestrator each run exactly once per workflow. Breaks if retries are ever added.
12+
- **Fix**: Update `run_triage` and `run_orchestrator` to return `tuple[OutputT | None, UUID | None]` (same pattern applied to domain agents in commits `176ce91`..`a5fef73`), then use the returned `execution_id` directly instead of re-querying.
13+
- **Priority**: Low (no current bug, future-proofing only)
14+
- **Added**: 2025-02-07, QC review of Phase 7 execution ID fix
15+
16+
## MI-002: DomainRunFn type alias covariance under strict pyright
17+
18+
- **File**: `backend/app/agents/domain_runner.py` line 24
19+
- **Detail**: `DomainRunFn` is typed as `Callable[..., Awaitable[tuple[DomainAgentOutput | None, UUID | None]]]`. Concrete functions (`run_financial`, `run_legal`, `run_evidence`) return `tuple[FinancialOutput | None, UUID | None]` etc. Works at runtime via structural subtyping but could flag under pyright strict mode due to `Callable` return type covariance rules.
20+
- **Fix**: Either annotate concrete functions to return `tuple[DomainAgentOutput | None, UUID | None]`, or use a generic `TypeVar` on the return type.
21+
- **Priority**: Low (no runtime impact, passes pyright default mode)
22+
- **Added**: 2025-02-07, QC review of Phase 7 execution ID fix
23+
24+
## ~~MI-003: Fuzzy entity matching produces false positives on numeric/temporal values~~ — RESOLVED
25+
26+
- **Status**: RESOLVED by Phase 7.1 (2026-02-08)
27+
- **Resolution**: The programmatic KG Builder with fuzzy dedup (`backend/app/services/kg_builder.py`) was entirely replaced by the LLM-based KG Builder Agent. The LLM uses a clear-and-rebuild strategy and handles deduplication naturally by seeing all findings holistically. No fuzzy string matching exists in the current pipeline.
28+
- **Original file**: `backend/app/services/kg_builder.py` — programmatic service is now dead code (superseded by `backend/app/agents/kg_builder.py`)
29+
- **Added**: 2026-02-07, live pipeline testing
30+
- **Resolved**: 2026-02-08, Phase 7.1 LLM KG Builder
31+
32+
## MI-004: Pipeline summary log mixes two different entity count semantics
33+
34+
- **File**: `backend/app/services/pipeline.py` line ~1089
35+
- **Log**: `entities=40 kg_entities=61` — confusing because both sound like entity counts but measure different things.
36+
- **Detail**: `entities` = triage file-level entities (line 1048: `sum(len(fr.entities) for fr in triage_output.file_results)`) + domain agent top-level `output.entities` (line 1043). `kg_entities` = KG Builder total (includes both top-level AND per-finding entities). The KG count is always >= domain entity count because KG Builder also extracts from `finding.entities` lists. The naming makes them appear comparable when they are not.
37+
- **Fix**: Rename `entities` to `triage_entities` and `total_domain_entities` to `domain_output_entities` in the log, or remove the triage entity count from this summary (it's already logged in the triage stage). The `processing-complete` SSE event (line 1065) also sums these two, which is sent to the frontend — verify frontend doesn't display this misleadingly.
38+
- **Priority**: Low (cosmetic log clarity, no data impact)
39+
- **Phase**: Fix opportunistically during Phase 8 pipeline extension
40+
- **Added**: 2026-02-07, live pipeline testing

.planning/REQUIREMENTS.md

Lines changed: 242 additions & 90 deletions
Large diffs are not rendered by default.

.planning/ROADMAP.md

Lines changed: 650 additions & 167 deletions
Large diffs are not rendered by default.

.planning/SHERLOCKS_DIARY_SPEC.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# Sherlock's Diary Feature Specification
2+
3+
## Overview
4+
A mobile-first note-taking extension for on-the-go investigators, accessible via a pencil icon in the sidebar.
5+
6+
## Feature Components
7+
8+
### 1. Sidebar Integration
9+
- Add pencil icon (`PenLine` from lucide-react) to the navigation
10+
- Route: `/cases/[id]/notebook` or a global `/notebook`
11+
12+
### 2. Sherlock's Diary UI
13+
A mobile-optimized interface with three main sections:
14+
15+
#### A. Note Entry
16+
- **Text Notes**: Simple textarea with save functionality
17+
- **Audio Notes**:
18+
- Record button using Web Audio API / MediaRecorder
19+
- Upload existing audio files
20+
- Auto-save recordings as MP3/WebM
21+
22+
#### B. Notes Directory
23+
- List view showing all notes for the current case
24+
- Each note displays:
25+
- AI-generated title (via Gemini)
26+
- AI-generated subtitle/summary
27+
- Date created
28+
- Type indicator (text/audio)
29+
- Sorted by date (newest first)
30+
31+
#### C. Chat Integration
32+
- Floating chatbot button (reuse existing Chatbot component)
33+
- Future: Notes will be integrated into chatbot memory
34+
35+
### 3. Export to Evidence
36+
Each note in the directory has an "Export as Evidence" button:
37+
- **Audio notes**: Upload MP3 directly to GCS evidence bucket
38+
- **Text notes**: Convert to PDF, then upload to GCS evidence bucket
39+
- Uses existing file upload infrastructure
40+
41+
### 4. Mobile-First UX
42+
- Optimized for small screens
43+
- Bottom navigation for main actions
44+
- Swipe gestures for note navigation
45+
- Responsive but mobile-first design
46+
47+
### 5. Device Detection
48+
- In middleware, detect mobile devices via User-Agent
49+
- On mobile login, redirect to `/cases/[id]/notebook` by default
50+
- Add toggle to switch to full desktop view
51+
52+
## Technical Implementation
53+
54+
### Backend API Endpoints
55+
```
56+
POST /api/cases/{case_id}/notes - Create note
57+
GET /api/cases/{case_id}/notes - List notes
58+
GET /api/cases/{case_id}/notes/{id} - Get single note
59+
PUT /api/cases/{case_id}/notes/{id} - Update note
60+
DELETE /api/cases/{case_id}/notes/{id} - Delete note
61+
POST /api/cases/{case_id}/notes/{id}/export - Export to evidence
62+
POST /api/cases/{case_id}/notes/generate-metadata - AI generate title/subtitle
63+
```
64+
65+
### Database Schema
66+
```sql
67+
CREATE TABLE case_notes (
68+
id UUID PRIMARY KEY,
69+
case_id UUID REFERENCES cases(id),
70+
user_id TEXT NOT NULL,
71+
type VARCHAR(10) NOT NULL, -- 'text' or 'audio'
72+
content TEXT, -- For text notes
73+
audio_storage_path VARCHAR(500), -- For audio notes
74+
audio_duration_seconds INTEGER,
75+
title VARCHAR(255), -- AI-generated
76+
subtitle TEXT, -- AI-generated
77+
is_exported BOOLEAN DEFAULT FALSE,
78+
exported_file_id UUID REFERENCES case_files(id),
79+
created_at TIMESTAMPTZ DEFAULT NOW(),
80+
updated_at TIMESTAMPTZ DEFAULT NOW()
81+
);
82+
83+
CREATE INDEX idx_case_notes_case_id ON case_notes(case_id);
84+
CREATE INDEX idx_case_notes_user_id ON case_notes(user_id);
85+
```
86+
87+
### Frontend Pages/Components
88+
- `/app/(app)/cases/[id]/notebook/page.tsx` - Main notebook page
89+
- `/components/notebook/` - All notebook components
90+
- `NoteEditor.tsx` - Text/audio note editor
91+
- `AudioRecorder.tsx` - Audio recording component
92+
- `NotesList.tsx` - Directory list view
93+
- `NoteCard.tsx` - Individual note display
94+
- `ExportButton.tsx` - Export to evidence button
95+
96+
### Gemini Integration
97+
Use existing Gemini configuration for title/subtitle generation:
98+
- Small context window call
99+
- Input: Note content (text or audio transcript)
100+
- Output: { title: string, subtitle: string }
101+
102+
## Implementation Phases
103+
104+
### Phase 1: Core Infrastructure ✅
105+
1. Database migration for case_notes table
106+
2. Backend API endpoints
107+
3. Basic frontend page structure
108+
109+
### Phase 2: Text Notes ✅
110+
1. Note editor component
111+
2. Create/edit/delete text notes
112+
3. Notes list view
113+
114+
### Phase 3: Audio Notes ✅
115+
1. Audio recorder component
116+
2. Audio upload to GCS
117+
3. Audio playback
118+
119+
### Phase 4: AI Metadata ✅
120+
1. Gemini integration for title/subtitle
121+
2. Auto-generate on save
122+
3. Manual regenerate option
123+
124+
### Phase 5: Export to Evidence ✅
125+
1. Text to PDF conversion (via reportlab)
126+
2. Audio export
127+
3. Evidence library integration
128+
129+
### Phase 6: Mobile Detection ✅
130+
1. Middleware device detection
131+
2. Mobile routing
132+
3. Full/mobile view toggle
133+
134+
---
135+
136+
## Implementation Status
137+
138+
### Backend Files Created/Modified:
139+
- `backend/alembic/versions/add_case_notes_table.py` - Database migration
140+
- `backend/app/models/note.py` - CaseNote SQLAlchemy model
141+
- `backend/app/models/__init__.py` - Added note model export
142+
- `backend/app/models/case.py` - Added notes relationship
143+
- `backend/app/schemas/notes.py` - Pydantic schemas for notes API
144+
- `backend/app/schemas/__init__.py` - Added notes schema exports
145+
- `backend/app/api/notes.py` - Full CRUD + export API endpoints
146+
- `backend/app/main.py` - Registered notes router
147+
148+
### Frontend Files Created/Modified:
149+
- `frontend/src/lib/api/notes.ts` - Notes API client
150+
- `frontend/src/hooks/useNotes.ts` - React hook for notes management
151+
- `frontend/src/hooks/index.ts` - Added useNotes export
152+
- `frontend/src/components/notebook/AudioRecorder.tsx` - Audio recording component
153+
- `frontend/src/components/notebook/NoteCard.tsx` - Note display card
154+
- `frontend/src/components/notebook/Notebook.tsx` - Main notebook interface
155+
- `frontend/src/components/notebook/ViewToggle.tsx` - Mobile/desktop view toggle
156+
- `frontend/src/components/notebook/index.ts` - Barrel exports
157+
- `frontend/src/components/app/case-nav-section.tsx` - Added Sherlock's Diary tab
158+
- `frontend/src/middleware.ts` - Mobile detection and routing
159+
- `frontend/src/app/(app)/cases/[id]/notebook/page.tsx` - Notebook page route
160+
161+
### Required Backend Dependencies:
162+
- `reportlab` - For PDF generation (text notes export)
163+
164+
### To Complete Setup:
165+
1. Run database migrations: `make migrate`
166+
2. Install reportlab: `cd backend && uv add reportlab`
167+
3. Restart backend: `make dev-backend`
168+
4. Test the notebook at: `/cases/{caseId}/notebook`
169+

0 commit comments

Comments
 (0)