- shorts.js подписан на SSE shorts_changed с дебаунсом и реконнектом
- dispatchEvent больше не отдаёт любой неизвестный тип как
entries_changed: entries_changed/lessons_changed/shorts_changed
теперь маршрутизируются по имени (иначе правка короткого
сообщения будила журнал с ложным тостом о новой записи)
- добавлен aiLimiter (30/мин на cache.rateLimitStore('ai')) и
навешен на оба вызова модели: /api/ai/correct и
/api/shorts/ai-correct
- purgeScheduledDeletions() теперь чистит short_messages по purge_at
- short_messages добавлена в BACKUP_TABLES/BACKUP_SEQUENCE_TABLES,
normalizeRestoreData и restore (поле автора проверяется на живость)
- сид short_messages_retention_days перенесён и в db/init.sql
- used_at по умолчанию берётся в зоне приложения (appTodayIso),
а не по UTC
- поле «Использовано» скрыто при создании, открыто при правке
- удалён пустой escInline и мёртвые data-message/canEdit: сырой
текст сообщения попадал в атрибут неэкранированным
Таблица short_messages (резидент, дата занятия, тема, текст, отметка
об использовании), CRUD API /api/shorts с мягким удалением и плановой
очисткой через purge_at, ИИ-коррекция текста по профилю, страница
shorts.html с массовым добавлением разбором строк «ФИО - текст».