131 lines
7.5 KiB
Markdown
131 lines
7.5 KiB
Markdown
---
|
||
title: "naisei / reflect-skip — проект"
|
||
aliases:
|
||
- "naisei"
|
||
- "nolvu"
|
||
- "reflect-skip"
|
||
- "Naisei проект"
|
||
- "Reflect Skip"
|
||
tags:
|
||
- personal
|
||
- project
|
||
- app
|
||
- psychology
|
||
- skip-tools
|
||
created: '2026-05-17'
|
||
updated: '2026-06-04'
|
||
related:
|
||
- "[[personal/projects/psychologist-app/overview]]"
|
||
- "[[personal/projects/psychologist-app/dev-setup]]"
|
||
---
|
||
# naisei / reflect-skip — проект
|
||
|
||
## Статус
|
||
- Имя продукта: **naisei**
|
||
- Активная кодовая база: `~/Developer/reflect-skip/`
|
||
- Android: публикуется на `https://reflect.qentra.top` (F-Droid)
|
||
- iOS: собирается через `Project.xcworkspace` → `Darwin/ReflectApp.xcodeproj`
|
||
- Текущая версия: **v0.0.108** (ветка `feature/document-import`; `main` на v0.0.60, не слит)
|
||
|
||
## Структура проекта
|
||
|
||
### Два источника Swift-кода в reflect-skip
|
||
- `Sources/ReflectApp/` — **актуальный** Skip-источник. Компилируется Darwin-проектом. Используется для iOS и Android.
|
||
- `ReflectApp/` — **legacy** iOS-only код, существовавший до перехода на Skip. Компилируется `ReflectApp.xcodeproj` в корне. Не используется в продакшне.
|
||
|
||
### Как открывать iOS-проект
|
||
Всегда через `Project.xcworkspace`, не через `ReflectApp.xcodeproj` напрямую.
|
||
|
||
### Как собирается Android
|
||
`build-and-publish.sh` → Skip компилирует `Sources/ReflectApp/` → APK → F-Droid repo.
|
||
|
||
## Прокси
|
||
|
||
- LaunchAgent: `ai.reflect-proxy` → `localhost:8765`
|
||
- Cloudflare tunnel: `ai.qentra.top` → `localhost:8765`
|
||
- Бэкенд: `claude-code` (переключено с `claude-proxy`)
|
||
- Лог сессий: `~/Developer/reflect-skip/sessions.log`
|
||
|
||
## Агенты
|
||
|
||
- **NarratorBot** — нейтрализует ввод пользователя (убирает эмоциональную окраску, 3-е лицо); детектирует `[SCHEDULE:]` тег для schedule intent
|
||
- **AnalystBot** — задаёт 2-3 глубоких вопроса по профилю + контексту сессии; возвращает `AnalystJSON` с полями `sessionComplete`, `moodDelta`, `suggestedInsight`
|
||
- **SafetyClassifier** — Layer 2, параллельный crisis detection; роутинг off-topic → redirect card
|
||
- **SessionManager** — управляет историей сессий, пейсингом (turn count, fatigue, hard limit 8 оборотов), closing-флоу
|
||
|
||
### ProcessingResult (текущие кейсы)
|
||
```swift
|
||
enum ProcessingResult {
|
||
case questions([String], observation: String?)
|
||
case readyToClose([String]?, observation: String?)
|
||
case offTopic(String)
|
||
case scheduleIntent(String)
|
||
case crisis(CrisisDetectionResult)
|
||
case moodDeclining / moodImproving // через OrbState
|
||
case error(String)
|
||
}
|
||
```
|
||
|
||
## Исправления (v0.0.92, 28 мая 2026) — Android vault validation
|
||
|
||
### Веха: Android onboarding работает
|
||
✅ Алерт валидации vault отображается корректно
|
||
✅ Кнопки появились после выбора папки
|
||
✅ Дошли до экрана "Доброе утро"
|
||
|
||
### Fix — @AppStorage вместо @State для UI-обновлений на Android
|
||
**Проблема:** `@State var folderPickerError: String?` и `@State var vaultFound: Int?` не триггерили Compose recompose в SkipFuse bridge.
|
||
**Симптом:** алерт не появлялся, кнопки не обновлялись, хотя логика SAF выполнялась.
|
||
**Исправление:** заменили на `@AppStorage("_vaultErr")` и `@AppStorage("_vaultFound")` — идут через SharedPreferences → всегда триггерят Compose recompose.
|
||
**Файл:** `Sources/naisei/Views/OnboardingView.swift`
|
||
|
||
## Реализовано (v0.0.92 → v0.0.108)
|
||
|
||
- **Session pacing + fatigue + recovery** — turn counter, fatigue detection, session recovery флоу
|
||
- **Structured session close** — `generateClose()`, `applyOps()`, ProfileManager DI; X-кнопка вызывает `endSession()`
|
||
- **Completion routing + insights** — `readyToClose` в ProcessingResult, `insights.md`, agentic insight
|
||
- **One-card UX** — `isActive`, `appendCard`, growing TextEditor, history view
|
||
- **Schedule intent detection** — Narrator `[SCHEDULE:]` тег → `ProcessingResult.scheduleIntent`
|
||
- **Mood delta → Orb visual feedback** — `MoodDelta` enum, `AnalystJSON`, `OrbState.moodDeclining/moodImproving`
|
||
- **Warm track** — реальные даты сессий из file attributes, `lastSessionText`, `daysSinceLastSession` → Analyst context
|
||
- **LLM safety classifier + off-topic routing** — `SafetyClassifier` Layer 2, параллельный crisis detection, redirect card
|
||
- **Android voice bridge** — `SpeechRecognizer` bridge via `AndroidBridge`, Kotlin companion в `AndroidAppMain`
|
||
|
||
## Исправления (v0.0.25, май 2026)
|
||
|
||
### Fix 1 — дублирование нарратора
|
||
`buildSessionContext()` вызывался после `addMessage()` → нарратор видел своё же сообщение в контексте.
|
||
Исправлено: поменяли порядок в `Sources/SessionManager.swift`.
|
||
|
||
### Fix 2 — AnalystBot не видел текущий диалог
|
||
`ProfileManager.loadRecentSessions(count: 3)` загружал прошлые ФАЙЛЫ, не текущую сессию.
|
||
Исправлено: добавили `buildCurrentSessionDialogue()` (последние 8 пар user/analyst), передаём в `analyze()`.
|
||
|
||
### Fix 3 — нарратору уходили вопросы аналитика
|
||
`sendBatchSummary()` отправлял весь диалог включая вопросы аналитика.
|
||
Исправлено: фильтруем только user answers.
|
||
|
||
## Obsidian-совместимость
|
||
|
||
Приложение читает сессии из папки `observations/` (формат vault), а не `sessions/`.
|
||
|
||
- **Папка сессий:** `observations/` в корне импортируемой директории
|
||
- **Именование файлов:** ISO week — `2026-W20.md`, `2026-W21.md`
|
||
- **Запись сессий:** аппендит `## Сессия DD.MM.YYYY HH:mm` в текущий недельный файл
|
||
- **Счётчик сессий:** считается напрямую как количество `.md` файлов в `observations/` (не хранится в profile.md)
|
||
- **Профиль:** `profile.md` — строка `**Имя:** Алексей` нужна для `parseName()`
|
||
- **Инсайты:** `insights.md` — произвольный формат, передаётся напрямую в Claude
|
||
|
||
Vault путь для импорта: `personal/psychology/` (на Taiga).
|
||
|
||
## Pitfalls
|
||
|
||
- Если на iOS-симуляторе не появляется `DevSetupView` (ввод токена) — значит `dev_setup_done = true` в UserDefaults. Удалить приложение с симулятора и запустить заново.
|
||
- `ReflectApp/` в корне — legacy, не трогать. Реальные правки только в `Sources/ReflectApp/`.
|
||
|
||
|
||
## Связанные заметки
|
||
|
||
- [[personal/projects/psychologist-app/overview]]
|
||
- [[personal/projects/psychologist-app/dev-setup]]
|