This commit is contained in:
2026-01-22 04:40:05 +00:00
parent fc7bbd0ea2
commit 073b70cc39
197 changed files with 18236 additions and 27240 deletions

View File

@@ -0,0 +1,66 @@
<section name="problem">
Review Bugfinder feedback in docs/sprints/phase-ongoing/Bugfinder/rpt1-8 to identify root causes across
Analytics, Home, Meetings, People, Post Meeting, and Tasks pages, then outline ideal fixes that restore
data visibility, interaction, and persistence.
</section>
<section name="findings">
- analytics_reports (branch finding: treat rpt1-rpt3 as equally urgent)
- rpt1 Analytics - Meetings: totals and charts depend on meeting.segments; ListMeetings returns
include_segments=false and no summary, so word counts and speaker stats remain zero. Root cause in
src/noteflow/grpc/mixins/meeting/meeting_mixin.py#L197 and client/src/pages/Analytics.tsx#L75.
- rpt2 Analytics - Performance: metrics come only from server psutil snapshots and heuristic latency
(src/noteflow/infrastructure/metrics/collector.py, client/src/components/features/analytics/performance-tab.tsx).
No client/server toggle exists, and high resource usage likely reflects ongoing post-processing or
other server workloads rather than a UI issue.
- rpt3 Analytics - Speech: entity map and speech patterns rely on meeting.segments/words in
client/src/components/features/analytics/speech-analysis-tab.tsx. With empty segments, entities are
blank and speech metrics show zeros; heuristics are placeholders rather than NER output.
- home_meetings (branch finding: missing data wiring and core interactions)
- rpt4 Home Page: MeetingCard renders meeting.segments[0]?.text so all cards show "No transcript
available" when ListMeetings omits segments (client/src/components/features/meetings/meeting-card.tsx).
Tasks come from meeting.summary.action_items, but ListMeetings omits summary, so tasks disappear on
reload (client/src/pages/Home.tsx, client/src/pages/Tasks.tsx). The Ask AI button has no handler
(client/src/components/layout/app-sidebar.tsx). Home uses HOME_RECENT_MEETINGS_LIMIT=5 and slices to 6,
conflicting with the requested "last 3" (client/src/lib/constants/timing.ts, client/src/pages/Home.tsx).
Summary generation does not update meeting titles, and there is no calendar-link signal in meeting
payloads to gate that behavior.
- rpt5 Meetings: UI calls listMeetings(limit=MEETINGS_PAGE_LIMIT) once with no offset or pagination UI,
even though the API returns total_count (client/src/pages/Meetings.tsx).
- people_post_meeting (branch finding: People list never populating due to data layer)
- rpt6 People: aggregateSpeakers uses meeting.segments and preferences-only speaker names
(client/src/lib/audio/speaker.ts, client/src/pages/People.tsx). With listMeetings returning empty
segments, speaker metrics are all zero. Speaker renames are local only; the renameSpeaker API exists
but is not used.
- rpt7 Post Meeting: transcript row clicks only trigger playback in Tauri; in web they only toggle
selection (client/src/pages/meeting-detail/use-playback.ts). Speaker renaming is hidden behind
double-click on SpeakerBadge and is local-only (client/src/components/common/badges/speaker-badge.tsx).
Summary tab is concise and relegated to the right panel; no dedicated view for call metrics or
background process status. Action items and entities are read-only.
- tasks_page (branch finding: task list empty)
- rpt8 Tasks: tasks are derived from meeting.summary.action_items, but summaries are not included in
ListMeetings, so lists are empty after refresh. Completion is stored in preferences only, and the
UI supports list view only (client/src/pages/Tasks.tsx). Backend already has TaskModel but no gRPC
endpoints, so edits/ownership/kanban are not supported.
</section>
<section name="recommendation">
- Selected strategy: build dedicated aggregate endpoints for analytics, speaker stats, and tasks.
- Add gRPC endpoints (e.g., GetAnalyticsOverview, ListSpeakerStats, ListTasks) with pagination and
time-series responses to replace client-side aggregation of segments/summary fields.
- Client Analytics, People, Home, and Tasks pages should consume these aggregates instead of listMeetings.
- Selected strategy: TaskModel CRUD as source of truth for action items.
- On summary persistence, create/update TaskModel rows linked to action_items for provenance.
- Add task CRUD endpoints (list/update/status/delete) to support owner, priority, due date, and kanban.
- Remove preferences-only completion state; sync status updates through the API.
- Selected strategy: move the LLM summary into the transcript panel and repurpose the right panel.
- Transcript pane shows verbose summary as the primary view; summary generation should target higher
verbosity templates by default.
- Right panel becomes call metrics (model/tokens/latency) plus processing status (summary/entities/
diarization, background jobs) and editing surfaces for tasks/entities.
- Supporting fixes: wire Ask AI to a route or modal, add Meetings pagination using total_count, expose
renameSpeaker in the UI for persistent names, add Analytics client/server toggle for performance metrics,
and add calendar-link indicators so summary title updates only apply to non-calendar meetings.
- Transcript interaction: in web mode, highlight transcript rows on click and surface a desktop-playback
message until web playback is implemented.
</section>

View File

@@ -0,0 +1,9 @@
![pasted_image_04836a5d-3226-48bf-b3ec-527cd202fad0.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\04836a5d-3226-48bf-b3ec-527cd202fad0.png)
Issues
1. The header cards for Total Words and Unique Speakers has always been 0 and never changed
2. The graphs for Speaker Particiaption and Word Count Trends has never visualized any data

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -0,0 +1,10 @@
![pasted_image_fdffb3dd-d491-4e25-b16d-5c8e3e554d1b.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\fdffb3dd-d491-4e25-b16d-5c8e3e554d1b.png)
Issues
1. The UI here is actually fine, however this is about 30 mins after my last recording and resources are still nearly fully allocated still - I suspect something to be hanging
Changes
1. If the server and client are on differing IP addresses or devices, then there should be a switch or a toggle to view the Server or Client performance.

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

View File

@@ -0,0 +1,7 @@
![pasted_image_9b688a19-5857-40fc-832a-02ce5ca149ef.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\9b688a19-5857-40fc-832a-02ce5ca149ef.png)
Issues
1. Entity Word Map has always been blank
2. Speech patters are either mocked data or incomplete

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

View File

@@ -0,0 +1,16 @@
![pasted_image_88eda805-636e-4f39-8f35-bbb772d66f6b.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\88eda805-636e-4f39-8f35-bbb772d66f6b.png)
Issues
1. Every meeting says “No Transcript Available” no matter how long ago it finished processing
2. Tasks and action items dont seem to be persisted
3. Ask AI button does not seem to be working
Change requests
1. Home screen should only show the last 3 recorded meetings, it currenly shows 5.
2. Summarization task should also update the meeting title ONLY if the meeting isnt connected to a calendar appointments

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

View File

@@ -0,0 +1,5 @@
![pasted_image_89fd9ba7-6eeb-4508-89b3-4e72f8cf5b99.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\89fd9ba7-6eeb-4508-89b3-4e72f8cf5b99.png)
Issues
1. The past recordings displayed does not seem to be paginated and risks excessive resource consumption

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -0,0 +1,7 @@
![pasted_image_39c6141e-1ae5-44d8-9900-85fbd6755e99.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\39c6141e-1ae5-44d8-9900-85fbd6755e99.png)
Issues
1.  None of the diararized speakers identified in any of the meetings have ended up here.
2. Metrics dont seem to be gathered or stored

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -0,0 +1,18 @@
![pasted_image_43050846-ab72-4fa6-b35f-067f01a67155.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\43050846-ab72-4fa6-b35f-067f01a67155.png)
Issues
1. Clicking on a transcript line doesnt seem to do anything
2. No way to update speaker names
Changes
1. The LLM Generated Summary should be significantly more verbose and be whats displayed  as a primary tab in the current transcript component.
2. If the summary is moving to the transcript component, then executive summary should be replaced with call metrics, model/token usage, and any additional processes still running in the background.
3. Should be able to edit or delete the tasks and action items, identify owners, criticality,  and deadline.
1. similar capabilities should be extended to extracted entities

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

View File

@@ -0,0 +1,12 @@
![pasted_image_a4e73673-65d1-4e02-8a95-d1929974f342.png](file://C:\Users\PC\AppData\Roaming\CherryStudio\Data\Files\a4e73673-65d1-4e02-8a95-d1929974f342.png)
Issues
1. Tasks only show up when they are first extracted from a recording but never persisted past restarts.
Changes
1. Should be able to display in List, Card, and Kanban views
2. Tasks should display what meeting they came from and link to that post meeting summary

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -0,0 +1,399 @@
# Sprint Bugfinder Strategy B: Aggregates + Tasks + Summary Layout
> **Size**: XL | **Owner**: TBD | **Prerequisites**: `docs/plans/2026-01-21-bugfinder-design.md`
> **Phase**: Ongoing - Bugfinder follow-ups
---
## Open Issues & Prerequisites
> ✅ **Review Date**: 2026-01-21 — Strategy B selected for aggregates, TaskModel CRUD, and summary layout.
### Blocking Issues
| ID | Issue | Status | Resolution |
|----|-------|--------|------------|
| **B1** | None | ✅ | N/A |
### Design Gaps to Address
| ID | Gap | Resolution |
|----|-----|------------|
| G1 | Cache TTL and invalidation rules for analytics aggregates | Default to 60s TTL; invalidate on summary/segment writes if hook available |
| G2 | Task de-duplication key definition | Use meeting_id + normalized text; ignore case/punctuation |
| G3 | Summary verbosity default after layout move | Use existing template defaults, add client toggle in follow-up |
### Prerequisite Verification
| Prerequisite | Status | Notes |
|--------------|--------|-------|
| TaskModel exists in DB | ✅ | `src/noteflow/infrastructure/persistence/models/organization/task.py` |
| Action items persisted in summaries | ✅ | `src/noteflow/infrastructure/persistence/models/core/summary.py` |
| Meeting list supports project filters | ✅ | `src/noteflow/grpc/mixins/meeting/meeting_mixin.py` |
---
## Validation Status (2026-01-22)
### IMPLEMENTED
| Component | Status | Notes |
|-----------|--------|-------|
| Analytics aggregate endpoints | ✅ Implemented | `GetAnalyticsOverview`, `ListSpeakerStats` gRPC methods + repositories |
| Task CRUD API | ✅ Implemented | Domain entity, service, repository, and gRPC endpoints (`ListTasks`, `UpdateTask`) |
| Analytics UI | ✅ Implemented | `Analytics.tsx` uses `getAnalyticsOverview` and `listSpeakerStats` |
| People page | ✅ Implemented | `People.tsx` uses server-backed `listSpeakerStats` endpoint |
| Tasks page | ✅ Implemented | `Tasks.tsx` uses server-backed `listTasks` and `updateTask` |
### IMPLEMENTED (All Components Complete)
**Summary layout in transcript panel** has been implemented:
- Summary now appears in a collapsible section above the transcript
- Right panel tabs reduced to Entities and Notes only
- Summary supports compact horizontal grid layout
### PARTIALLY IMPLEMENTED
| Component | Status | Notes |
|-----------|--------|-------|
| Task storage | ✅ Complete | TaskModel in DB, API implemented, task ingestion from summary action items via `_ingest_tasks_from_summary` in `_generation_mixin.py` |
**Downstream impact**: Meeting Detail page (summary layout move).
---
## Objective
Provide server-backed analytics aggregates and task persistence so Analytics, People, and Tasks pages no
longer depend on loading meeting segments. Move the LLM summary into the transcript panel and repurpose
the right panel for metrics/status to align with Bugfinder feedback.
---
## Key Decisions
| Decision | Choice | Rationale |
|----------|--------|-----------|
| **Analytics data source** | Dedicated aggregate endpoints with in-memory caching | Scales better than listMeetings; avoids heavy payloads |
| **Task persistence** | TaskModel CRUD as source of truth | Enables edits, ownership, kanban, and sync across devices |
| **Summary layout** | Summary becomes primary content inside transcript panel | Matches requested UX priority and keeps metrics in sidebar |
| **Task dedupe** | Only new, deduped tasks on summary regen | Honors user directive; preserves user-edited tasks |
---
## What Already Exists
| Asset | Location | Implication |
|-------|----------|-------------|
| TaskModel schema + relationships | `src/noteflow/infrastructure/persistence/models/organization/task.py` | Reuse existing DB table and FK links |
| Action items in summaries | `src/noteflow/infrastructure/persistence/models/core/summary.py` | Seed tasks from action items |
| Summary save flow | `src/noteflow/grpc/mixins/summarization/_generation_mixin.py` | Hook task ingestion after summary persistence |
| Segment + word timing models | `src/noteflow/infrastructure/persistence/models/core/meeting.py` | Aggregate words/speakers for analytics |
| Usage aggregate query pattern | `src/noteflow/infrastructure/persistence/repositories/usage_event/_aggregations.py` | Reuse structure for analytics queries |
| Observability metrics endpoint | `src/noteflow/grpc/mixins/observability.py` | Co-locate performance data in analytics UI |
---
## Scope
| Task | Effort | Notes |
|------|--------|-------|
| **Domain Layer** | | |
| Add Task entity + TaskStatus enum | M | Align with DB statuses: open/done/dismissed |
| Add analytics DTOs (overview, speaker stats, speech metrics) | M | Plain dataclasses for service outputs |
| **Infrastructure Layer** | | |
| Build SqlAlchemyTaskRepository | L | CRUD, list filters (status, project_id(s), meeting_id) |
| Add analytics aggregation queries | L | Meeting trends, word counts, speaker stats, entity frequencies |
| Add index migration (tasks.status, tasks.meeting_id, segments.speaker_id) | M | Improve query performance |
| **Application Layer** | | |
| TaskService (CRUD + summary ingestion) | L | Create tasks from action items (dedupe) |
| AnalyticsService with TTL cache | L | Query aggregates, cache by workspace/project/date range |
| **API Layer** | | |
| Extend proto with analytics + task messages | L | New gRPC methods and enums |
| Add AnalyticsMixin + TaskMixin | L | Wire into `NoteFlowServicer` and stubs |
| Update Rust + TS generated clients | L | Regenerate stubs and adapters |
| **Client Layer** | | |
| Analytics page uses aggregate endpoints | M | Replace computeAnalytics with API data |
| People page uses speaker stats endpoint | M | Remove listMeetings dependency |
| Tasks page uses TaskModel data | L | Persist completion/status, keep project filters |
| Meeting detail layout: summary in transcript panel | L | Refactor transcript panel to include summary section |
**Total Effort**: XL (1-2 days)
---
## Domain Model
### Task Entity
```python
# src/noteflow/domain/entities/task.py
class TaskStatus(Enum):
"""Task lifecycle status."""
OPEN = "open"
DONE = "done"
DISMISSED = "dismissed"
@dataclass
class Task:
"""User-managed task derived from an action item."""
id: UUID
workspace_id: UUID
meeting_id: UUID | None
action_item_id: int | None
text: str
status: TaskStatus = TaskStatus.OPEN
assignee_person_id: UUID | None = None
due_date: datetime | None = None
priority: int = 0
completed_at: datetime | None = None
created_at: datetime = field(default_factory=utc_now)
updated_at: datetime = field(default_factory=utc_now)
```
**Constraints**:
- TaskStatus limited to open/done/dismissed (match DB constraint).
- Task dedupe key = meeting_id + normalized text (lowercase, trim, strip punctuation).
---
## API Schema
### Proto Additions (gRPC)
```protobuf
enum TaskStatus {
TASK_STATUS_UNSPECIFIED = 0;
TASK_STATUS_OPEN = 1;
TASK_STATUS_DONE = 2;
TASK_STATUS_DISMISSED = 3;
}
message Task {
string id = 1;
string meeting_id = 2;
int32 action_item_id = 3;
string text = 4;
TaskStatus status = 5;
string assignee_person_id = 6;
double due_date = 7;
int32 priority = 8;
double completed_at = 9;
}
message TaskWithMeeting {
Task task = 1;
string meeting_title = 2;
double meeting_created_at = 3;
string project_id = 4;
}
message ListTasksRequest {
repeated TaskStatus statuses = 1;
int32 limit = 2;
int32 offset = 3;
optional string project_id = 4;
repeated string project_ids = 5;
optional string meeting_id = 6;
}
message ListTasksResponse {
repeated TaskWithMeeting tasks = 1;
int32 total_count = 2;
}
message UpdateTaskRequest {
string task_id = 1;
string text = 2;
TaskStatus status = 3;
string assignee_person_id = 4;
double due_date = 5;
int32 priority = 6;
}
message UpdateTaskResponse {
Task task = 1;
}
message AnalyticsOverviewRequest {
double start_time = 1;
double end_time = 2;
optional string project_id = 3;
repeated string project_ids = 4;
}
message DailyMeetingStats {
string date = 1;
int32 meetings = 2;
double total_duration = 3;
int32 word_count = 4;
}
message AnalyticsOverviewResponse {
repeated DailyMeetingStats daily = 1;
int32 total_meetings = 2;
double total_duration = 3;
int32 total_words = 4;
int32 total_segments = 5;
int32 speaker_count = 6;
}
message SpeakerStat {
string speaker_id = 1;
string display_name = 2;
double total_time = 3;
int32 segment_count = 4;
int32 meeting_count = 5;
double avg_confidence = 6;
}
message ListSpeakerStatsRequest {
double start_time = 1;
double end_time = 2;
optional string project_id = 3;
repeated string project_ids = 4;
}
message ListSpeakerStatsResponse {
repeated SpeakerStat speakers = 1;
}
service NoteFlowService {
rpc ListTasks(ListTasksRequest) returns (ListTasksResponse);
rpc UpdateTask(UpdateTaskRequest) returns (UpdateTaskResponse);
rpc GetAnalyticsOverview(AnalyticsOverviewRequest) returns (AnalyticsOverviewResponse);
rpc ListSpeakerStats(ListSpeakerStatsRequest) returns (ListSpeakerStatsResponse);
}
```
---
## Migration Strategy
### Phase 1: Schema
1. Add index on `noteflow.tasks.status` and `noteflow.tasks.meeting_id` for list filters.
2. Add index on `noteflow.segments.speaker_id` for speaker aggregates.
### Phase 2: Backfill
1. Optional: backfill tasks from existing summaries by replaying summary action items per meeting.
### Migration Risks
| Risk | Mitigation |
|------|------------|
| Large segment tables slow aggregate queries | Use TTL cache + add indexes; consider materialized view later |
| Task dedupe false positives | Normalize text conservatively; log collisions |
---
## Shared Types & Reuse Notes
- **Aggregation pattern**: reuse SQL aggregation style from `src/noteflow/infrastructure/persistence/repositories/usage_event/_aggregations.py`.
- **Processing status UI**: reuse `ProcessingStatus` component in meeting detail sidebar.
- **Task priority badges**: reuse `client/src/components/common/PriorityBadge` and `client/src/types/task.ts`.
---
## UI Components (if applicable)
### Meeting Summary Section
```tsx
// client/src/pages/meeting-detail/summary-panel.tsx
export function SummaryPanel({ summary, summaryMeta, onGenerateSummary }: SummaryPanelProps) {
// Render executive summary, key points, and action items inside transcript panel
}
```
---
## Deliverables
### Backend
**Domain Layer**:
- [x] `src/noteflow/domain/entities/task.py` — Task entity + TaskStatus enum
**Infrastructure Layer**:
- [x] `src/noteflow/infrastructure/persistence/repositories/task_repo.py` — Task CRUD + list filters
- [x] `src/noteflow/infrastructure/persistence/repositories/analytics_repo.py` — aggregate queries
- [x] `src/noteflow/infrastructure/persistence/repositories/__init__.py` — export repositories
**Application Layer**:
- [x] `src/noteflow/application/services/tasks/service.py` — CRUD + summary ingestion
- [x] `src/noteflow/application/services/analytics/service.py` — cached aggregates
**API Layer**:
- [x] `src/noteflow/grpc/proto/noteflow.proto` — new messages + RPCs (TaskStatusProto, TaskProto, DailyMeetingStatsProto, SpeakerStatProto, etc.)
- [x] `src/noteflow/grpc/proto/noteflow_pb2.pyi` — Python type stubs for new proto types
- [x] `src/noteflow/grpc/mixins/analytics_mixin.py` — GetAnalyticsOverview + ListSpeakerStats
- [x] `src/noteflow/grpc/mixins/tasks.py` — ListTasks + UpdateTask
- [x] `src/noteflow/grpc/mixins/converters/_domain.py` — Task/Analytics proto converters
- [x] `src/noteflow/grpc/service.py` — register mixins (AnalyticsMixin, TasksMixin)
**Migrations**:
- [x] `src/noteflow/infrastructure/persistence/migrations/versions/v6w7x8y9z0a1_add_task_analytics_indexes.py` — indexes for tasks.status, tasks.meeting_id, segments.speaker_id
### Client
- [x] `client/src/api/types/features/analytics.ts` — analytics request/response types
- [x] `client/src/api/types/features/tasks.ts` — task request/response types
- [x] `client/src/api/interface.ts` — new API methods
- [x] `client/src/api/adapters/tauri/sections/analytics.ts` — analytics endpoint bridge
- [x] `client/src/api/adapters/tauri/sections/tasks.ts` — task CRUD bridge
- [x] `client/src-tauri/src/commands/analytics.rs` — Rust Tauri commands
- [x] `client/src-tauri/src/commands/tasks.rs` — Rust Tauri commands
- [x] `client/src-tauri/src/grpc/client/analytics.rs` — Rust gRPC client methods
- [x] `client/src-tauri/src/grpc/client/tasks.rs` — Rust gRPC client methods
- [x] `client/src-tauri/src/grpc/types/analytics.rs` — Rust type definitions
- [x] `client/src-tauri/src/grpc/types/tasks.rs` — Rust type definitions
- [x] `client/src/pages/Analytics.tsx` — use aggregates (uses getAnalyticsOverview, listSpeakerStats)
- [x] `client/src/pages/People.tsx` — use speaker stats (uses listSpeakerStats endpoint)
- [x] `client/src/pages/Tasks.tsx` — use TaskModel data (uses listTasks, updateTask)
- [x] `client/src/pages/meeting-detail/index.tsx` — summary in collapsible section above transcript
- [x] `client/src/pages/meeting-detail/summary-panel.tsx` — added compact mode with horizontal grid layout
---
## Test Strategy
### Fixtures to extend or create
- `tests/conftest.py`: add Task fixtures for task lists
- `tests/infrastructure/`: add repository fixtures for analytics aggregates
### Parameterized tests
- Task status transitions: open/done/dismissed
- Analytics filters: project_id vs project_ids vs none
### Core test cases
- **Domain**: TaskStatus normalization + completion timestamp logic
- **Service**: summary ingestion dedupe, analytics cache hits/misses
- **API**: ListTasks pagination + filters, GetAnalyticsOverview range queries
- **Integration**: task list after summary regenerate adds only new tasks
---
## Quality Gates
- [x] `pytest tests/application/test_task_service.py` passes (19 tests)
- [x] `pytest tests/application/test_analytics_service.py` passes (10 tests)
- [x] `pytest tests/grpc/test_tasks_mixin.py` passes (7 tests)
- [x] `pytest tests/grpc/test_analytics_mixin.py` passes (6 tests)
- [ ] `make quality` passes
- [x] No type suppression comments or loose typing introduced
---
## Post-Sprint
- [ ] Add kanban view for tasks and inline editing
- [ ] Add NER-based entity analytics (NamedEntityModel aggregates)
- [ ] Materialized analytics tables if cache misses become expensive