feat(backend): шедулер на демо-часах — дозы, missed, чек-ины, шкалы, молчание #59

Merged
Eleboba merged 1 commits from feat/13-scheduler into dev 2026-10-04 03:29:25 +03:00
Owner

Что меняется

Шедулер на демо-часах: run_until(now) по порядку прогоняет все наступившие события — напоминание о дозе (с созданием строки doses), missed через 2 ч без ответа, ежедневный чек-ин, шкалы по расписанию модулей, ежедневный проход мониторинга (молчание > 3 дней, пропуски). «Следующий день» в API (POST /demo/clock/advance) вызывает прогон.

Closes #13

Зона

B

Детали

  • app/scheduler/demo.py — сбор событий по всем пациентам с согласием, сортировка по времени, доставка через get_engine().handle() → integrations.outbox.
  • Идемпотентность: таблица scheduler_events (unique token+key), запись после успешной доставки (at-least-once).
  • Свежесть: событие старше своего окна не отправляется — после прыжка часов пациент не получает пачку вчерашних напоминаний, а неотправленное напоминание не превращается в «пропуск».
  • Действия правил вне диалога: reach_out → system:reach_out {topic}, schedule_scale → system:scale_due.
  • Шкала, ни разу не пройденная, считается от therapy_start (не «просрочена» в день 0).
  • Нет контакта без согласия и при открытом кризисе.
  • config/scheduler.yaml — время и окна. Работает, пока выключен релиз анкеты (intake_runtime.yaml), — режимы взаимоисключающие, код анкеты не тронут.

Для lane A: core-триггер system:reach_out игнорирует params.topic и всегда отвечает reach_out.default; тексты silence/missed_doses есть в config/texts/dialog.yaml.

Как проверить

make lint && make test — tests/test_scheduler_demo.py (напоминание один раз → missed, ответ отменяет missed, 3 пропуска → алерт, устаревшие события, шкалы по расписанию, молчание, согласие/кризис, точка входа jobs.run_until_now).

Чек-лист

  • rebase на origin/dev
  • make lint + make test зелёные (701 passed)
  • лог в issue (### 2026-10-04)
  • нет ПДн в логах, нет реальных данных, нет секретов
  • общие файлы не трогались

🤖 Generated with Claude Code

## Что меняется Шедулер на демо-часах: `run_until(now)` по порядку прогоняет все наступившие события — напоминание о дозе (с созданием строки `doses`), `missed` через 2 ч без ответа, ежедневный чек-ин, шкалы по расписанию модулей, ежедневный проход мониторинга (молчание > 3 дней, пропуски). «Следующий день» в API (`POST /demo/clock/advance`) вызывает прогон. Closes #13 ## Зона B ## Детали - `app/scheduler/demo.py` — сбор событий по всем пациентам с согласием, сортировка по времени, доставка через `get_engine().handle()` → `integrations.outbox`. - **Идемпотентность:** таблица `scheduler_events` (unique `token+key`), запись после успешной доставки (at-least-once). - **Свежесть:** событие старше своего окна не отправляется — после прыжка часов пациент не получает пачку вчерашних напоминаний, а неотправленное напоминание не превращается в «пропуск». - Действия правил вне диалога: `reach_out` → `system:reach_out {topic}`, `schedule_scale` → `system:scale_due`. - Шкала, ни разу не пройденная, считается от `therapy_start` (не «просрочена» в день 0). - Нет контакта без согласия и при открытом кризисе. - `config/scheduler.yaml` — время и окна. Работает, пока выключен релиз анкеты (`intake_runtime.yaml`), — режимы взаимоисключающие, код анкеты не тронут. **Для lane A:** core-триггер `system:reach_out` игнорирует `params.topic` и всегда отвечает `reach_out.default`; тексты `silence`/`missed_doses` есть в `config/texts/dialog.yaml`. ## Как проверить `make lint && make test` — `tests/test_scheduler_demo.py` (напоминание один раз → missed, ответ отменяет missed, 3 пропуска → алерт, устаревшие события, шкалы по расписанию, молчание, согласие/кризис, точка входа `jobs.run_until_now`). ## Чек-лист - [x] rebase на origin/dev - [x] make lint + make test зелёные (701 passed) - [x] лог в issue (### 2026-10-04) - [x] нет ПДн в логах, нет реальных данных, нет секретов - [x] общие файлы не трогались 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Eleboba added 1 commit 2026-10-04 03:26:03 +03:00
- app/scheduler/demo.py: run_until(now) replays due and fresh events in time order: dose rows +
  med_reminder, missed after 2 h without an answer → monitoring, daily_checkin, scale_due by the
  config/modules.yaml schedules, daily monitoring pass (silence, missed doses) → reach_out/scale_due
- idempotent via scheduler_events (unique token+key); stale events are not sent; no contact without
  consent or during an open crisis
- config/scheduler.yaml: times and windows; runs while the questionnaire release is off
- POST /demo/clock/advance replays the scheduler up to the new time

Refs #13

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eleboba merged commit 66986255e2 into dev 2026-10-04 03:29:25 +03:00
Sign in to join this conversation.