diff --git a/personal/projects/psychologist-app/dev-setup.md b/personal/projects/psychologist-app/dev-setup.md index 2e53a30b..46e2da57 100644 --- a/personal/projects/psychologist-app/dev-setup.md +++ b/personal/projects/psychologist-app/dev-setup.md @@ -151,14 +151,6 @@ Documents/UserProfile/ --- -## Тест-результаты (2026-05-16) - -- ClaudeServiceTests: 4/4 -- CrisisDetectorTests: 17/17 (включая RU+EN, нормализация) -- ProfileManagerTests: 6/6 (включая onboarding init) -- SessionSchedulerTests: 3/3 -- **ИТОГО: 30/30, 0 failures** - ## App Lock (Face ID / Touch ID / Passcode) **Файлы:** @@ -172,3 +164,73 @@ Documents/UserProfile/ - При уходе в фон (`scenePhase == .background`) — блокируется - При возврате — LockView показывается поверх всего, сразу запрашивает биометрию - Симулятор: биометрия эмулируется через Features → Face ID → Enrolled + +--- + +## Голосовой ввод + +**`Services/VoiceInputService.swift`** — `@MainActor ObservableObject`: +- `AVAudioEngine` + `SFSpeechRecognizer` (locale: ru-RU с fallback на системный) +- Push-to-talk: `startRecording()` / `stopRecording()` +- `onTranscript: ((String) -> Void)?` — коллбэк с финальным текстом +- `audioLevel: Float` — уровень звука в реальном времени (→ орб анимация) +- `requestPermissions()` — запрашивает mic + speech recognition + +--- + +## Адаптивное расписание (ScheduleNegotiationService) + +**`Services/ScheduleNegotiationService.swift`**: +- `nextSessionDate` — следующая запланированная сессия (UserDefaults) +- `buildSuggestion()` → предлагает время на основе паттерна прошлых встреч +- `acceptSchedule(date)` → сохраняет и ставит разовое `UNCalendarNotificationTrigger` +- `postpone(by: PostponeOption)` → переносит на +1ч / +3ч / завтра / skip +- Уведомление содержит action-кнопки прямо из шторы (без открытия приложения) + +**Флоу в конце сессии:** +1. Агент завершает сессию → `SessionManager.endSession()` → `clearSchedule()` +2. `SessionViewModel.endSession()` → показывает `CardData` с предложением времени +3. Пользователь: "Да, подходит" / "Другое время" / "Без расписания" +4. При выборе времени → `ScheduleNegotiationService.acceptSchedule(date)` → уведомление + +**HomeView:** +- Если есть расписание → показывает badge "Следующая: пятница 20:00" + кнопка "Перенести" +- Если нет расписания → "Назначить следующую встречу" (кнопка открывает `PostponeSheetView`) + +--- + +## Тулинг агента (AnalystBot) + +Аналитик использует tool-use loop (`ClaudeService.completeWithTools`). + +**4 инструмента:** +| Tool | Описание | +|------|----------| +| `get_index` | Индекс сессий — таблица дат/тем/тегов, < 1K токенов | +| `search_sessions` | Полнотекстовый поиск по архиву | +| `read_session` | Прочитать полную сессию по дате | +| `read_profile` | Прочитать profile.md | + +Агент сам решает когда обращаться к хранилищу — когда видит паттерн или нужен контекст прошлых встреч. + +--- + +## Session Index + +**`ProfileManager.updateSessionIndex()`** обновляет `sessions/index.md`: +``` +| Дата | Темы | Теги | Содержание | +|------------|------------------------|-------------------|-------------------| +| 2026-05-16 | работа, усталость | #сессия #инсайт | Говорит что устал | +``` +Заполняется автоматически при `endSession()`. Агент читает через `get_index` — без засорения контекста полными файлами. + +--- + +## Тест-результаты (2026-05-16) + +- ClaudeServiceTests: 4/4 +- CrisisDetectorTests: 17/17 (включая RU+EN, нормализация) +- ProfileManagerTests: 6/6 (включая onboarding init) +- SessionSchedulerTests: 3/3 +- **ИТОГО: 30/30, 0 failures**