Files
obsidian-vault/personal/projects/nolvu-reflect-skip.md
T

141 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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, сервер локально на Mac через CF tunnel → localhost:8766, не VPS)
- 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)
}
```
## Онбординг — экран с подписью-обещанием
Добавить экран онбординга с подписью от пользователя: согласие заниматься собой + обещание себе работать.
- **Что делает:** пользователь ставит подпись (или вводит имя/псевдоним + чекбокс/кнопку) под коротким текстом вроде «Я согласен(на) заниматься собой. Это моё обещание себе работать.»
- **Зачем:** commitment device — повышает вовлечённость и retention через эффект обязательства (Cialdini — Consistency).
- **Где в онбординг-флоу:** после Agreement overlay, перед анкетами (A/B тестировать позицию). Либо как финальный экран перед первой сессией.
- **UX:** минималистично — текстовое поле + кнопка «Обещаю»/«Начинаю». Подпись хранится локально.
## Исправления (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/`.
- Skip Fuse: новый SwiftUI View требует ручного Kotlin-стаба в 2 местах.
## Связанные заметки
- [[personal/projects/psychologist-app/overview]]
- [[personal/projects/psychologist-app/dev-setup]]