2026-06-02 reflect-skip: план session pacing & closing

This commit is contained in:
Alexey Martemyanov
2026-06-02 12:34:27 +06:00
parent cb07c1f0f5
commit 1b980f9899
+80
View File
@@ -103,6 +103,86 @@ Vault путь для импорта: `personal/psychology/` (на Taiga).
- Если на iOS-симуляторе не появляется `DevSetupView` (ввод токена) — значит `dev_setup_done = true` в UserDefaults. Удалить приложение с симулятора и запустить заново.
- `ReflectApp/` в корне — legacy, не трогать. Реальные правки только в `Sources/ReflectApp/`.
## Plan: Session Pacing & Closing (не реализовано)
### Проблема
`SessionViewModel.endSession()` существует и реализует полный closing-флоу (инсайт → warm phrase → oneLastThing → vault save), но **нигде не вызывается из UI**.
`dismissSession()` (кнопка X) отменяет сессию без сохранения.
Пейсинга нет: агент не знает сколько оборотов прошло, нет правил завершения в systemPrompt, нет сигнала `session_complete` в `AnalystJSON`.
### Решение: Вариант Б + hard limit
**Шаг 1 — Quick-fix: X-кнопка вызывает `endSession()` (даёт рабочий closing уже сейчас)**
Вместо `dismissSession()` при нажатии X — вызывать `endSession()`.
Пользователь сам решает когда нажать, но closing-флоу отработает корректно.
**Шаг 2 — Turn count в SessionManager**
Добавить `var userTurnCount: Int = 0`, инкрементировать в `processUserInput()`.
Передавать в `AnalystBot.analyze()` как параметр `turnCount`.
**Шаг 3 — `session_complete` в AnalystJSON**
```swift
struct AnalystJSON: Codable {
let questions: [String]
let observation: String?
let crisisSignal: Bool
let suggestedInsight: String?
let sessionComplete: Bool // новое поле
}
```
**Шаг 4 — Пейсинг-правила в systemPrompt AnalystBot**
```
Правила ведения сессии (параметр turn_count передаётся в каждом запросе):
- После 4+ оборотов: если пользователь пришёл к пониманию, тема исчерпана
или повторяется — установи session_complete: true
- Если открылась новая важная тема — продолжай (session_complete: false)
- После 7+ оборотов: session_complete: true независимо от темы
- session_complete: true — предложение закрыть, не принудительное завершение
```
**Шаг 5 — `.readyToClose` в ProcessingResult**
```swift
enum ProcessingResult {
case questions([String], observation: String?)
case readyToClose([String]?, observation: String?)
case crisis(CrisisDetectionResult)
case error(String)
}
```
SessionManager возвращает `.readyToClose` когда `analystResponse.sessionComplete == true` ИЛИ `userTurnCount >= 8`.
**Шаг 6 — UI карточка в SessionViewModel**
При `.readyToClose`: показать вопросы (если есть) → после ответа добавить карточку
"Кажется, мы хорошо прошлись по теме. Завершить сессию?"
Кнопки: "Завершить" → `endSession()` | "Продолжить" → сбросить сигнал на текущий turn.
**Шаг 7 — Интеграционные тесты (TDD: писать до реализации шагов 2-6)**
- `testAnalystReturnsSessionComplete_afterExhaustedTopic()`
- `testSessionManager_readyToClose_onSessionCompleteSignal()`
- `testSessionManager_hardLimit_at8Turns()`
- `testSessionViewModel_showsCompletionCard_onReadyToClose()`
- `testSessionViewModel_continueAfterReadyToClose_resetsSignal()`
### Порядок реализации
1. Тесты (шаг 7) — все RED
2. Quick-fix X-кнопки (шаг 1) — немедленно даёт рабочий closing
3. Шаги 2-5 — данные + логика
4. Шаг 6 — UI карточка
5. Все тесты GREEN
---
## Связанные заметки
- [[personal/projects/psychologist-app/overview]]