# v0.0.32 — фиксы по ревью обфускатора — 2026-08-18 **Дата:** 2026-08-18 **Версия:** 0.0.31 → 0.0.32 **Ветка:** master (коммит `94e588a`, после rebase над `c3c6e5b`) **Источник:** ревью `History/2026-08-18-review-sonnet-bugs.md` --- ## Внесённые изменения ### 1. Единый обфускатор на все файлы (ТОП-1 — согласованность токенов) - **Было:** `api_bp.py` обрабатывал каждый файл отдельным `obfuscate_files()` → новая инстанс `TwoPassObfuscator` на файл → одна сущность получала РАЗНЫЕ токены в разных файлах, `all_mapping` (ручной разбор CSV) был неверен. - **Стало:** все файлы сессии → один `obfuscate_files(...)` с общим mapping. Проверено: «ООО Ромашка» в двух файлах → один токен «Сфера_0001». - **Побочно:** убран хрупкий ручной разбор CSV `split(",", 2)` (ломавшийся на запятых в значениях) — больше не нужен при едином вызове. ### 2. Рекурсивный `expand_zips` (ТОП-2) - `extractor.py:expand_zips` переписан: очередь + распаковка вложенных ZIP. ZIP внутри ZIP теперь раскрывается. Проверено: вложенный `doc1.txt` извлекается. ### 3. SSE-дисконнект (ТОП-3) - `api_bp.py:generate()`: ловится `(GeneratorExit, BrokenPipeError, ConnectionResetError)` вместо одного `GeneratorExit`. - `obfuscate_files` выполняется в отдельном потоке; прогресс через очередь; флаг отмены (`threading.Event`) при разрыве соединения. - Коллбек прогресса `(phase, idx, total, fname)` где phase ∈ {start, done} — согласуется с фронт-протоколом (`start`/`done` по `idx`). ### 4. Кириллица 1С (CP437→CP866) - `extractor.py._decode_name`: если имя не-UTF-8 (флаг 0x800 снят) и содержит не-ASCII → `name.encode("cp437").decode("cp866")`. Проверено изолированно. ### 5. Дедупликация имён - `builder.py:build_zip`: одинаковое имя+контент → пропуск; имя+разный контент → суффикс `_2`, `_3`... Проверено: `['a.md', 'a_2.md']`. - `obfuscator.py:_dedupe_file_names`: уникализация имён на входе до прохода 1 (защита `all_texts[fname]` от перезаписи). ### 6. upload(): цикл по всем файлам - `api_bp.py:upload()`: обрабатывает все `request.files.getlist("files")` (поддержка загрузки целых папок через `webkitdirectory`). - Сохранено поведение ошибок: файл без имени → «No filename» (совместимо с тестом). ### 7. Лимит объёма сессии - `session.py`: `MAX_SESSION_BYTES = 500 MB`; в `add_file` — скип при превышении. ### 8. Защита от ZIP-бомб - `extractor.py`: проверка `total + file_size > LIMIT` ДО `zf.read`; скип архива при превышении ratio/лимита/числа файлов; лимиты глобально на вызов. --- ## Тесты | Набор | Результат | |-------|-----------| | test_builder | 20/20 OK | | test_zip | 28/28 OK | | test_replacer | 10/10 OK | | test_scanner | 20/20 OK | | test_upload | 13/13 OK | | test_extractor | **14/15** — падает `.doc` (внешний сервис liberta) | `test_extractor` `.doc`: отправляется мусорный OLE2-магик `b"\xD0\xCF\x11\xE0\x00"*10`, сервис `liberta.containerk8s.dev.nubes.ru/convert` возвращает «conversion produced no .docx». НЕ регрессия от v0.0.32 — ветка `.doc`/`doc_to_markdown` не менялась. Вероятно, сервис изменил поведение или не принимает невалидный контент. Требует отдельного разбора. --- ## Git - Remote был впереди (посторонний коммит `c3c6e5b` про магистратуру) → `git pull --rebase`. - Push успешен: `c3c6e5b..94e588a`. - Ветка-сохранение документации: `save-docs-2026-08-18` (`de072d8`). ## TODO / открытые вопросы - [ ] Разобраться с тестом `.doc` (возможно, обновить тест на актуальный сервис). - [ ] Визуально проверить SSE-прогресс во фронте после рефакторинга.