Commit graph

1 commit

Author SHA1 Message Date
oskar c65f0f2075 feat(kb-mail): adapter IMAP + model stanu synca + migracja 005
Krok 7 fazy mailowej, warstwa wspoldzielona. Realizuje decyzje (a), (b), (f)
reconu kb/audits/mail-sync-2026-08-06.md (zatwierdzone przez operatora
2026-08-06): jeden adapter IMAP na oba konta, stan synca jako tabela w bazie.

Nowe moduly w packages/kb-mail:
- imap.py    — ImapAccount/ImapClient nad stdlib imaplib (zero nowych zaleznosci).
               Foldery otwierane READ-ONLY (EXAMINE) i pobierane przez BODY.PEEK[],
               zeby job nie ustawial \Seen na skrzynce operatora. Wybor folderu po
               atrybucie SPECIAL-USE, nigdy po nazwie — Gmail lokalizuje
               "[Gmail]/All Mail". search_from_uid filtruje zakres po stronie
               klienta, bo n:* zwraca ostatnia wiadomosc takze gdy przedzial pusty.
- sync_state.py — tabela mail_sync_state + czyste funkcje: plan_folder_sync
               (pierwszy tick / przyrost / uniewaznienie UIDVALIDITY) i
               contiguous_last_uid (kursor przesuwa sie tylko po nieprzerwanym
               ciagu sukcesow — bledna wiadomosc jest ponawiana, nie przeskakiwana).
- headers.py / message.py — parse_headers(+fallback) z gmail-header-backfill oraz
               message_id/parse_date/parse_attachments/eml_ref z gmail-bulk-import,
               przeniesione zamiast skopiowane. Klucz dedup musi pochodzic z jednej
               implementacji: kazdy insert przyrostowki trafia na 225 030 istniejacych
               id. Stare joby re-eksportuja te nazwy — ich CLI i testy bez zmian.

kb_mail.db.insert_envelope zwraca teraz command tag (+ rows_affected,
envelope_source) — bez tego nie da sie odroznic zwyklego duplikatu od kolizji
Message-ID miedzy kontami (recon §2.4).

Migracja 005_mail_sync_state.sql: addytywna, klucz (account, folder).

Testy: 285 passed (111 kb-mail w tym 37 adaptera IMAP na fake serwerze i 26
planera kursora; 174 istniejace suity jobow bez zmian po ekstrakcji).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 15:30:57 +02:00