fix: архивация/вложения больше не помечают письма прочитанными (Seen)

Причина: himalaya message read и attachment download используют IMAP BODY[],
который по RFC 3501 выставляет \Seen на сервере (Microsoft Exchange).
Пользователь: письма в ящике после скачивания становятся прочитанными.

Фикс:
- чтение тела: himalaya message read --preview (не ставит Seen)
- вложения: fetch_attachments_imaplib() — сырой IMAP stdlib (socket+ssl),
  UID FETCH (BODY.PEEK[]), папки в modified UTF-7, литералы до 1.5МБ,
  MIME-encoded words, фолбэк himalaya + flag remove seen

Проверено живьём: UID 14200 (INBOX, docx 1.1МБ) флаги ()->() — Seen не выставлен.
Openspec: change no-mark-seen-on-archive заархивирован (2026-09-14-no-mark-seen-on-archive), 7/7 validate OK
This commit is contained in:
2026-09-14 08:08:48 +00:00
parent 8bff6f9aa4
commit 39df85b51c
12 changed files with 662 additions and 16 deletions
@@ -48,3 +48,21 @@
**GIVEN** архив `/opt/hermes/email/`
**WHEN** change применён
**THEN** файлы писем остаются без изменений (проверка: `find /opt/hermes/email -name 'email.md' | wc -l` — то же число, что и до change).
### Requirement: Архивация не помечает письмо прочитанным
Чтение тела письма при архивации MUST NOT выставлять IMAP-флаг `\Seen`.
`mail_archive.py` для получения тела использует `himalaya message read` с
флагом `--preview` (документировано: читает БЕЗ установки `\Seen`).
#### Scenario: Архивация непрочитанного письма
- **GIVEN** письмо в INBOX с флагами `()` (непрочитанное)
- **WHEN** `mail_archive.py` архивирует письмо (пишет `email.md`)
- **THEN** в frontmatter `email.md` флаг `Seen` отсутствует И на IMAP флаги
письма остаются `()`
#### Scenario: Флаги в frontmatter соответствуют IMAP
- **GIVEN** письмо заархивировано после фикса
- **WHEN** флаги письма на IMAP меняются (пользователь прочитал/ответил)
- **THEN** frontmatter `email.md` после следующей синхронизации отражает флаги
IMAP (Seen появляется только если письмо реально прочитано пользователем)