Дорожная карта
Приоритезированный список задач по этапам ТЗ (TZ_pochtovy_client.md) и
технический долг. Статусы: ✅ готово · 🟡 частично · ⬜ не начато.
ТЗ v0.5.0 «Стабильное ядро» (TZ_opus_1.md)
Этап A — критические дефекты:
- ✅ A1 — синхронизация не сбрасывает контекст (v0.4.8)
- ✅ A2 — ссылки в письмах открываются в браузере (v0.4.8)
- ✅ A3 — реальная проверка соединения (IMAP+SMTP, таймаут 10 с, раздельные
ошибки, различение аутентификации и сети) (v0.4.10)
- ✅ Переключатель цепочек писем в настройках (v0.4.10)
- ✅ Лицензионное соглашение (EULA) — установщик, приложение, сайт (v0.4.10)
- ✅ A4 — роли папок через SPECIAL-USE, запрет молчаливого EXPUNGE
(диалог подтверждения, TRASH_NOT_FOUND) (v0.4.10)
- ✅ A5 — сохранение отправленных в «Отправленные» (IMAP APPEND, миграция V006,
per-account smtp_saves_sent, тост при appendFailed) (v0.4.10)
- ✅ A6 — цитирование, Cc, реальные получатели, references (миграция V007) (v0.4.10)
- ✅ A7 — понятная причина ошибки загрузки + «Повторить» + автоповтор при лимите
подключений (api/error.ts) (v0.4.11)
- ✅ A8 — картинки в письмах: cid: inline (data:), проксирование внешних
http(s)-картинок ядром (reqwest, лимиты, кэш html_with_images, миграция
V008), скрытие «битых» внешних, mime/content_id вложений (v0.4.11).
- ✅ Опция «всегда показывать картинки от этого отправителя»: кнопка «Всегда»
в плашке блокировки, доверенные адреса в таблице trusted_senders
(миграция V011), у них картинки встраиваются автоматически при открытии.
Этап A ТЗ v0.5.0 полностью закрыт.
Этап B — честная синхронизация:
- ✅ B1 — реконсиляция: удаления с сервера чистят кэш (
UID SEARCH ALL), флаги
обновляются по всему кэшу папки, а не в окне 500 UID.
- ✅ B2 — догрузка истории страницами (
messages_load_older, миграция V010,
folders.history_complete) + бесконечная прокрутка в UI.
- ✅ B3 — серверный поиск (
UID SEARCH с CHARSET UTF-8 и фоллбэком):
кнопка «Искать на сервере» в поиске и в пустом состоянии; найденные письма
догружаются в кэш (search_server, sync_engine::search_on_server).
Операторы from:/to:/subject: транслируются в IMAP, остальные слова — TEXT
(сервер ищет и по телам); has:/in:/before:/after: применяются локально.
- ⬜ CONDSTORE/QRESYNC (RFC 7162): сейчас реконсиляция каждый цикл делает
UID SEARCH ALL + FETCH FLAGS по всей папке. Корректно, но на ящике в
50k писем это заметный трафик — с CONDSTORE сервер отдавал бы только
изменения с прошлого раза.
Этап C — фон и мультиаккаунт:
- ✅ C1 — IDLE для OAuth-аккаунтов: вход по XOAUTH2 в IDLE-воркере, токен
обновляется по refresh при каждом переподключении. Фоновые уведомления
работают у всех аккаунтов, а не только у парольных.
- ✅ C2 — пул IMAP-соединений (≤ 3 на аккаунт, health-check
NOOP,
core::imap_pool). Устранена причина лимита сессий у Яндекса.
- ✅ C3 — воркеры IDLE поднимаются/останавливаются на лету: реестр воркеров
с флагом остановки, start_for_account при добавлении аккаунта и
stop_for_account при удалении. Перезапуск приложения не нужен.
Этап D — качество продукта:
- ✅ D2 — виртуализация списка (
react-virtuoso). - 🟡 D7 — тесты ядра: покрыты очередь операций (retry/backoff/терминальный
отказ/UIDVALIDITY), outbox (undo-гонка, отложенная отправка, восстановление
после крэша), правила фильтрации, санитайзер (включая CSS/label-векторы),
доверенные отправители, адресная книга и vCard. Не покрыто: инкрементальный
синк и реконсиляция (нужен фейковый IMAP), compute_thread_key,
идемпотентность миграций; на фронте не подключён vitest.
- ✅ D1 — видимые ошибки: тосты вместо пустых
catch в store и компонентах. - ⬜ D3 — i18n (ru/en, i18next): строки UI захардкожены по-русски.
- ⬜ D4 — авто-высота письма (
corvo:height из моста вместо
clamp(280px,55vh,820px)).
- ⬜ D5 — inline-CSS в письмах (allowlist свойств в ammonia). ВНИМАНИЕ:
требует ослабить санитайзер, который сейчас вырезает style целиком —
это защищает от класса атак «разметка проходит фильтр, но опасна при
отрисовке» (см. регрессионные тесты в sanitizer.rs). Делать только с
жёстким allowlist и тестами на url(...), expression(...), position.
- 🟡 D6 — CI написан (
.github/workflows/ci.yml: матрица трёх ОС, clippy
-D warnings, cargo test), но не выполняется: репозиторий на GitFlic,
где GitHub Actions не работают. Нужен пайплайн GitFlic либо зеркало.
- 🟡 D8 — гигиена репозитория сделана (мусор убран из git,
.gitignore
дополнен); описание OAuth в README приведено к фактическому OOB-потоку.
Не сделано: loopback-поток как таковой (опционален по ТЗ).
Сверх ТЗ (решение по итогам обсуждения конкурентности — фокус на ПК-версии):
- ✅ Трей: иконка, меню (Открыть/Написать/Проверить/Выход), клик по иконке,
сворачивание в трей вместо закрытия (настройка minimizeToTray).
- ✅ Автозапуск с системой (
tauri-plugin-autostart) + старт свёрнутым
(--minimized), переключатель в настройках.
- ✅ Счётчик непрочитанных в подсказке трея («Corvo — 5 непрочитанных писем»,
с русским склонением), обновляется после синка и пометки «прочитано».
- ⬜ Экспорт/импорт
.eml/.mbox (возвращено в объём: пользователи не хотят
«запирать» почту в клиенте).
Этап 3 — Отправка почты и композер ✅ (завершён)
- ✅
smtp_client.rs (lettre) + outbox + фоновый воркер, статусы в UI. - ✅ Композер: текстовый редактор, вложения (файлы) + предупреждение > 20 МБ.
- ✅ Ответ / Ответить всем / Переслать (цитирование,
In-Reply-To/References,
префиксы Re:/Fwd:).
- ✅ Черновики: локальное автосохранение (localStorage) + сохранение на сервере
(IMAP APPEND в Drafts, кнопка «Черновик»).
- ✅ Отмена отправки: окно undo (по умолчанию 10 сек) с тостом «Отменить».
- ✅ Отложенная отправка: выбор даты/времени; воркер отправляет по сроку
(в т.ч. после перезапуска — очередь персистентна).
- ✅ Подписи per-account (редактор в композере, автоподстановка).
- ✅ Проверки «пустая тема» и «упомянули вложение, но не приложили».
- ✅ WYSIWYG-редактор (TipTap): жирный/курсив/списки/цитата.
Этап 4 — OAuth 2.0 (Яндекс, Mail.ru) ✅ (завершён)
- ✅ OAuth через системный браузер + PKCE, OOB-поток (
verification_code);
ключи из oauth.json; создание аккаунта; refresh-токен в keyring.
- ✅ XOAUTH2 для IMAP и SMTP — чтение и отправка по OAuth-токену.
- ✅ Автообновление access-токена по refresh (при каждом подключении).
- ✅ Статус «требуется вход» при отзыве токена: IDLE отличает ошибку
аутентификации от сетевой (не реконнектится вечно), шлёт reauth,
индикатор в сайдбаре кликабелен и ведёт в настройки аккаунта; успешный
синк снимает статус и заново поднимает IDLE-воркер.
- ⛔ Gmail/Outlook — исключены (только РФ-провайдеры). У Яндекса почтовый scope
выдаётся только по запросу — на практике для Яндекса надёжнее пароль приложения.
Этап 5 — Мультиаккаунт, цепочки, объединённый inbox ✅ (завершён)
- 🟡 Мультиаккаунт (базово есть; нужен параллельный синк без блокировок UI).
- ✅ Объединённый inbox «Все входящие» с цветовой полоской аккаунта (v0.3.7).
- ✅ Смарт-папки «Непрочитанные» и «С вложениями» по всем аккаунтам (v0.3.7).
- ✅ Группировка в цепочки (
thread_key по References/In-Reply-To), свёрнутый
вид с разворотом (v0.3.9).
- ✅ Snooze (отложить до…) + смарт-папка «Отложенные», авто-возврат (v0.3.10).
- ✅ Правила фильтрации (от/тема/содержит → папка/пометить/удалить) (v0.3.11).
Этап 6 — Поиск (FTS5) ✅ (завершён)
- ✅ Индексация заголовков при синке + тел при открытии письма (v0.4.0).
- ✅ Операторы
from:, to:, subject:, has:attachment, in:, before:/after:. - ✅ Подсветка совпадений в результатах.
- ✅ Серверный
UID SEARCH кнопкой — сделано в рамках B3. - 🟡 Фоновая доиндексация тел всех писем (сейчас тело индексируется при открытии).
Этап 7 — Полировка и дистрибуция
- ✅ Счётчики непрочитанных у папок в сайдбаре (v0.4.1).
- ✅ Горячие клавиши (стиль Gmail) + шпаргалка по «?» (v0.4.1).
- 🟡 Настройки: тема, плотность, интервал синка, уведомления (v0.4.2), окно
undo и выбор папки загрузок (нативный диалог, tauri-plugin-dialog).
Осталось: язык (i18next).
- ✅ Автозапуск и трей (см. «Сверх ТЗ»).
- ⬜ Экспорт/импорт
.eml/.mbox. - ✅ Установщик NSIS (.exe) + автообновления с подписью (Tauri updater),
сервер обновлений в Docker, диалог «О программе» (v0.4.3). Осталось:
DMG/AppImage/deb для macOS/Linux.
- ⬜ Профилирование на 100k писем, аудит безопасности.
Этап 8 (после 1.0)
- ✅ Адресная книга: автосбор контактов из переписки (кто писал нам и кому
писали мы, свои адреса не собираются), подсказки адресов в композере,
диалог «Контакты» (поиск, ручное добавление/правка/удаление, «Написать»),
импорт и экспорт vCard (.vcf). Автосбор не затирает имя, введённое вручную.
- ⬜ Локальный ИИ (суммаризация, черновики), OpenPGP, CalDAV/CardDAV, шаблоны, плагины.
---
Найдено на живом ящике (проверка сборки 0.5.2)
- ✅ Удаление писем падало с
UID MOVE No such folder (9 операций в очереди
провалились после 7 попыток). Причина: папки только добавлялись в кэш и
никогда не удалялись, поэтому у аккаунта копились «призраки» — у Яндекса
на роль trash приходилось три записи (Trash, Удаленные,
Deleted Messages), а find_by_role брал произвольную (LIMIT 1 без
сортировки) и попадал в несуществующую. Исправлено: синк удаляет папки,
которых нет в ответе LIST (delete_missing, защита от пустого списка);
find_by_role предпочитает реально синхронизировавшуюся папку. Заодно
repo_folders::upsert переведён на RETURNING id (тот же дефект
last_insert_rowid, что чинили в upsert_message).
- ✅ Не было синхронизации при запуске:
initApp поднимал только локальный
кэш, а первый синк приходил лишь по интервалу — до 5 минут «старой» почты
после открытия приложения. Теперь синк всех аккаунтов идёт сразу после
инициализации (проверено на живом ящике: старт 11:34:11 → синк 11:34:18).
- ✅ Операции удалённых аккаунтов и давние
failed копились в очереди
(9 записей от 4–7 июля, часть — от аккаунта, удалённого месяц назад).
Добавлена уборка repo_ops::cleanup: сироты (аккаунта нет — внешнего ключа
у op_queue тоже) и failed старше 30 дней. Вызывается при старте воркера
и при удалении аккаунта. Ожидающие операции не трогаются.
Очередь пользователя разово очищена (9 записей).
- ⬜ Показывать проваленные операции в UI: сейчас пользователь не узнаёт, что
удаление/перемещение не доехало до сервера (письмо исчезло локально, но
осталось на сервере).
Технический долг и известные ограничения
- ~~IDLE-воркеры требуют перезапуска приложения~~ — закрыто в C3.
- ~~Очередь операций открывает отдельное IMAP-соединение на каждую операцию~~
— операции группируются по аккаунту и выполняются на одной сессии из пула.
Денормализованные креды из строк очереди убраны (миграция V012): воркер берёт
соединение по account_id, а копии host/email/keyring_ref устаревали после
изменения настроек аккаунта.
- Локализация i18next ещё не подключена: часть строк UI захардкожена по-русски.
Нужно вынести в i18next (ru/en), как требует ТЗ.
- ~~Виртуализация списка не внедрена~~ —
react-virtuoso подключён в
MessageList.tsx (D2).
- ~~
#![allow(dead_code)] в ядре~~ — снят. Мёртвый код удалён (OutboxItem,
repo_folders::delete_by_account, repo_ops::enqueue/pending_count, неиспользуемые
константы событий, ImapFolder.delimiter); осознанные исключения помечены
точечно с объяснением (AppError::Smtp/Network, ParsedEmail). Сборка и
cargo clippy --all-targets — без предупреждений.
- Свёрнутая цепочка в панели чтения (из макета) появится с группировкой в
цепочки (Этап 5).
- CI (GitHub Actions/аналог): сборка под три ОС, clippy, тесты — не настроен.
imap-proto выдаёт future-incompat предупреждение (транзитивная зависимость).
Приоритезированный список задач по этапам ТЗ (TZ_pochtovy_client.md) и
технический долг. Статусы: ✅ готово · 🟡 частично · ⬜ не начато.
ТЗ v0.5.0 «Стабильное ядро» (TZ_opus_1.md)
Этап A — критические дефекты:
- ✅ A1 — синхронизация не сбрасывает контекст (v0.4.8)
- ✅ A2 — ссылки в письмах открываются в браузере (v0.4.8)
- ✅ A3 — реальная проверка соединения (IMAP+SMTP, таймаут 10 с, раздельные
ошибки, различение аутентификации и сети) (v0.4.10)
- ✅ Переключатель цепочек писем в настройках (v0.4.10)
- ✅ Лицензионное соглашение (EULA) — установщик, приложение, сайт (v0.4.10)
- ✅ A4 — роли папок через SPECIAL-USE, запрет молчаливого EXPUNGE
(диалог подтверждения, TRASH_NOT_FOUND) (v0.4.10)
- ✅ A5 — сохранение отправленных в «Отправленные» (IMAP APPEND, миграция V006,
per-account smtp_saves_sent, тост при appendFailed) (v0.4.10)
- ✅ A6 — цитирование, Cc, реальные получатели, references (миграция V007) (v0.4.10)
- ✅ A7 — понятная причина ошибки загрузки + «Повторить» + автоповтор при лимите
подключений (api/error.ts) (v0.4.11)
- ✅ A8 — картинки в письмах: cid: inline (data:), проксирование внешних
http(s)-картинок ядром (reqwest, лимиты, кэш html_with_images, миграция
V008), скрытие «битых» внешних, mime/content_id вложений (v0.4.11).
- ✅ Опция «всегда показывать картинки от этого отправителя»: кнопка «Всегда»
в плашке блокировки, доверенные адреса в таблице trusted_senders
(миграция V011), у них картинки встраиваются автоматически при открытии.
Этап A ТЗ v0.5.0 полностью закрыт.
Этап B — честная синхронизация:
- ✅ B1 — реконсиляция: удаления с сервера чистят кэш (
UID SEARCH ALL), флаги
обновляются по всему кэшу папки, а не в окне 500 UID.
- ✅ B2 — догрузка истории страницами (
messages_load_older, миграция V010,
folders.history_complete) + бесконечная прокрутка в UI.
- ✅ B3 — серверный поиск (
UID SEARCHсCHARSET UTF-8и фоллбэком):
кнопка «Искать на сервере» в поиске и в пустом состоянии; найденные письма
догружаются в кэш (search_server, sync_engine::search_on_server).
Операторы from:/to:/subject: транслируются в IMAP, остальные слова — TEXT
(сервер ищет и по телам); has:/in:/before:/after: применяются локально.
- ⬜ CONDSTORE/QRESYNC (RFC 7162): сейчас реконсиляция каждый цикл делает
UID SEARCH ALL + FETCH FLAGS по всей папке. Корректно, но на ящике в
50k писем это заметный трафик — с CONDSTORE сервер отдавал бы только
изменения с прошлого раза.
Этап C — фон и мультиаккаунт:
- ✅ C1 — IDLE для OAuth-аккаунтов: вход по XOAUTH2 в IDLE-воркере, токен
обновляется по refresh при каждом переподключении. Фоновые уведомления
работают у всех аккаунтов, а не только у парольных.
- ✅ C2 — пул IMAP-соединений (≤ 3 на аккаунт, health-check
NOOP,
core::imap_pool). Устранена причина лимита сессий у Яндекса.
- ✅ C3 — воркеры IDLE поднимаются/останавливаются на лету: реестр воркеров
с флагом остановки, start_for_account при добавлении аккаунта и
stop_for_account при удалении. Перезапуск приложения не нужен.
Этап D — качество продукта:
- ✅ D2 — виртуализация списка (
react-virtuoso). - 🟡 D7 — тесты ядра: покрыты очередь операций (retry/backoff/терминальный
отказ/UIDVALIDITY), outbox (undo-гонка, отложенная отправка, восстановление
после крэша), правила фильтрации, санитайзер (включая CSS/label-векторы),
доверенные отправители, адресная книга и vCard. Не покрыто: инкрементальный
синк и реконсиляция (нужен фейковый IMAP), compute_thread_key,
идемпотентность миграций; на фронте не подключён vitest.
- ✅ D1 — видимые ошибки: тосты вместо пустых
catchв store и компонентах. - ⬜ D3 — i18n (ru/en, i18next): строки UI захардкожены по-русски.
- ⬜ D4 — авто-высота письма (
corvo:heightиз моста вместо
clamp(280px,55vh,820px)).
- ⬜ D5 — inline-CSS в письмах (allowlist свойств в ammonia). ВНИМАНИЕ:
требует ослабить санитайзер, который сейчас вырезает style целиком —
это защищает от класса атак «разметка проходит фильтр, но опасна при
отрисовке» (см. регрессионные тесты в sanitizer.rs). Делать только с
жёстким allowlist и тестами на url(...), expression(...), position.
- 🟡 D6 — CI написан (
.github/workflows/ci.yml: матрица трёх ОС, clippy
-D warnings, cargo test), но не выполняется: репозиторий на GitFlic,
где GitHub Actions не работают. Нужен пайплайн GitFlic либо зеркало.
- 🟡 D8 — гигиена репозитория сделана (мусор убран из git,
.gitignore
дополнен); описание OAuth в README приведено к фактическому OOB-потоку.
Не сделано: loopback-поток как таковой (опционален по ТЗ).
Сверх ТЗ (решение по итогам обсуждения конкурентности — фокус на ПК-версии):
- ✅ Трей: иконка, меню (Открыть/Написать/Проверить/Выход), клик по иконке,
сворачивание в трей вместо закрытия (настройка minimizeToTray).
- ✅ Автозапуск с системой (
tauri-plugin-autostart) + старт свёрнутым
(--minimized), переключатель в настройках.
- ✅ Счётчик непрочитанных в подсказке трея («Corvo — 5 непрочитанных писем»,
с русским склонением), обновляется после синка и пометки «прочитано».
- ⬜ Экспорт/импорт
.eml/.mbox(возвращено в объём: пользователи не хотят
«запирать» почту в клиенте).
Этап 3 — Отправка почты и композер ✅ (завершён)
- ✅
smtp_client.rs(lettre) +outbox+ фоновый воркер, статусы в UI. - ✅ Композер: текстовый редактор, вложения (файлы) + предупреждение > 20 МБ.
- ✅ Ответ / Ответить всем / Переслать (цитирование,
In-Reply-To/References,
префиксы Re:/Fwd:).
- ✅ Черновики: локальное автосохранение (localStorage) + сохранение на сервере
(IMAP APPEND в Drafts, кнопка «Черновик»).
- ✅ Отмена отправки: окно undo (по умолчанию 10 сек) с тостом «Отменить».
- ✅ Отложенная отправка: выбор даты/времени; воркер отправляет по сроку
(в т.ч. после перезапуска — очередь персистентна).
- ✅ Подписи per-account (редактор в композере, автоподстановка).
- ✅ Проверки «пустая тема» и «упомянули вложение, но не приложили».
- ✅ WYSIWYG-редактор (TipTap): жирный/курсив/списки/цитата.
Этап 4 — OAuth 2.0 (Яндекс, Mail.ru) ✅ (завершён)
- ✅ OAuth через системный браузер + PKCE, OOB-поток (
verification_code);
ключи из oauth.json; создание аккаунта; refresh-токен в keyring.
- ✅ XOAUTH2 для IMAP и SMTP — чтение и отправка по OAuth-токену.
- ✅ Автообновление access-токена по refresh (при каждом подключении).
- ✅ Статус «требуется вход» при отзыве токена: IDLE отличает ошибку
аутентификации от сетевой (не реконнектится вечно), шлёт reauth,
индикатор в сайдбаре кликабелен и ведёт в настройки аккаунта; успешный
синк снимает статус и заново поднимает IDLE-воркер.
- ⛔ Gmail/Outlook — исключены (только РФ-провайдеры). У Яндекса почтовый scope
выдаётся только по запросу — на практике для Яндекса надёжнее пароль приложения.
Этап 5 — Мультиаккаунт, цепочки, объединённый inbox ✅ (завершён)
- 🟡 Мультиаккаунт (базово есть; нужен параллельный синк без блокировок UI).
- ✅ Объединённый inbox «Все входящие» с цветовой полоской аккаунта (v0.3.7).
- ✅ Смарт-папки «Непрочитанные» и «С вложениями» по всем аккаунтам (v0.3.7).
- ✅ Группировка в цепочки (
thread_keyпо References/In-Reply-To), свёрнутый
вид с разворотом (v0.3.9).
- ✅ Snooze (отложить до…) + смарт-папка «Отложенные», авто-возврат (v0.3.10).
- ✅ Правила фильтрации (от/тема/содержит → папка/пометить/удалить) (v0.3.11).
Этап 6 — Поиск (FTS5) ✅ (завершён)
- ✅ Индексация заголовков при синке + тел при открытии письма (v0.4.0).
- ✅ Операторы
from:,to:,subject:,has:attachment,in:,before:/after:. - ✅ Подсветка совпадений в результатах.
- ✅ Серверный
UID SEARCHкнопкой — сделано в рамках B3. - 🟡 Фоновая доиндексация тел всех писем (сейчас тело индексируется при открытии).
Этап 7 — Полировка и дистрибуция
- ✅ Счётчики непрочитанных у папок в сайдбаре (v0.4.1).
- ✅ Горячие клавиши (стиль Gmail) + шпаргалка по «?» (v0.4.1).
- 🟡 Настройки: тема, плотность, интервал синка, уведомления (v0.4.2), окно
undo и выбор папки загрузок (нативный диалог, tauri-plugin-dialog).
Осталось: язык (i18next).
- ✅ Автозапуск и трей (см. «Сверх ТЗ»).
- ⬜ Экспорт/импорт
.eml/.mbox. - ✅ Установщик NSIS (.exe) + автообновления с подписью (Tauri updater),
сервер обновлений в Docker, диалог «О программе» (v0.4.3). Осталось:
DMG/AppImage/deb для macOS/Linux.
- ⬜ Профилирование на 100k писем, аудит безопасности.
Этап 8 (после 1.0)
- ✅ Адресная книга: автосбор контактов из переписки (кто писал нам и кому
писали мы, свои адреса не собираются), подсказки адресов в композере,
диалог «Контакты» (поиск, ручное добавление/правка/удаление, «Написать»),
импорт и экспорт vCard (.vcf). Автосбор не затирает имя, введённое вручную.
- ⬜ Локальный ИИ (суммаризация, черновики), OpenPGP, CalDAV/CardDAV, шаблоны, плагины.
---
Найдено на живом ящике (проверка сборки 0.5.2)
- ✅ Удаление писем падало с
UID MOVE No such folder(9 операций в очереди
провалились после 7 попыток). Причина: папки только добавлялись в кэш и
никогда не удалялись, поэтому у аккаунта копились «призраки» — у Яндекса
на роль trash приходилось три записи (Trash, Удаленные,
Deleted Messages), а find_by_role брал произвольную (LIMIT 1 без
сортировки) и попадал в несуществующую. Исправлено: синк удаляет папки,
которых нет в ответе LIST (delete_missing, защита от пустого списка);
find_by_role предпочитает реально синхронизировавшуюся папку. Заодно
repo_folders::upsert переведён на RETURNING id (тот же дефект
last_insert_rowid, что чинили в upsert_message).
- ✅ Не было синхронизации при запуске:
initAppподнимал только локальный
кэш, а первый синк приходил лишь по интервалу — до 5 минут «старой» почты
после открытия приложения. Теперь синк всех аккаунтов идёт сразу после
инициализации (проверено на живом ящике: старт 11:34:11 → синк 11:34:18).
- ✅ Операции удалённых аккаунтов и давние
failedкопились в очереди
(9 записей от 4–7 июля, часть — от аккаунта, удалённого месяц назад).
Добавлена уборка repo_ops::cleanup: сироты (аккаунта нет — внешнего ключа
у op_queue тоже) и failed старше 30 дней. Вызывается при старте воркера
и при удалении аккаунта. Ожидающие операции не трогаются.
Очередь пользователя разово очищена (9 записей).
- ⬜ Показывать проваленные операции в UI: сейчас пользователь не узнаёт, что
удаление/перемещение не доехало до сервера (письмо исчезло локально, но
осталось на сервере).
Технический долг и известные ограничения
- ~~IDLE-воркеры требуют перезапуска приложения~~ — закрыто в C3.
- ~~Очередь операций открывает отдельное IMAP-соединение на каждую операцию~~
— операции группируются по аккаунту и выполняются на одной сессии из пула.
Денормализованные креды из строк очереди убраны (миграция V012): воркер берёт
соединение по account_id, а копии host/email/keyring_ref устаревали после
изменения настроек аккаунта.
- Локализация i18next ещё не подключена: часть строк UI захардкожена по-русски.
Нужно вынести в i18next (ru/en), как требует ТЗ.
- ~~Виртуализация списка не внедрена~~ —
react-virtuosoподключён в
MessageList.tsx (D2).
- ~~
#![allow(dead_code)]в ядре~~ — снят. Мёртвый код удалён (OutboxItem,
repo_folders::delete_by_account, repo_ops::enqueue/pending_count, неиспользуемые
константы событий, ImapFolder.delimiter); осознанные исключения помечены
точечно с объяснением (AppError::Smtp/Network, ParsedEmail). Сборка и
cargo clippy --all-targets — без предупреждений.
- Свёрнутая цепочка в панели чтения (из макета) появится с группировкой в
цепочки (Этап 5).
- CI (GitHub Actions/аналог): сборка под три ОС, clippy, тесты — не настроен.
imap-protoвыдаёт future-incompat предупреждение (транзитивная зависимость).