docs(map): transcript segments and preset prompts now sync
This commit is contained in:
parent
2aac10fc5d
commit
1f46c3de96
4 changed files with 8 additions and 8 deletions
|
|
@ -97,7 +97,7 @@ The existing `input-telemetry-handlers` and `suggestion-handlers` IPC extensions
|
|||
| `ConfigService` | electron-store `AppConfig` (`configGet/Set`, defaults) |
|
||||
| `LicenseService` | Freemium tiers, quotas (`daily_usage`), activation, upgrade prompts |
|
||||
| `CloudSyncService` | Supabase auth + lifecycle: per-user DB switching, local-mode import on first sign-in, Realtime (`ws` transport) + 5-min heartbeat, device check-in, debounced flush/pull triggers. The sync itself lives in `services/sync/` |
|
||||
| `services/sync/` | `SyncEngine` (backfill once per user DB → push outbox → pull by server-clock keyset cursor → tombstones → memo-tag reconcile), `sync-outbox` (`sync_outbox`/`sync_state` tables), `sync-adapters` (history, dictionary, meetings, meeting memos/documents, custom commands, templates), `memo-tag-sync`, `supabase-sync-remote`, `device-registration`, `realtime-transport`, `settings-sync` (mobile `user_settings` ↔ language/theme/defaultLLMAction/activeInstructionId), `audio-sync` (recording upload to the `audio` bucket, remote cleanup on delete), knowledge adapter (source-text chunks). Services record changes with `getCloudSyncService().pushOne/pushDelete(entity, id)`; pulled rows are applied without re-queuing |
|
||||
| `services/sync/` | `SyncEngine` (backfill once per user DB → push outbox → pull by server-clock keyset cursor → tombstones → memo-tag reconcile), `sync-outbox` (`sync_outbox`/`sync_state` tables), `sync-adapters` (history, dictionary, meetings, meeting memos/documents, custom commands, templates), `memo-tag-sync`, `supabase-sync-remote`, `device-registration`, `realtime-transport`, `settings-sync` (mobile `user_settings` ↔ language/theme/defaultLLMAction/activeInstructionId), `audio-sync` (recording upload to the `audio` bucket, remote cleanup on delete), knowledge adapter (source-text chunks), `transcript-sync` (meeting segments from the desktop transcript), `builtin-instruction-sync` (preset prompt edits ↔ server preset rows). Services record changes with `getCloudSyncService().pushOne/pushDelete(entity, id)`; pulled rows are applied without re-queuing |
|
||||
| `CloudSTTService` | Thin cloud STT wrapper over `D3ROCloudDriver` |
|
||||
| `UpdateService` | electron-updater (canonical Forgejo feed, channels, mandatory/full-vs-delta policy, staged rollout, restart dialog) |
|
||||
| `AutoLaunchService` | OS login-item auto-start |
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
## 1. Supabase project (`server/supabase/`)
|
||||
|
||||
- `config.toml` — project_id `d3ro-voice`; local ports API 55321 / DB 55322 / Studio 55323 / Inbucket 55324; DB major v17; storage 50 MiB; auth `site_url` localhost:5173, redirects include `https://d3ro.chanpaca.net` and `d3ro-voice://auth-callback`; external providers Google/GitHub/Apple; per-function `verify_jwt` settings; analytics off.
|
||||
- `seed.sql`, `migrations/` (65; production at `20260927000035` — it lagged at `0028` until 2026-09-27, GAP-OPS-01), `functions/` (23, all redeployed 2026-09-27), `functions/_shared/`, `tests/`, `deno.json`.
|
||||
- `seed.sql`, `migrations/` (66; production at `20260927000036` — it lagged at `0028` until 2026-09-27, GAP-OPS-01), `functions/` (23, all redeployed 2026-09-27), `functions/_shared/`, `tests/`, `deno.json`.
|
||||
|
||||
> Note: root `supabase/` contains only empty scaffolding (`.branches/`, `snippets/`). The real project lives under `server/supabase/`.
|
||||
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
| Core schema + RLS + auth triggers | initial tables, profiles, triggers |
|
||||
| Storage buckets | audio, meeting documents, exports |
|
||||
| Teams | team invites, membership, roles, `team_activities` feed (`20260913000033`) |
|
||||
| Cross-device sync | `20260927000035`: knowledge_documents tombstones/cursor/realtime + `pg_cron` job `prune-sync-tombstones` (daily 03:17 UTC). `20260927000034`: `sync_tombstones` (AFTER DELETE triggers on 8 synced tables, owner-read RLS, realtime, `prune_sync_tombstones_v1`), `history.revision` bump on content change, `meeting_memos.updated_at`, `(user_id, updated_at, id)` cursor indexes, client-keyed `sync_upsert/delete_user_template_v1`, realtime for `devices`/`memo_tags`/`custom_instructions`/`user_templates`, and `export_account_portability` projected back onto v1 archive keys |
|
||||
| Cross-device sync | `20260927000036`: preset prompt RPCs `sync_set_builtin_instruction_prompt_v1` / `sync_list_builtin_instructions_v1` + default SSOT `builtin_instruction_default_prompt_v1`. `20260927000035`: knowledge_documents tombstones/cursor/realtime + `pg_cron` job `prune-sync-tombstones` (daily 03:17 UTC). `20260927000034`: `sync_tombstones` (AFTER DELETE triggers on 8 synced tables, owner-read RLS, realtime, `prune_sync_tombstones_v1`), `history.revision` bump on content change, `meeting_memos.updated_at`, `(user_id, updated_at, id)` cursor indexes, client-keyed `sync_upsert/delete_user_template_v1`, realtime for `devices`/`memo_tags`/`custom_instructions`/`user_templates`, and `export_account_portability` projected back onto v1 archive keys |
|
||||
| Knowledge / RAG | `knowledge_documents`, `knowledge_chunks`, pgvector |
|
||||
| Notifications / push | push tokens, durable outbox |
|
||||
| Billing | Payple, subscriptions, payment provider events/operations (`'stripe'` provider values kept only for historical rows) |
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Status quick-reference: `[x]` done+verified · `[~]` partial/unverified · `[ ]`
|
|||
| AI-02 | Cloud LLM (Claude/OpenAI) | [x] | [x] | [x] | [x] | Desktop `PremiumLLMService`; web/mobile via `llm-proxy`; .NET `LlmProxyService` |
|
||||
| AI-03 | Auto Polish (cleanup/filler removal) | [x] | [~] | [~] | [x] | Desktop built-in; web/mobile via commands. Desktop Auto Polish is the plain `refine` action (`llm-prompts.ts:14`), not a custom instruction, so it was **not** affected by the 2026-09-21 instruction-prompt fix (AI-05); regression cases now pin `refine`/`summarize`/`grammar`/`expand` (`VoiceModeService.test.ts:435`, `llm-prompts.test.ts:153`) |
|
||||
| AI-04 | Translate / summarize / rephrase | [~] | [x] | [x] | [x] | Built-in instructions. **Desktop has two paths and only one of them worked.** The plain-action path (Settings → `defaultLLMAction`, `SettingsModal.tsx:653`) reads `BASE_SYSTEM_PROMPTS` directly and was always correct. The built-in *instruction* presets (`CustomInstructionService.ts:26/35/44/53/62`) ran through the custom-instruction path and inserted the instruction's own wording instead of the result — see AI-05. Fixed in `9c2b4d4` (2026-09-21), **not verified in a running app** (`11` GAP-LLM-02). Translate still always targets English: `AppConfig` has no target-language key and neither `language` (UI locale) nor `sttLanguage` (source language) can stand in (`llm-prompts.ts:37-52`, `11` GAP-LLM-01) |
|
||||
| AI-05 | Custom instructions (user commands) | [~] | [x] | [x] | [x] | Web `commands`; mobile `CommandsScreen` — both go through Edge Functions and are unaffected. **Desktop: the custom-instruction path never worked in any shipped release.** The instruction was passed in the `text` argument of `processText(text, action, targetLanguage, customPrompt)` with the system-prompt argument left empty; `BASE_SYSTEM_PROMPTS` has no `custom` key, so resolution fell back to `refine` silently and the model polished the instruction it was handed — the transcript never reached it. Separately, only `{{text}}` was substituted and **none of the five built-ins use it** (`{{targetLanguage}}`, `{{userPrompt}}`, or no placeholder), so the substitution was a no-op from the day it was written. Introduced `fea923d` (2026-04-05); present `v0.1.0-alpha`..`v1.4.0`; **not a regression**. Three entry points were affected: commands-page activation (`CommandsPage.tsx:102-117`), command-popup selection (`bootstrap.ts:377-381`), voice keyword match (`VoiceModeService.ts:681-686`). Fixed in `9c2b4d4` (2026-09-21) — `llm-prompts.ts` is now the SSOT for placeholder substitution and argument placement (`renderInstructionPrompt:78`, `buildInstructionInvocation:101`, `resolveSystemPrompt:118`). The earlier "Red Team RT-03 verified" claim did not catch this and its cited evidence file (`red_team_log.md`) is not in the repo. `[~]` because unit tests pass (`llm-prompts.test.ts` 21, `llm-handlers.test.ts` 7, `VoiceModeService.test.ts` 22, `ChainService.test.ts` 5) but there is **no running-app verification** and the related `tests/red/{instruction,chain,voice,config}.usecase.test.ts` could not execute (`11` GAP-INFRA-06). See `11` GAP-LLM-01/02 **Sync (2026-09-27):** desktop user commands (not the desktop presets) mirror Supabase `custom_instructions` both ways, deletions included; server presets (`builtin_key`) are not copied to desktop. |
|
||||
| AI-05 | Custom instructions (user commands) | [~] | [x] | [x] | [x] | Web `commands`; mobile `CommandsScreen` — both go through Edge Functions and are unaffected. **Desktop: the custom-instruction path never worked in any shipped release.** The instruction was passed in the `text` argument of `processText(text, action, targetLanguage, customPrompt)` with the system-prompt argument left empty; `BASE_SYSTEM_PROMPTS` has no `custom` key, so resolution fell back to `refine` silently and the model polished the instruction it was handed — the transcript never reached it. Separately, only `{{text}}` was substituted and **none of the five built-ins use it** (`{{targetLanguage}}`, `{{userPrompt}}`, or no placeholder), so the substitution was a no-op from the day it was written. Introduced `fea923d` (2026-04-05); present `v0.1.0-alpha`..`v1.4.0`; **not a regression**. Three entry points were affected: commands-page activation (`CommandsPage.tsx:102-117`), command-popup selection (`bootstrap.ts:377-381`), voice keyword match (`VoiceModeService.ts:681-686`). Fixed in `9c2b4d4` (2026-09-21) — `llm-prompts.ts` is now the SSOT for placeholder substitution and argument placement (`renderInstructionPrompt:78`, `buildInstructionInvocation:101`, `resolveSystemPrompt:118`). The earlier "Red Team RT-03 verified" claim did not catch this and its cited evidence file (`red_team_log.md`) is not in the repo. `[~]` because unit tests pass (`llm-prompts.test.ts` 21, `llm-handlers.test.ts` 7, `VoiceModeService.test.ts` 22, `ChainService.test.ts` 5) but there is **no running-app verification** and the related `tests/red/{instruction,chain,voice,config}.usecase.test.ts` could not execute (`11` GAP-INFRA-06). See `11` GAP-LLM-01/02 **Sync (2026-09-27):** desktop user commands (not the desktop presets) mirror Supabase `custom_instructions` both ways, deletions included; server presets (`builtin_key`) are not copied to desktop. **Presets (2026-09-27):** prompt edits of the four desktop presets that have a phone counterpart (translate/summarize/formal/explain-code) update the server preset row through `sync_set_builtin_instruction_prompt_v1` (NULL restores the default; `{{targetLanguage}}` is sent as English because both sides translate to English); the phone executes that prompt. `자유 프롬프트` has no phone counterpart and stays local |
|
||||
| AI-06 | Voice keyword commands | [~] | [-] | [ ] | [-] | Desktop `VoiceCommandService` + command popup. Keyword matching itself works (`VoiceModeService.ts:681-686`), but execution went through the broken custom-instruction path (AI-05), and a **second, separate defect** made the shortcut skip LLM processing entirely whenever `defaultLLMAction === 'none'` — which is exactly the value the commands UI and the command popup write when no command is active (`CommandsPage.tsx:116`, `bootstrap.ts:385`), so an explicitly named instruction was nullified by the default setting. Both fixed in `9c2b4d4` (`VoiceModeService.ts:779`/`:785`); **not verified in a running app** (`11` GAP-LLM-02) |
|
||||
| AI-07 | LLM Chains (multi-step pipelines) | [~] | [ ] | [ ] | [-] | Desktop `ChainService`. The argument placement was already correct here, but chain steps **never substituted placeholders**, so `{{targetLanguage}}` / `{{userPrompt}}` reached the model verbatim as the system prompt. Now shares the same resolution function as the other two paths (`ChainService.ts:196`). Fixed in `9c2b4d4` (2026-09-21); 5 unit tests GREEN, but `tests/red/chain.usecase.test.ts` could not execute (`11` GAP-INFRA-06) and there is **no running-app verification** (`11` GAP-LLM-02) |
|
||||
| AI-08 | Screen/context capture for prompts | [x] | [-] | [ ] | [-] | Desktop `ScreenContextService` |
|
||||
|
|
@ -75,7 +75,7 @@ Status quick-reference: `[x]` done+verified · `[~]` partial/unverified · `[ ]`
|
|||
|
||||
| ID | Feature | D | W | M | B | Anchors / notes |
|
||||
|---|---|---|---|---|---|---|
|
||||
| MTG-01 | Meeting recording + live transcript | [x] | [~] | [x] | [x] | Desktop `MeetingModeService`; web realtime view; mobile |
|
||||
| MTG-01 | Meeting recording + live transcript | [x] | [~] | [x] | [x] | Desktop `MeetingModeService`; web realtime view; mobile **Sync (2026-09-27):** the phone renders `transcripts` segments in preference to `edited_transcript`, so desktop edits, auto-polish and diarization never reached it; the desktop now rewrites the meeting's segments from its `[MM:SS] [speaker] text` lines on every transcript change (upsert by `meeting_id,segment_index`, trailing segments trimmed; parser SSOT `@d3ro/core/meeting-transcript`, `tests/main/sync/SyncTranscriptsPresets.test.ts` (4) + local-stack integration case "전사 구간·프리셋") |
|
||||
| MTG-02 | Meeting creation (title/attendees/language/template) | [x] | [x] | [x] | [x] | SSOT H-006 GREEN |
|
||||
| MTG-03 | Timestamped memos during meeting | [x] | [x] | [x] | [x] | |
|
||||
| MTG-04 | AI summary generation | [x] | [x] | [~] | [x] | Desktop `MeetingSummaryService`; web `generate-document-button`; mobile via Edge |
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ Legend: `[ ]` open · `[~]` in progress · `[!]` blocked externally · `[x]` res
|
|||
| GAP-SYNC-03 | Portability | Mobile/cloud data export failed for any account with meetings or generated documents: `export_account_portability` used `to_jsonb(row)` while the v1 archive contract (server restore + mobile `exactKeys`) is a frozen key set, and later migrations added columns. | `server/supabase/migrations/20260927000034_cross_device_sync_parity.sql` §7 | `[x]` 2026-09-27: export projects each dataset onto its v1 keys; integration test asserts the memo/meeting key sets. |
|
||||
| GAP-SYNC-04 | Mobile devices | Mobile `normalizeRegisteredDevice` rejected every platform except `android`/`ios`, so the first desktop/web device on an account would have made the whole Devices screen fail to load. | `apps/mobile-rn/src/features/devices/device-service.ts`, `screens/DevicesScreen.tsx` | `[x]` 2026-09-27: accepts the server platform list (`android/ios/web/windows/macos`), card shows the platform; `__tests__/devices.test.ts` 3/3, full mobile Jest 372/372. |
|
||||
| GAP-SYNC-05 | Sync | **Not yet in production.** Migration `20260927000034` is applied only to the local stack; the rewritten desktop sync ships with the next desktop release. Until the migration is deployed, desktop tombstone pulls and template RPCs fail against production (they are isolated per step, other entities keep syncing). | migration file, `apps/desktop/src/main/services/sync/` | `[~]` 2026-09-27: production `supabase db push` applied `0029`–`0035` (backup dump taken first; see GAP-OPS-01), all 23 Edge Functions redeployed, v1.7.0 published (runtime/portable first, then the updater feed) and installed on the owner PC. **Remaining:** the desktop on that PC is in local mode — one Google sign-in in Settings > Cloud, then edit/delete on the phone and confirm the desktop updates within seconds and appears under Devices. |
|
||||
| GAP-SYNC-06 | Sync | Still desktop-local (not mirrored): knowledge base (desktop local RAG embeddings vs cloud pgvector — different embedding spaces), audio (desktop WAV files vs mobile Storage uploads), `user_settings` (locale, auto-polish, active command), desktop preset-command prompt edits, meeting `transcripts` segments (desktop uses `raw_transcript`, which mobile also fills). | `docs/map/10-feature-catalog.md` MEM-09/MEM-05/SHELL rows | `[x]` 2026-09-27: knowledge docs sync as source text and are re-embedded per surface; recordings upload to the mobile storage contract (≤50 MiB per file, opt-out toggle) and remote recordings play through signed URLs; `user_settings` maps the shared fields. Deliberately not synced (see §7.x): meeting `transcripts` segments (mobile renders `raw_transcript`/`edited_transcript`, which the desktop fills) and desktop preset-command prompt edits (server presets are immutable by RLS). Evidence: `tests/main/sync/SyncExtensions.test.ts` (9) + integration case "지식 문서·설정·녹음" against the local Supabase stack. |
|
||||
| GAP-SYNC-06 | Sync | Still desktop-local (not mirrored): knowledge base (desktop local RAG embeddings vs cloud pgvector — different embedding spaces), audio (desktop WAV files vs mobile Storage uploads), `user_settings` (locale, auto-polish, active command), desktop preset-command prompt edits, meeting `transcripts` segments (desktop uses `raw_transcript`, which mobile also fills). | `docs/map/10-feature-catalog.md` MEM-09/MEM-05/SHELL rows | `[x]` 2026-09-27: knowledge docs sync as source text and are re-embedded per surface; recordings upload to the mobile storage contract (≤50 MiB per file, opt-out toggle) and remote recordings play through signed URLs; `user_settings` maps the shared fields. 2026-09-27 (later): the two items first left out are done too — meeting `transcripts` segments are rebuilt from the desktop transcript (the phone renders segments before `edited_transcript`, so desktop edits were invisible there) and preset prompt edits reach the phone through `sync_set_builtin_instruction_prompt_v1` (migration `0036`, production applied; `tests/main/sync/SyncTranscriptsPresets.test.ts` (4) + local-stack integration case "전사 구간·프리셋"). Evidence: `tests/main/sync/SyncExtensions.test.ts` (9) + integration case "지식 문서·설정·녹음" against the local Supabase stack. |
|
||||
| GAP-SYNC-07 | Sync | `sync_tombstones` grows without a schedule. `prune_sync_tombstones_v1(interval)` (service_role) exists; nothing calls it. | migration §1 | `[x]` 2026-09-27: migration `0035` enables `pg_cron` and schedules `prune-sync-tombstones` daily at 03:17 UTC (production `cron.job` active=true). A client whose cursor is older than 180 days must fall back to a full resync. |
|
||||
|
||||
| GAP-OPS-01 | Ops | **Production Supabase was five migrations behind** (`0029` content reporting, `0030` profile column ACL — users could raise their own `role`/`tier` through PostgREST, `0031` audit-log actor unlink, `0032` meeting-document reporting, `0033` team activity feed) and `content-report` was never deployed, while the map listed those features GREEN from local runs. Most functions still ran the 2026-08-21 build. | `server/supabase/migrations/`, Management API `schema_migrations`, `functions` list | `[x]` 2026-09-27: schema+data dump taken, `db push` applied `0029`–`0035`, all functions redeployed (unauthenticated calls return 401, i.e. live and gated); `authenticated` can no longer UPDATE `profiles.tier`; real-account export returns exactly the v1 keys. Next: add a CI check that compares `supabase migration list --linked` with the repo so production cannot silently lag again. |
|
||||
|
|
@ -218,8 +218,8 @@ Actionable checklist for the work started this session. Fields to fill are blank
|
|||
- **원격 삭제가 로컬 미전송 수정보다 우선한다.** 다른 기기에서 지운 행은 데스크톱에서 편집 중이었어도 지워진다.
|
||||
- **업그레이드 후 첫 동기화는 예전 삭제를 되살릴 수 있다.** 이 버전 이전에는 삭제 기록이 없었으므로, 데스크톱에만 남아 있던 행(모바일에서 지웠지만 데스크톱이 몰랐던 행)은 첫 대조에서 서버로 다시 올라간다. 데이터 유실보다 되살림을 택했다.
|
||||
- **데스크톱 사용 횟수(`dictionary.usage_count`)는 자주 바뀌어 매번 올리지 않는다.** 단어를 추가·수정할 때 함께 올라간다.
|
||||
- **회의 `transcripts` 세그먼트는 동기화하지 않는다.** 모바일은 회의 본문을 `raw_transcript`/`edited_transcript`로 그리고, 데스크톱은 이 두 컬럼을 채운다.
|
||||
- **데스크톱 프리셋 명령의 프롬프트 수정은 기기 로컬이다.** 서버 프리셋(`builtin_key`)은 RLS상 클라이언트가 고칠 수 없고 두 프리셋 목록도 다르다. 사용자가 만든 명령만 동기화한다.
|
||||
- **회의 전사 구간은 데스크톱 전사에서 다시 만든다.** 폰은 구간이 있으면 구간을 그리므로, 데스크톱 전사가 바뀔 때마다 구간을 통째로 맞춘다(시각 표기가 없는 줄은 앞 줄의 시각을 잇는다). 폰은 전사를 고치지 않으므로 서버→데스크톱 방향은 `raw_transcript`로 충분하다.
|
||||
- **프리셋 명령은 짝이 있는 4개만 맞춘다.** 폰은 프리셋 행을 직접 고칠 수 없고(RLS) 데스크톱에서 고친 프롬프트만 전용 RPC로 올린다. 번역의 `{{targetLanguage}}`는 English로 바꿔 올린다(양쪽 모두 영어 번역). `자유 프롬프트`는 폰에 대응이 없어 로컬이다.
|
||||
- **설정은 뜻이 같은 것만 맞춘다.** 언어는 모바일이 지원하는 ko/en, 테마는 시스템/라이트/다크만(데스크톱 전용 테마는 로컬), 자동 다듬기는 켬/끔, 활성 명령은 양쪽에 있는 사용자 명령일 때만. 서버에 설정 행이 이미 있으면 첫 동기화에서 서버 값이 이긴다.
|
||||
- **지식 문서는 원문만 옮긴다.** 기기마다 임베딩 모델·차원이 달라(데스크톱 768, 서버 1536) 벡터는 각자 만든다. 다른 기기에서 온 문서에는 원본 파일이 없다.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue