chore: update client submodule and improve linting results
- Updated the client submodule to the latest commit for enhanced features and stability. - Cleared previous linting errors, resulting in zero errors and warnings in the linting reports. - Added a new documentation file outlining cross-stack wiring gaps and implementation checklists for gRPC RPCs.
This commit is contained in:
567
docs/sprints/phase-ongoing/wiring.md
Normal file
567
docs/sprints/phase-ongoing/wiring.md
Normal file
@@ -0,0 +1,567 @@
|
||||
# Cross-Stack Wiring Gaps Analysis
|
||||
|
||||
> **Generated:** 2026-01-05
|
||||
> **Scope:** Frontend (TypeScript), Backend (Python), Desktop Client (Rust/Tauri)
|
||||
|
||||
This document identifies areas where the gRPC proto contract is not fully wired across all three layers.
|
||||
|
||||
---
|
||||
|
||||
## Core Features Status
|
||||
|
||||
The following features have their **individual RPCs wired** across all layers, but several lack **automatic orchestration** - they require manual user action to trigger.
|
||||
|
||||
| Feature | Proto RPC | Python Mixin | Rust Client | Rust Commands | TS Adapter | Orchestration | Notes |
|
||||
|---------|-----------|--------------|-------------|---------------|------------|---------------|-------|
|
||||
| **Transcription** | StreamTranscription | StreamingMixin | streaming/ | recording/ | ✅ | ✅ Auto | Real-time during recording |
|
||||
| **Summaries** | GenerateSummary | SummarizationMixin | meetings.rs | summary.rs | ✅ | ⚠️ Manual | **GAP-W05**: No auto-trigger after recording |
|
||||
| **Action Items** | (via Summary) | (via Summary) | (via Summary) | (via Summary) | ✅ | ⚠️ Manual | Depends on summary generation |
|
||||
| **Annotations** | Add/Get/List/Update/Delete | AnnotationMixin | annotations.rs | annotation.rs | ✅ | ✅ User-driven | CRUD by design |
|
||||
| **Preferences** | Get/SetPreferences | PreferencesMixin | preferences.rs | preferences.rs | ✅ | ✅ Auto | Sync on change |
|
||||
| **Meetings** | Create/Get/List/Stop/Delete | MeetingMixin | meetings.rs | meeting.rs | ✅ | ✅ Auto | Full lifecycle |
|
||||
| **Export** | ExportTranscript | ExportMixin | annotations.rs | export.rs | ✅ | ✅ User-driven | On-demand by design |
|
||||
| **Diarization** | Refine/Rename/Status/Cancel | DiarizationMixin | diarization.rs | diarization.rs | ✅ | ⚠️ Manual | **GAP-W05**: No auto-trigger after recording |
|
||||
| **Entities (NER)** | Extract/Update/Delete | EntitiesMixin | annotations.rs | entities.rs | ✅ | ⚠️ Manual | **GAP-W05**: Auto-extract disabled |
|
||||
| **Calendar** | List/Providers/OAuth | CalendarMixin | calendar.rs | calendar.rs | ✅ | ✅ Auto | Sync scheduled |
|
||||
| **Webhooks** | Register/List/Update/Delete | WebhooksMixin | webhooks.rs | webhooks.rs | ✅ | ✅ Auto | Fire on events |
|
||||
| **Projects** | Full CRUD + Membership | ProjectMixin | projects.rs | projects.rs | ✅ | ✅ User-driven | CRUD by design |
|
||||
| **Identity** | GetCurrentUser | IdentityMixin | identity.rs | identity.rs | ✅ | ✅ Auto | On connection |
|
||||
| **Observability** | Logs/Metrics | ObservabilityMixin | observability.rs | observability.rs | ✅ | ✅ Auto | Background |
|
||||
| **Sync** | Start/Status/History | SyncMixin | sync.rs | sync.rs | ✅ | ✅ Auto | Scheduled |
|
||||
|
||||
**Legend:**
|
||||
- ✅ **Auto**: Triggers automatically at the appropriate time
|
||||
- ✅ **User-driven**: Requires user action by design (CRUD, export, etc.)
|
||||
- ⚠️ **Manual**: Requires manual trigger but should be automatic
|
||||
|
||||
### Tasks Page Note
|
||||
|
||||
The "Tasks" feature (`/tasks` page) aggregates **action items from summaries** across meetings. There is no separate Task entity in the proto - tasks are derived from `Summary.action_items` which includes:
|
||||
- `text`: Action item description
|
||||
- `assignee`: Person assigned (if mentioned)
|
||||
- `due_date`: Due date (if mentioned)
|
||||
- `priority`: Priority level (high/medium/low)
|
||||
- `segment_ids`: Links to transcript segments
|
||||
|
||||
Task completion status is stored in local preferences (`completed_tasks`).
|
||||
|
||||
**Note:** Tasks only appear after summaries are generated, which currently requires manual triggering (see GAP-W05).
|
||||
|
||||
---
|
||||
|
||||
## Identified Gaps
|
||||
|
||||
## Summary
|
||||
|
||||
| Gap ID | Feature | Severity | Backend | Rust Client | TS Adapter | Desktop Testing |
|
||||
|--------|---------|----------|---------|-------------|------------|-----------------|
|
||||
| GAP-W05 | Post-Processing Orchestration | **Critical** | ✅ | ✅ | ❌ Missing | Required |
|
||||
| GAP-W01 | OIDC Provider Management | **Critical** | ✅ | ❌ None | ❌ None | Required |
|
||||
| GAP-W02 | Workspace gRPC Methods | Informational | ✅ | ⚠️ Fallback | ✅ | Not Required |
|
||||
| GAP-W03 | Preferences Sync Constants | Low | ✅ | ✅ | Bypassed | Not Required |
|
||||
| ~~GAP-W04~~ | ~~OAuth Loopback Adapter~~ | ~~Removed~~ | — | — | — | — |
|
||||
|
||||
**Audit Notes (2026-01-05):**
|
||||
- GAP-W01: Sprint-17 doc claims client wiring done, but verification shows **no client files exist**
|
||||
- GAP-W02: Downgraded - intentional local-first fallback behavior, not a bug
|
||||
- GAP-W04: **Removed** - `use-oauth-flow.ts` directly invokes `initiate_oauth_loopback` (line 193); it works
|
||||
|
||||
---
|
||||
|
||||
## GAP-W05: Post-Processing Orchestration (Critical)
|
||||
|
||||
> **Full Details:** See [sprint-gap-011-post-processing-pipeline/README.md](./sprint-gap-011-post-processing-pipeline/README.md)
|
||||
|
||||
### Description
|
||||
|
||||
After a meeting recording completes, the system fails to automatically trigger post-processing workflows (summarization, entity extraction, diarization refinement). Users see only raw recordings without automatic transcriptions, summaries, or extracted intelligence. **The architecture has all the RPC components but lacks orchestration to connect them.**
|
||||
|
||||
### The Disconnect
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ WHAT EXISTS (Individual RPCs - All Working) │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ GenerateSummary ─────────► Manual button click only │
|
||||
│ ExtractEntities ─────────► Auto-extract disabled │
|
||||
│ RefineSpeakerDiarization ► Manual button click only │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ WHAT'S MISSING (Orchestration Layer) │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ No usePostProcessing hook │
|
||||
│ Recording.tsx navigates away immediately after stop │
|
||||
│ MeetingDetail only fetches, doesn't trigger generation │
|
||||
│ No ProcessingStatus component │
|
||||
│ Summary progress events emitted but not listened to │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Implementation Status
|
||||
|
||||
| Layer | File | Status |
|
||||
|-------|------|--------|
|
||||
| **Python Backend** | `src/noteflow/grpc/_mixins/summarization.py` | ✅ RPC works |
|
||||
| **Python Backend** | `src/noteflow/grpc/_mixins/entities.py` | ✅ RPC works |
|
||||
| **Python Backend** | `src/noteflow/grpc/_mixins/diarization/_mixin.py` | ✅ RPC works |
|
||||
| **Rust gRPC Client** | `client/src-tauri/src/grpc/client/meetings.rs` | ✅ Wired |
|
||||
| **Rust Commands** | `client/src-tauri/src/commands/summary.rs` | ✅ Emits progress events |
|
||||
| **TS Adapter** | `client/src/api/tauri-adapter.ts` | ✅ All methods available |
|
||||
| **TS Hooks** | `client/src/hooks/use-entity-extraction.ts` | ⚠️ Auto-extract disabled |
|
||||
| **TS Hooks** | `client/src/hooks/use-diarization.ts` | ⚠️ No auto-start |
|
||||
| **TS Pages** | `client/src/pages/Recording.tsx:313-346` | ❌ Navigates immediately |
|
||||
| **TS Pages** | `client/src/pages/MeetingDetail.tsx:79-98` | ❌ Fetches only, no triggers |
|
||||
| **TS Orchestration** | N/A | ❌ `usePostProcessing` hook missing |
|
||||
| **TS Progress** | N/A | ❌ `ProcessingStatus` component missing |
|
||||
|
||||
### Code Excerpts
|
||||
|
||||
**Recording.tsx immediately navigates away:**
|
||||
```typescript
|
||||
// client/src/pages/Recording.tsx:313-346
|
||||
const stopRecording = async () => {
|
||||
setIsRecording(false);
|
||||
streamRef.current?.close();
|
||||
|
||||
const stoppedMeeting = await api.stopMeeting(meeting.id);
|
||||
setMeeting(stoppedMeeting);
|
||||
|
||||
// GAP: Immediately navigates away - no processing triggered
|
||||
navigate(
|
||||
projectId
|
||||
? `/projects/${projectId}/meetings/${meeting.id}`
|
||||
: '/projects'
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
**MeetingDetail only fetches existing data:**
|
||||
```typescript
|
||||
// client/src/pages/MeetingDetail.tsx:79-98
|
||||
useEffect(() => {
|
||||
const loadMeeting = async () => {
|
||||
const data = await getAPI().getMeeting({
|
||||
meeting_id: id,
|
||||
include_segments: true,
|
||||
include_summary: true, // Fetches existing, doesn't generate
|
||||
});
|
||||
setMeeting(data.meeting);
|
||||
setSegments(data.segments || []);
|
||||
setSummary(data.summary); // null if not generated - stays null
|
||||
};
|
||||
|
||||
loadMeeting();
|
||||
}, [id]);
|
||||
```
|
||||
|
||||
**Auto-extract feature exists but is disabled:**
|
||||
```typescript
|
||||
// client/src/hooks/use-entity-extraction.ts:116-121
|
||||
useEffect(() => {
|
||||
if (autoExtract && meetingId && meetingState === 'completed') {
|
||||
extract(false); // This would work!
|
||||
}
|
||||
}, [autoExtract, meetingId, meetingState, extract]);
|
||||
|
||||
// client/src/pages/MeetingDetail.tsx:72-76
|
||||
const { extract: extractEntities } = useEntityExtraction({
|
||||
meetingId: id,
|
||||
meetingTitle: meeting?.title,
|
||||
meetingState: meeting?.state,
|
||||
// autoExtract: true <-- MISSING - feature disabled
|
||||
});
|
||||
```
|
||||
|
||||
**Summary progress events emitted but ignored:**
|
||||
```rust
|
||||
// client/src-tauri/src/commands/summary.rs:96-134
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let elapsed_s = start.elapsed().as_secs();
|
||||
|
||||
// Emits event but no React component listens
|
||||
emit_summary_progress(app.clone(), meeting_id.clone(), elapsed_s);
|
||||
|
||||
if elapsed_s >= 300 { break; }
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Blockers
|
||||
|
||||
1. **No orchestration hook** (`usePostProcessing`) to coordinate processing after recording
|
||||
2. **Immediate navigation** in Recording.tsx prevents client-side orchestration
|
||||
3. **Auto-extract disabled** - simple fix but not done
|
||||
4. **No progress UI** - summary progress events ignored
|
||||
5. **No ProcessingStatus tracking** on Meeting entity
|
||||
|
||||
### Required Changes
|
||||
|
||||
**Quick Wins (No new code):**
|
||||
1. Enable `autoExtract: true` in MeetingDetail.tsx entity extraction hook
|
||||
|
||||
**New Components:**
|
||||
1. Create `client/src/hooks/use-post-processing.ts` - Orchestrates summary + entities + diarization
|
||||
2. Create `client/src/components/meeting/processing-status.tsx` - Progress UI
|
||||
|
||||
**Modifications:**
|
||||
1. `client/src/pages/Recording.tsx` - Trigger processing before/after navigation
|
||||
2. `client/src/pages/MeetingDetail.tsx` - Wire `usePostProcessing` hook
|
||||
3. `client/src/hooks/use-diarization.ts` - Add auto-start capability
|
||||
4. `src/noteflow/domain/entities/meeting.py` - Add `ProcessingStatus` dataclass
|
||||
5. `src/noteflow/grpc/proto/noteflow.proto` - Add `ProcessingStatus` message
|
||||
|
||||
### Desktop Testing Required
|
||||
|
||||
**Yes** - The full recording → stop → processing → display flow must be tested on the desktop app to verify:
|
||||
- Processing triggers after recording stops
|
||||
- Progress events display correctly
|
||||
- Failed processing steps don't block others
|
||||
- Navigation and state transitions work correctly
|
||||
|
||||
---
|
||||
|
||||
## GAP-W01: OIDC Provider Management (Sprint 17)
|
||||
|
||||
### Description
|
||||
|
||||
The OIDC provider management feature is fully implemented in the Python backend but has **no client wiring at all**. The Sprint-17 documentation claims client files were created, but **verification shows they do not exist**.
|
||||
|
||||
### Documentation vs Reality
|
||||
|
||||
**Sprint-17 doc claims (lines 97-106):**
|
||||
- `client/src/api/types/oidc.ts` - **DOES NOT EXIST**
|
||||
- `client/src/hooks/use-oidc-providers.ts` - **DOES NOT EXIST**
|
||||
- `client/src/components/settings/oidc-providers-section.tsx` - **DOES NOT EXIST**
|
||||
- OIDC methods in `tauri-adapter.ts` - **NOT PRESENT**
|
||||
- 9 OIDC constants in `tauri-constants.ts` - **NOT PRESENT**
|
||||
|
||||
**What actually exists:**
|
||||
- Generic `Integration` type with `oidc_config` field in `integration-config-panel.tsx`
|
||||
- This saves to **local preferences only** - no gRPC calls
|
||||
|
||||
### User Flow Trace
|
||||
|
||||
```
|
||||
User Action What Actually Happens
|
||||
─────────────────────────────────────────────────────────────────────────
|
||||
Settings → OIDC tab Shows generic Integration panel
|
||||
(integration-config-panel.tsx:660-810)
|
||||
|
||||
Configure issuer_url, client_id Updates local state only
|
||||
(onUpdate → preferences.updateIntegration)
|
||||
|
||||
Click "Test OIDC Connection" FAKE TEST:
|
||||
1. Sleep 1.5 seconds (line 205)
|
||||
2. Check if fields filled
|
||||
3. Update local preferences
|
||||
4. Show "Connection test passed" toast
|
||||
|
||||
NO gRPC call to backend
|
||||
NO actual OIDC discovery
|
||||
NO token validation
|
||||
|
||||
Save/Close Stored in localStorage only
|
||||
Backend has no knowledge of provider
|
||||
```
|
||||
|
||||
### Code Evidence
|
||||
|
||||
**The "test" is completely fake:**
|
||||
```typescript
|
||||
// client/src/components/settings/integrations-section.tsx:203-226
|
||||
const handleTestIntegration = async (integration: Integration) => {
|
||||
setTestingIntegration(integration.id);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500)); // Just sleeps!
|
||||
|
||||
if (hasRequiredIntegrationFields(integration)) {
|
||||
toast({
|
||||
title: 'Connection test passed', // Lies to user
|
||||
description: `${integration.name} is configured correctly`,
|
||||
});
|
||||
preferences.updateIntegration(integration.id, { status: 'connected' });
|
||||
}
|
||||
// NO gRPC call anywhere
|
||||
};
|
||||
```
|
||||
|
||||
### Proto RPCs (7 endpoints - Backend Only)
|
||||
|
||||
```protobuf
|
||||
// src/noteflow/grpc/proto/noteflow.proto:90-96
|
||||
rpc RegisterOidcProvider(RegisterOidcProviderRequest) returns (OidcProviderProto);
|
||||
rpc ListOidcProviders(ListOidcProvidersRequest) returns (ListOidcProvidersResponse);
|
||||
rpc GetOidcProvider(GetOidcProviderRequest) returns (OidcProviderProto);
|
||||
rpc UpdateOidcProvider(UpdateOidcProviderRequest) returns (OidcProviderProto);
|
||||
rpc DeleteOidcProvider(DeleteOidcProviderRequest) returns (DeleteOidcProviderResponse);
|
||||
rpc RefreshOidcDiscovery(RefreshOidcDiscoveryRequest) returns (RefreshOidcDiscoveryResponse);
|
||||
rpc ListOidcPresets(ListOidcPresetsRequest) returns (ListOidcPresetsResponse);
|
||||
```
|
||||
|
||||
### Implementation Status
|
||||
|
||||
| Layer | File | Status |
|
||||
|-------|------|--------|
|
||||
| **Python Backend** | `src/noteflow/grpc/_mixins/oidc.py` | ✅ Complete (7 RPCs) |
|
||||
| **Python Tests** | `tests/grpc/test_oidc_mixin.py` | ✅ 27 tests passing |
|
||||
| **Rust gRPC Client** | `client/src-tauri/src/grpc/client/mod.rs` | ❌ No `oidc` module |
|
||||
| **Rust Commands** | `client/src-tauri/src/commands/` | ❌ No `oidc.rs` |
|
||||
| **TS Constants** | `client/src/api/tauri-constants.ts` | ❌ No OIDC commands |
|
||||
| **TS Interface** | `client/src/api/interface.ts` | ❌ No OIDC methods |
|
||||
| **TS Adapter** | `client/src/api/tauri-adapter.ts` | ❌ No OIDC implementation |
|
||||
| **TS Types** | `client/src/api/types/oidc.ts` | ❌ Does not exist |
|
||||
| **TS Hooks** | `client/src/hooks/use-oidc-providers.ts` | ❌ Does not exist |
|
||||
| **UI Component** | `client/src/components/settings/oidc-providers-section.tsx` | ❌ Does not exist |
|
||||
|
||||
### Impact
|
||||
|
||||
- **User deception**: UI shows "Connection test passed" when nothing was tested
|
||||
- **No SSO functionality**: OIDC providers cannot actually authenticate users
|
||||
- **Sprint doc inaccuracy**: Claimed deliverables don't exist
|
||||
|
||||
### Blockers
|
||||
|
||||
1. **No Rust gRPC wrapper module** for OIDC operations
|
||||
2. **No Tauri commands** to invoke OIDC RPCs
|
||||
3. **No TypeScript adapter methods** to call Tauri commands
|
||||
4. **No dedicated OIDC UI component** (current UI uses generic Integration panel)
|
||||
|
||||
### Required Changes
|
||||
|
||||
1. Create `client/src-tauri/src/grpc/client/oidc.rs` wrapper
|
||||
2. Create `client/src-tauri/src/commands/oidc.rs` with Tauri commands
|
||||
3. Add OIDC constants to `client/src/api/tauri-constants.ts`
|
||||
4. Add OIDC methods to `NoteFlowAPI` interface and `tauri-adapter.ts`
|
||||
5. Create `client/src/api/types/oidc.ts` with proper types
|
||||
6. Create `client/src/hooks/use-oidc-providers.ts`
|
||||
7. Create `client/src/components/settings/oidc-providers-section.tsx`
|
||||
8. **Update Sprint-17 doc** to reflect actual vs claimed deliverables
|
||||
|
||||
### Desktop Testing Required
|
||||
|
||||
**Yes** - OIDC involves OAuth redirects, token exchange, and discovery that must be tested end-to-end.
|
||||
|
||||
---
|
||||
|
||||
## GAP-W02: Workspace Management via gRPC (Informational)
|
||||
|
||||
### Description
|
||||
|
||||
The workspace management RPCs are implemented in Python. The Rust commands return local defaults instead of calling gRPC. **This is intentional local-first behavior.**
|
||||
|
||||
### User Flow Trace
|
||||
|
||||
```
|
||||
User Action What Actually Happens
|
||||
─────────────────────────────────────────────────────────────────────────
|
||||
App starts workspace-context.tsx:71-102
|
||||
├─ Calls api.getCurrentUser()
|
||||
├─ Calls api.listWorkspaces()
|
||||
└─ Falls back to local defaults if error
|
||||
|
||||
Click workspace dropdown workspace-switcher.tsx calls switchWorkspace()
|
||||
|
||||
Select different workspace workspace-context.tsx:108-133
|
||||
├─ Calls api.switchWorkspace(workspaceId)
|
||||
├─ Uses response.workspace if available
|
||||
├─ Falls back to local list match
|
||||
└─ Persists to localStorage
|
||||
|
||||
Backend unavailable ✅ Works - uses fallback workspace
|
||||
Backend available ✅ Works - uses backend response
|
||||
```
|
||||
|
||||
### Why This Is Not A Bug
|
||||
|
||||
The `workspace-context.tsx` implementation (lines 71-102, 108-133) demonstrates **graceful degradation**:
|
||||
|
||||
```typescript
|
||||
// workspace-context.tsx:81-82
|
||||
const availableWorkspaces =
|
||||
workspaceResponse.workspaces.length > 0 ? workspaceResponse.workspaces : [fallbackWorkspace];
|
||||
```
|
||||
|
||||
When the backend returns real workspaces, they're used. When unavailable, the app continues working with local defaults.
|
||||
|
||||
### Status: Design Decision Complete
|
||||
|
||||
| Behavior | Status |
|
||||
|----------|--------|
|
||||
| Local-first operation | ✅ Implemented |
|
||||
| Backend integration | ⚠️ Optional - could add gRPC-first with fallback |
|
||||
| User experience | ✅ Seamless - works offline and online |
|
||||
|
||||
### Recommendation
|
||||
|
||||
No action required unless multi-user workspace sync is needed. The current implementation correctly prioritizes local-first operation while still accepting backend responses when available.
|
||||
|
||||
### Desktop Testing Required
|
||||
|
||||
**No** - behavior is intentional and works correctly.
|
||||
|
||||
---
|
||||
|
||||
## GAP-W03: Preferences Sync Constants
|
||||
|
||||
### Description
|
||||
|
||||
The preferences sync commands exist in Rust but are not registered in `TauriConstants` or the `NoteFlowAPI` interface. They are called directly via `invoke()` in `preferences-sync.ts`.
|
||||
|
||||
### Tauri Commands
|
||||
|
||||
```rust
|
||||
// client/src-tauri/src/commands/preferences.rs:40-66
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
pub async fn get_preferences_sync(/* ... */) -> Result<PreferencesSyncResult> { /* ... */ }
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
pub async fn set_preferences_sync(/* ... */) -> Result<SetPreferencesResult> { /* ... */ }
|
||||
```
|
||||
|
||||
### Usage (Bypasses Adapter)
|
||||
|
||||
```typescript
|
||||
// client/src/lib/preferences-sync.ts:148-151
|
||||
const response = await invoke<GetPreferencesResult>('get_preferences_sync', {
|
||||
keys: keys ?? [],
|
||||
});
|
||||
|
||||
// client/src/lib/preferences-sync.ts:200-205
|
||||
const response = await invoke<SetPreferencesResult>('set_preferences_sync', {
|
||||
preferences: encoded,
|
||||
if_match: options?.force ? null : meta.etag,
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Assessment
|
||||
|
||||
**Low severity** - Functionality works, but pattern is inconsistent with the rest of the codebase.
|
||||
|
||||
### Recommendation
|
||||
|
||||
Add to `TauriConstants` for consistency:
|
||||
```typescript
|
||||
GET_PREFERENCES_SYNC: 'get_preferences_sync',
|
||||
SET_PREFERENCES_SYNC: 'set_preferences_sync',
|
||||
```
|
||||
|
||||
### Desktop Testing Required
|
||||
|
||||
Optional - basic functionality already works.
|
||||
|
||||
---
|
||||
|
||||
## ~~GAP-W04: OAuth Loopback Adapter Method~~ (Resolved)
|
||||
|
||||
### Status: **REMOVED** - Works correctly
|
||||
|
||||
### User Flow Trace
|
||||
|
||||
```
|
||||
User Action What Actually Happens
|
||||
─────────────────────────────────────────────────────────────────────────
|
||||
Settings → Calendar → Connect integrations-section.tsx:131
|
||||
└─ Calls initiateAuth(provider)
|
||||
|
||||
initiateAuth runs use-oauth-flow.ts:163-243
|
||||
├─ Checks isTauriEnvironment()
|
||||
├─ If Tauri: invoke('initiate_oauth_loopback')
|
||||
│ (lines 189-193)
|
||||
└─ If browser: window.open(authUrl)
|
||||
|
||||
Rust handles OAuth calendar.rs:73-77
|
||||
├─ Starts local HTTP server
|
||||
├─ Opens browser with OAuth URL
|
||||
├─ Waits for callback
|
||||
└─ Completes OAuth with server
|
||||
|
||||
Success use-oauth-flow.ts:197-208
|
||||
├─ Fetches connection status
|
||||
└─ Updates state to 'connected'
|
||||
```
|
||||
|
||||
### Why This Was Incorrectly Flagged
|
||||
|
||||
The original analysis only checked if the method was in `tauri-adapter.ts`. It is not - but `use-oauth-flow.ts` **directly invokes** the Tauri command:
|
||||
|
||||
```typescript
|
||||
// use-oauth-flow.ts:189-193
|
||||
const result = await invoke<{
|
||||
success: boolean;
|
||||
integration_id: string | null;
|
||||
error_message: string | null;
|
||||
}>('initiate_oauth_loopback', { provider });
|
||||
```
|
||||
|
||||
This pattern (direct `invoke()` bypassing the adapter) is used intentionally for specialized flows. The feature **works correctly**.
|
||||
|
||||
### Lesson Learned
|
||||
|
||||
Checking if a method exists in an adapter is insufficient. Must trace actual user flows to verify functionality.
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
### For GAP-W05 (Post-Processing Orchestration) Implementation
|
||||
|
||||
**Quick Win (Day 1):**
|
||||
- [ ] Enable `autoExtract: true` in MeetingDetail.tsx
|
||||
|
||||
**Client Components (Days 2-4):**
|
||||
- [ ] Create `client/src/hooks/use-post-processing.ts`
|
||||
- [ ] Create `client/src/components/meeting/processing-status.tsx`
|
||||
- [ ] Add summary progress event listener
|
||||
- [ ] Add auto-diarization trigger to `use-diarization.ts`
|
||||
- [ ] Wire `usePostProcessing` into MeetingDetail.tsx
|
||||
- [ ] Update Recording.tsx to handle processing transitions
|
||||
|
||||
**Backend (Days 3-5):**
|
||||
- [ ] Add `ProcessingStatus` dataclass to Meeting entity
|
||||
- [ ] Add `ProcessingStatus` message to proto
|
||||
- [ ] Regenerate proto stubs
|
||||
- [ ] Update GetMeeting to include processing status
|
||||
- [ ] Add `ENTITIES_EXTRACTED` webhook event
|
||||
- [ ] Add `DIARIZATION_COMPLETED` webhook event
|
||||
|
||||
**Testing:**
|
||||
- [ ] Unit test: `usePostProcessing` hook state transitions
|
||||
- [ ] Unit test: `ProcessingStatus` component rendering
|
||||
- [ ] Integration test: Full post-processing flow
|
||||
- [ ] E2E test: Recording → Processing → Complete flow on desktop
|
||||
|
||||
---
|
||||
|
||||
### For GAP-W01 (OIDC) Implementation
|
||||
|
||||
- [ ] Create Rust gRPC client module (`grpc/client/oidc.rs`)
|
||||
- [ ] Add module to `grpc/client/mod.rs`
|
||||
- [ ] Create Rust command handlers (`commands/oidc.rs`)
|
||||
- [ ] Register commands in Tauri
|
||||
- [ ] Add types to `grpc/types/`
|
||||
- [ ] Add constants to `tauri-constants.ts`
|
||||
- [ ] Add methods to `interface.ts`
|
||||
- [ ] Implement in `tauri-adapter.ts`
|
||||
- [ ] Add mock implementation in `mock-adapter.ts`
|
||||
- [ ] Test OIDC flow on desktop app
|
||||
|
||||
### General Wiring Checklist
|
||||
|
||||
When adding a new gRPC RPC:
|
||||
|
||||
1. **Proto** → `noteflow.proto` + regenerate stubs
|
||||
2. **Python** → Create/update mixin in `_mixins/`
|
||||
3. **Rust gRPC Client** → Add method to appropriate module in `grpc/client/`
|
||||
4. **Rust Types** → Add types to `grpc/types/`
|
||||
5. **Rust Commands** → Add Tauri command in `commands/`
|
||||
6. **TS Constants** → Add command name to `tauri-constants.ts`
|
||||
7. **TS Types** → Add request/response types to `api/types/`
|
||||
8. **TS Interface** → Add method signature to `interface.ts`
|
||||
9. **TS Adapter** → Implement in `tauri-adapter.ts`
|
||||
10. **Mock Adapter** → Add mock implementation for browser dev
|
||||
11. **Cached Adapter** → Add offline implementation or explicitly reject
|
||||
12. **Tests** → Add tests at each layer
|
||||
Reference in New Issue
Block a user