chore(core): stage 1 contracts — dialog, API schemas + fixtures, integrations, monitoring, modules
CI / ruff · pytest (pull_request) Successful in 22s

Freeze interfaces between lanes A (bot), B (backend), C (dashboard) so three developers work in
parallel without touching each other's files: dialog Event/Reply + engine registry with echo stub,
integration protocols with in-memory defaults, MonitoringService with noop stub, /api/v1 schemas
with fixture-backed routes, frozen main.py wiring, config/modules.yaml + due_scales(), DAR-5,
tzdata, Andrey trajectory, lanes in WORKFLOW, ADR-010.

Refs #34

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 15:22:25 +03:00
co-authored by Claude Opus 5.5
parent a86008f248
commit b4731fb1d6
30 changed files with 1126 additions and 89 deletions
+6
View File
@@ -3,6 +3,12 @@
> Живой журнал: **что** решили и **почему**. Новые записи — сверху. Одна запись = одно решение.
> При конфликте с [`docs/info/SPEC.md`](info/SPEC.md) действует ADR.
## 2026-10-03 — ADR-010: Фокус — ветераны СВО; разработчики делят слои, а не направления
**Контекст:** три направления (ветераны, семьи, общая популяция с депрессией) и три разработчика. Если каждый ведёт своё направление, все трое правят одни и те же файлы: движок, API, дашборд.
**Решение:** на хакатоне делаем только ветеранов (эпик #35); семьи (#40) и общая популяция (#43) — опциональные направления на будущее, без срока. Разработчики делят **слои**: A — бот (диалог, каналы, LLM), B — backend (API, сервисы, шедулер, адаптеры, БД, ядро), C — дашборд. Между слоями — замороженные контракты (#34) с заглушками, поэтому все трое стартуют одновременно. Направления подключаются как `app/dialog/flows/<module>.py` + `config/modules.yaml`.
**Причина:** ноль коллизий по файлам при параллельной работе; общее ядро переиспользуется направлениями, как и задумано в ADR-001. Главный герой демо — Андрей (решение #9).
**Последствия:** изменения контрактов — только мелкими PR `[contract]` с пингом всех. Владение файлами — в `docs/dev/WORKFLOW.md`.
## 2026-10-03 — ADR-009: Процесс — регламент TenVex в облегчённом режиме хакатона
**Контекст:** 3 дня, сдача в пн 05.10 в 13:00 МСК. Два разработчика и Claude Code-агенты работают параллельно. Опыт TenVex/vobot: без issue и изоляции агенты конфликтуют.
**Решение:** берём ядро регламента vobot: задача = issue, одна зона на issue, worktree + ветка от `origin/dev`, PR в `dev`, Conventional Commits, лог в теле issue, ADR сюда. Облегчения: PR в `dev` можно мержить самому после зелёного `make lint test`, без обязательного ревью; contract-first/orval нет; `main` = сдаваемая версия, промоушн `dev → main` делает человек.
+31 -16
View File
@@ -2,37 +2,52 @@
> Основан на регламенте TenVex/vobot, облегчён под хакатон (ADR-009). Дедлайн: **пн 05.10.2026, 13:00 МСК**.
## Зоны и файлы
## Зоны (lanes) и файлы
| Зона (`label`) | Пути | Отвечает за | Владелец |
Три разработчика делят **слои**, а не направления (ADR-010): каждый ведёт свой слой для всех сценариев. У каждой зоны свои файлы — в чужие не заходим, поэтому коллизий нет.
| Lane | Кто | Владеет файлами | Метки зон |
|---|---|---|---|
| `core` | `app/core/` | privacy, шкалы и скоринг, safety, движок правил, демо-часы | dev |
| `bot` | `app/dialog/`, `app/channels/`, `app/llm/` | FSM диалога, Telegram-адаптер, LLM-шлюз (GigaChat/mock) | Егор |
| `backend` | `app/db/`, `app/api/`, `app/scheduler/`, `app/integrations/` | БД, API, шедулер, моки МИС/ЕСИА/ГРЛС, уведомления | dev |
| `dashboard` | `dashboard/` | дашборд врача, демо-консоль (Streamlit) | dev |
| `devops` | `docker-compose.yml`, `Dockerfile`, `Makefile`, `.gitea/`, `pyproject.toml` | окружение, CI, запуск на чистой машине | dev |
| `clinical` | `config/` (содержимое), `data/eval/` | шкалы, пороги, тексты, лексикон, кризисный скрипт | психиатр |
| `pitch` | `docs/pitch/` | презентация, клинико-экономика, CJM, правовые слайды | медик |
| `docs` | `docs/`, `README.md`, `CLAUDE.md` | регламенты, ADR, README | все |
| **A — бот** | Егор (`Eleboba`) | `app/dialog/**`, `app/channels/**`, `app/llm/**`, `config/texts/**`, `tests/test_dialog_*`, `tests/test_telegram*`, `tests/test_llm*` | `bot`, `lane/A` |
| **B — backend** | разработчик 2 | `app/api/**` (кроме `schemas.py`), `app/services/**` (кроме `contracts.py`), `app/scheduler/**`, `app/integrations/**` (кроме `base.py`), `app/db/**`, `app/core/**` (правки — мелким PR), `scripts/**`, `data/**`, `tests/test_api*`, `tests/test_services*`, `tests/test_scheduler*`, `tests/test_seed*`, `tests/test_integrations*`, `tests/test_db*` | `backend`, `core`, `lane/B` |
| **C — дашборд** | разработчик 3 | `dashboard/**`, `tests/test_dashboard*` | `dashboard`, `lane/C` |
| clinical | психиатр | значения в `config/scales/*`, `config/rules.yaml`, `config/lexicon/*`, вычитка `config/texts/*` | `clinical` |
| pitch | медик | `docs/pitch/**` | `pitch` |
**Замок зоны:** пока на зону есть открытый issue с активной веткой, файлы зоны принадлежат ему до мержа. Перед правкой: `git fetch origin && git branch -r`.
**Схемы `config/*.yaml`** задаёт `core` (pydantic-модели), а **значения** в них — `clinical`.
**Замороженные контракты** (меняются только отдельным мелким PR `[contract]` с пингом всех трёх):
`app/core/types.py`, `app/dialog/contracts.py`, `app/api/schemas.py`, `app/integrations/base.py`, `app/services/contracts.py`, `app/main.py`, `app/config.py`, `config/modules.yaml`, `config/rules.yaml` (схема), `pyproject.toml`/`uv.lock`, `docker-compose.yml`, `Makefile`, `.gitea/`.
**Как зоны стыкуются без ожидания друг друга:**
- A → B: движок вызывает `get_monitoring().after_data_change()` и `integrations.*`. До готовности B это заглушки (`NoopMonitoring`, `Memory*`).
- B → A: API и шедулер вызывают `get_engine().handle()`. До готовности A это `EchoEngine`.
- C → B: дашборд ходит только в `/api/v1`. До #12 ручки отдают фикстуры (`app/api/fixtures.py`).
- Реальная реализация регистрируется сама: `register_engine()` / `register_monitoring()` при импорте модуля, `integrations.<field> = ...` в `app/integrations/setup()`.
### Направления развития
| Направление | Статус | Milestone |
|---|---|---|
| Ветераны СВО | **фокус хакатона** | `Этап 1 · Ветераны СВО` (+ `M3 Сдача`) |
| Семьи ветеранов | опционально, после хакатона | `Будущее · Семьи ветеранов` (эпик #40) |
| Депрессия в общей популяции | опционально, после хакатона | `Будущее · Депрессия (общая популяция)` (эпик #43) |
Новое направление = новый файл `app/dialog/flows/<module>.py` + запись в `config/modules.yaml`; движок, API и дашборд расширяются в своих зонах.
---
## Откуда задачи
- Только из Gitea `$GITEA_REPO`. Нет issue — нет кода. Нет подходящего issue — заведи его на себя.
- Атрибуты: **метка зоны**, **тип** (`feature`/`fix`/`refactor`/`docs`/`chore`), **размер** (`size/0.5` ≈ 2 ч, `size/1` ≈ 4 ч, `size/2` ≈ день), **milestone** (M1/M2/M3), приоритет `P0`/`P1`/`P2`.
- Атрибуты: **метка зоны** + `lane/A|B|C`, **тип** (`feature`/`fix`/`refactor`/`docs`/`chore`), **размер** (`size/0.5` ≈ 2 ч, `size/1` ≈ 4 ч, `size/2` ≈ день), **milestone** (M1/M2/M3), приоритет `P0`/`P1`/`P2`.
- **Одна задача = одна зона.** Нужно несколько зон → несколько issue, связанных зависимостью через API ([`GITEA_API.md`](GITEA_API.md)).
- Тело issue — по шаблону [`ISSUE_TEMPLATE.md`](ISSUE_TEMPLATE.md).
### Milestones
| Milestone | Срок | Смысл |
|---|---|---|
| M1 Каркас | сб 03.10, 23:59 | ядро, БД, LLM-шлюз, конфиги; тесты зелёные |
| M2 Демо-сценарий | вс 04.10, 23:59 | весь сценарий проходит end-to-end в демо-консоли |
| M1 Каркас | сб 03.10 ✅ | ядро, БД, LLM-шлюз, конфиги |
| Этап 1 · Ветераны СВО | вс 04.10, 23:59 | демо Андрея end-to-end (эпик #35) |
| M3 Сдача | пн 05.10, 13:00 | README, презентация PDF, репетиция, `dev → main` |
| Будущее · … | без срока | опциональные направления |
---
@@ -78,7 +93,7 @@ scope = зона. `Closes #N` — только в описании PR.
Closes #<N>
## Зона
core | bot | backend | dashboard | devops | clinical | pitch | docs
A | B | C | clinical | pitch | docs | contract
## Как проверить
шаги/команды
+59 -1
View File
@@ -2,7 +2,65 @@
Источник для seed-данных и сценария демо. Все данные вымышленные.
## Основная персона
> **Решение #9 / ADR-010:** главный герой хакатона — **ветеран Андрей** (этап 1). Траектория Алексея перенесена на Андрея по структуре. Сценарии Алексея (депрессия, самоскрининг) остаются ниже — это опциональное направление «Депрессия в общей популяции» (эпик #43).
## Главная персона этапа 1 — Андрей
| Поле | Значение |
|---|---|
| Кто | Андрей С., 34 года, контрактник, вернулся после ранения 8 месяцев назад; райцентр, до психиатра — областной центр |
| Жалобы | плохо спит, кошмары, раздражительность, срывается на домашних, стал чаще выпивать |
| Барьер | «я не псих»; страх учёта, лишения прав на оружие и вождение; доверяет только своим |
| Точка входа | углублённая диспансеризация → консультация психолога → психиатр кабинета медико-психологического консультирования предлагает подключиться (QR/код) |
| Скрининг (день −3) | PC-PTSD-5 = 4 (положительный), PCL-5 = 46, PHQ-9 = 14 (п.9 = 0), AUDIT-C = 3 (ниже порога для мужчин), оружия дома нет (охотничье ружьё у брата) |
| Визит (день 0) | ПТСР + депрессивный эпизод средней степени; **сертралин 50 мг 1 р/сут утром после еды**; модули `veteran` + `depression`; следующий визит через 4 недели |
| Врач | д-р Смирнова Е. А., психиатр (`doc-smirnova`) |
### Траектория Андрея (сценарий этапа 1)
Ожидаемые цвета проверяются тестом правил: 🟢🟢🟢🟡🟡🟢🟢🟢🟢🟡🟡🟡🔴.
| День | Приём | Настр. 0–10 | Сон | Кошмары | Алкоголь | Свободный текст | Шкалы | Уровень | Реакция системы |
|---|---|---|---|---|---|---|---|---|---|
| 0 | — | — | — | — | — | — | — | 🟢 | врач «Подключить пациента» → код; Андрей: согласие, знакомство, напоминания |
| 1 | да | 4 | 4–6 | да | нет | — | — | 🟢 | приветствие, напоминание, вопрос про сон |
| 2 | да | 4 | 4–6 | да | нет | «подташнивает с утра» | — | 🟢 | med_qa: частая побочка, обычно проходит за 1–2 нед.; принимать после еды |
| 3 | да | 4 | 4–6 | да | нет | «опять тошнит» | — | 🟢 | поддержка |
| 4 | да | 3 | 4–6 | нет | нет | «тошнота не проходит» | — | 🟡 | побочка 3 дня подряд → алерт `side_effect_3d` |
| 5 | **нет** | 3 | 4–6 | нет | нет | «от таблеток только хуже, брошу» | — | 🟡 | отсроченный эффект, не отменять без врача; алерт `quit_intent` |
| 6 | да | 4 | 6–8 | нет | нет | «врач позвонил, продолжаю» | — | 🟢 | врач подтвердил алерты (ack) после звонка |
| 7 | да | 4 | 6–8 | нет | нет | — | PCL-5 = 44, PHQ-9 = 13, п.9 = 0 | 🟢 | плановые шкалы |
| 8 | да | 4 | < 4 | да | 1–2 | «снится одно и то же» | — | 🟢 | — |
| 9 | да | 4 | < 4 | да | больше | — | — | 🟢 | — |
| 10 | да | 4 | < 4 | да | больше | «без пива не могу уснуть» | — | 🟡 | сон < 4 ч три ночи (`sleep_lt4_3n`) + алкоголь 3 дня (`alcohol_3d`) → инфо о взаимодействии с сертралином, предложение записаться раньше |
| 11 | да | 3 | 4–6 | нет | 1–2 | — | — | 🟡 | (алерты открыты) |
| 12 | да | 3 | 4–6 | нет | нет | — | — | 🟡 | — |
| 13 | да | 2 | < 4 | да | нет | «всё бессмысленно, устал от всего» | внеплановый PHQ-9 = 18, п.9 = 2 | 🔴 | настроение ≤ 3 три дня → внеплановый PHQ-9 → п.9 ≥ 1 → кризисный скрипт, C-SSRS, план безопасности, алерт `phq9_item9` дежурному ≤ 3 с |
### Дашборд: ещё 7 пациентов кабинета
| Пациент | Возраст | Модули | Препарат | День | Статус | Последнее событие |
|---|---|---|---|---|---|---|
| Дмитрий В. | 41 | veteran | сертралин 100 мг | 45 | 🟡 | кошмары, сон < 4 ч 3 ночи |
| Сергей П. | 37 | veteran | пароксетин 20 мг | 30 | 🟡 | пропуски 3 из 7 дней |
| Николай Ф. | 52 | veteran | сертралин 50 мг | 14 | 🟡 | алкоголь 4 из 7 дней |
| Анна Л. | 23 | depression | флуоксетин 20 мг | 8 | 🟡 | возраст < 25: усиленный мониторинг |
| Ирина К. | 34 | depression | эсциталопрам 10 мг | 21 | 🟢 | PHQ-9 16 → 9 |
| Ольга Н. | 29 | depression | сертралин 50 мг | 10 | 🟢 | тошнота прошла |
| Павел Р. | 45 | depression | эсциталопрам 10 мг | 60 | 🟢 | ремиссия, PHQ-9 = 4 |
### Порядок демо этапа 1 (≈ 3 мин)
1. Врач подключает Андрея на дашборде → QR — 15 с
2. Андрей: согласие, короткий скрининг, напоминание о сертралине → «принял» — 40 с
3. День 5: «брошу» → объяснение + 🟡 у врача — 35 с
4. День 10: кошмары, сон, алкоголь → инфо о взаимодействии, «записаться раньше» — 30 с
5. День 13: «всё бессмысленно» → внеплановый PHQ-9 → 🔴 кризисный протокол + карточка алерта и план безопасности на дашборде — 40 с
---
# Опциональное направление: депрессия в общей популяции (Алексей)
## Персона Алексея
| Поле | Значение |
|---|---|