Шедулер на демо-часах: run_until(now) по порядку прогоняет все наступившие события — напоминание о дозе (с созданием строки doses), missed через 2 ч без ответа, ежедневный чек-ин, шкалы по расписанию модулей, ежедневный проход мониторинга (молчание > 3 дней, пропуски). «Следующий день» в API (POST /demo/clock/advance) вызывает прогон.
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)
нет ПДн в логах, нет реальных данных, нет секретов
## Что меняется
Шедулер на демо-часах: `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)
- 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>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Что меняется
Шедулер на демо-часах:
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(uniquetoken+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).Чек-лист
🤖 Generated with Claude Code