добавлен возобновляемый нормализатор локального архива ЦБД без внешних Python-зависимостей;
формируются канонические двуязычные метаданные, безопасный HTML, чистый текст и детерминированные фрагменты;
добавлены SHA-256, показатели качества, атомарная публикация результатов и SQLite-манифест для пропуска неизменившихся документов и повторной обработки ошибок;
загрузчик теперь пересоздаёт временный список документов после исчерпания повторов запроса страницы, а не только после HTTP 404;
добавлены тесты нормализатора и регрессионный тест восстановления пагинации;
добавлены план frontend поисковой СПС, исходное функциональное задание и задание агенту по нормализации;
локальные data/ и logs/ исключены из Git;
версия backend повышена до 0.2.1.
Ограничения этой версии
OCR, перевод, юридическое сопоставление редакций, PostgreSQL/OpenSearch и RAG намеренно оставлены для следующих этапов. Исходный архив не изменяется.
полный проход ранее нормализовал 209811 документов без ошибок.
## Что сделано
- добавлен возобновляемый нормализатор локального архива ЦБД без внешних Python-зависимостей;
- формируются канонические двуязычные метаданные, безопасный HTML, чистый текст и детерминированные фрагменты;
- добавлены SHA-256, показатели качества, атомарная публикация результатов и SQLite-манифест для пропуска неизменившихся документов и повторной обработки ошибок;
- загрузчик теперь пересоздаёт временный список документов после исчерпания повторов запроса страницы, а не только после HTTP 404;
- добавлены тесты нормализатора и регрессионный тест восстановления пагинации;
- добавлены план frontend поисковой СПС, исходное функциональное задание и задание агенту по нормализации;
- локальные data/ и logs/ исключены из Git;
- версия backend повышена до 0.2.1.
## Ограничения этой версии
OCR, перевод, юридическое сопоставление редакций, PostgreSQL/OpenSearch и RAG намеренно оставлены для следующих этапов. Исходный архив не изменяется.
## Проверка
- PYTHONPATH=backend python3 -m unittest backend/test_minjust_cbd.py backend/test_minjust_normalization.py -v — 8 тестов, OK;
- python3 -m unittest discover -s tools/telegram-bot -v — 6 тестов, OK;
- python3 -m py_compile backend/ingestion/minjust_cbd.py backend/normalization/minjust_cbd.py — OK;
- git diff --check — OK;
- полный проход ранее нормализовал 209811 документов без ошибок.
Add a resumable standard-library normalization pipeline for the downloaded CBD archive. It produces canonical bilingual metadata, sanitized HTML, plain text, deterministic fragments, checksums, quality markers, and an SQLite processing manifest while preserving the raw source.
Recover document-list pagination when the Ministry API exhausts request retries, and cover that scenario with a regression test.
Document the normalization workflow and frontend-search MVP plan, include the source functional specification, ignore local runtime logs, and bump the backend version to 0.2.1.
Высокая — ошибочная конфигурация путей может удалить исходный архив.backend/normalization/minjust_cbd.py:534-545, backend/normalization/minjust_cbd.py:574-577. Код не проверяет пересечение input_root и output. Если передать одинаковые пути (например, --input data/minjust-cbd --output data/minjust-cbd), целевой каталог output/documents/<code> совпадает с исходным документом: publish_directory() переносит сырой каталог в .previous, ставит на его место нормализованный результат и затем удаляет backup. На временных данных сценарий воспроизведён: metadata.json исходника исчез, остался document.json. Это прямо нарушает обязательное требование не изменять data/minjust-cbd. Нужно до открытия manifest и обработки документов сравнить resolve() путей (с учётом symlink) и отклонять равные/пересекающиеся input/output; добавить регрессионный тест.
Средняя — manifest может навсегда скрыть отсутствующий результат после прерывания публикации.backend/normalization/minjust_cbd.py:517-531, backend/normalization/minjust_cbd.py:569-572. publish_directory() сначала переносит target в .previous, затем ставит staged на его место. Если процесс завершится между этими операциями, target отсутствует, но manifest по-прежнему содержит прежний success. При следующем обычном запуске совпадение checksum/schema/version приводит к skipped, без проверки существования documents/<code>/document.json и без восстановления .previous. Сценарий воспроизведён в /tmp: повторный запуск вернул skipped=1, target остался отсутствовать. Нужны восстановление незавершённой публикации при старте и/или проверка целевого результата перед skip; добавить тест имитации прерывания.
Низкая — некорректный URL в HTML валит нормализацию всего документа вместо удаления атрибута.backend/normalization/minjust_cbd.py:222-225, backend/normalization/minjust_cbd.py:236-255. urlsplit() может выбросить ValueError; пример href="http://[" воспроизведён. Ошибка из санитайзера переводит весь документ в error. На входной границе безопаснее считать такой href/src недопустимым и удалить атрибут; нужен граничный тест.
Что проверено
изучен полный diff origin/main...07381ad (17 файлов);
отдельно проверены санитайзер, конфликт input/output и состояние после прерванной публикации на временных данных. Полный проход и запись в локальный data/ не выполнялись.
Остальная реализация выглядит целостно: нормализатор использует стандартную библиотеку, сохраняет происхождение и контрольные суммы, разделяет RU/KY, публикует документ через staging и изолирует ошибку отдельной записи; исправление восстановления пагинации покрыто регрессионным тестом. Остаточные риски после перечисленных исправлений: отсутствие межпроцессной блокировки и стоимость полного повторного хеширования архива примерно из 210 тысяч документов.
## Независимое code review
**Итог: перед слиянием нужны исправления.**
### Замечания
1. **Высокая — ошибочная конфигурация путей может удалить исходный архив.** `backend/normalization/minjust_cbd.py:534-545`, `backend/normalization/minjust_cbd.py:574-577`. Код не проверяет пересечение `input_root` и `output`. Если передать одинаковые пути (например, `--input data/minjust-cbd --output data/minjust-cbd`), целевой каталог `output/documents/<code>` совпадает с исходным документом: `publish_directory()` переносит сырой каталог в `.previous`, ставит на его место нормализованный результат и затем удаляет backup. На временных данных сценарий воспроизведён: `metadata.json` исходника исчез, остался `document.json`. Это прямо нарушает обязательное требование не изменять `data/minjust-cbd`. Нужно до открытия manifest и обработки документов сравнить `resolve()` путей (с учётом symlink) и отклонять равные/пересекающиеся input/output; добавить регрессионный тест.
2. **Средняя — manifest может навсегда скрыть отсутствующий результат после прерывания публикации.** `backend/normalization/minjust_cbd.py:517-531`, `backend/normalization/minjust_cbd.py:569-572`. `publish_directory()` сначала переносит target в `.previous`, затем ставит staged на его место. Если процесс завершится между этими операциями, target отсутствует, но manifest по-прежнему содержит прежний `success`. При следующем обычном запуске совпадение checksum/schema/version приводит к `skipped`, без проверки существования `documents/<code>/document.json` и без восстановления `.previous`. Сценарий воспроизведён в `/tmp`: повторный запуск вернул `skipped=1`, target остался отсутствовать. Нужны восстановление незавершённой публикации при старте и/или проверка целевого результата перед skip; добавить тест имитации прерывания.
3. **Низкая — некорректный URL в HTML валит нормализацию всего документа вместо удаления атрибута.** `backend/normalization/minjust_cbd.py:222-225`, `backend/normalization/minjust_cbd.py:236-255`. `urlsplit()` может выбросить `ValueError`; пример `href="http://["` воспроизведён. Ошибка из санитайзера переводит весь документ в `error`. На входной границе безопаснее считать такой `href/src` недопустимым и удалить атрибут; нужен граничный тест.
### Что проверено
- изучен полный diff `origin/main...07381ad` (17 файлов);
- `PYTHONPATH=backend python3 -m unittest backend/test_minjust_cbd.py backend/test_minjust_normalization.py -v` — **8 тестов, OK**;
- `python3 -m unittest discover -s tools/telegram-bot -v` — **6 тестов, OK**;
- `python3 -m py_compile backend/ingestion/minjust_cbd.py backend/normalization/minjust_cbd.py` — **OK**;
- `git diff --check origin/main...HEAD` — **OK**;
- отдельно проверены санитайзер, конфликт input/output и состояние после прерванной публикации на временных данных. Полный проход и запись в локальный `data/` не выполнялись.
Остальная реализация выглядит целостно: нормализатор использует стандартную библиотеку, сохраняет происхождение и контрольные суммы, разделяет RU/KY, публикует документ через staging и изолирует ошибку отдельной записи; исправление восстановления пагинации покрыто регрессионным тестом. Остаточные риски после перечисленных исправлений: отсутствие межпроцессной блокировки и стоимость полного повторного хеширования архива примерно из 210 тысяч документов.
Reject overlapping source and destination roots before any write so normalization cannot replace the raw Ministry archive.
Recover an interrupted directory publication before resume checks and require the normalized target to exist before skipping a manifest success.
Treat malformed link and image URLs as unsafe attributes, add regressions for all review findings, and bump the backend version to 0.2.2.
Итог: замечания из предыдущего review закрыты, PR можно сливать.
Проверка прежних замечаний
HIGH — пересечение input/output: закрыто. Добавленный validate_roots() сравнивает разрешённые абсолютные пути и отклоняет равенство, вложение output в input и input в output до создания выходного каталога и manifest. Тест проверяет все три варианта и неизменность исходного metadata.json.
MEDIUM — восстановление .previous и ошибочный skip: закрыто.recover_directory(target) вызывается перед вычислением решения о skip: если target отсутствует, backup атомарно возвращается на место; если target уже опубликован, оставшийся backup удаляется. Дополнительная проверка target.is_dir() не позволяет manifest со статусом success пропустить полностью отсутствующий результат. Регрессионный тест воспроизводит прерывание target -> .previous и подтверждает восстановление до skip.
LOW — malformed URL: закрыто.safe_link() и safe_image() теперь перехватывают ValueError от urlsplit() и считают URL небезопасным. Тест покрывает href="http://[" и src="http://["; документ нормализуется, URL отсутствует в безопасном HTML.
Регрессии и проверки
Новых блокирующих замечаний в diff 07381ad..752bd38 не найдено. Исправления локальны, не добавляют зависимостей, backend patch-версия корректно повышена до 0.2.2, документация и footer-версии обновлены.
Полный проход и изменения локального data/ не выполнялись. Неблокирующие прежние остаточные риски (межпроцессная блокировка и стоимость полного повторного хеширования около 210 тысяч документов) остаются отдельными будущими задачами и не препятствуют слиянию этого PR.
## Повторное code review после `752bd38`
**Итог: замечания из предыдущего review закрыты, PR можно сливать.**
### Проверка прежних замечаний
1. **HIGH — пересечение input/output: закрыто.** Добавленный `validate_roots()` сравнивает разрешённые абсолютные пути и отклоняет равенство, вложение output в input и input в output до создания выходного каталога и manifest. Тест проверяет все три варианта и неизменность исходного `metadata.json`.
2. **MEDIUM — восстановление `.previous` и ошибочный skip: закрыто.** `recover_directory(target)` вызывается перед вычислением решения о skip: если target отсутствует, backup атомарно возвращается на место; если target уже опубликован, оставшийся backup удаляется. Дополнительная проверка `target.is_dir()` не позволяет manifest со статусом `success` пропустить полностью отсутствующий результат. Регрессионный тест воспроизводит прерывание `target -> .previous` и подтверждает восстановление до skip.
3. **LOW — malformed URL: закрыто.** `safe_link()` и `safe_image()` теперь перехватывают `ValueError` от `urlsplit()` и считают URL небезопасным. Тест покрывает `href="http://["` и `src="http://["`; документ нормализуется, URL отсутствует в безопасном HTML.
### Регрессии и проверки
Новых блокирующих замечаний в diff `07381ad..752bd38` не найдено. Исправления локальны, не добавляют зависимостей, backend patch-версия корректно повышена до `0.2.2`, документация и footer-версии обновлены.
- `PYTHONPATH=backend python3 -m unittest backend/test_minjust_cbd.py backend/test_minjust_normalization.py -v` — **10 тестов, OK**;
- `python3 -m unittest discover -s tools/telegram-bot -v` — **6 тестов, OK**;
- `python3 -m py_compile backend/ingestion/minjust_cbd.py backend/normalization/minjust_cbd.py` — **OK**;
- `git diff --check origin/main...HEAD` — **OK**.
Полный проход и изменения локального `data/` не выполнялись. Неблокирующие прежние остаточные риски (межпроцессная блокировка и стоимость полного повторного хеширования около 210 тысяч документов) остаются отдельными будущими задачами и не препятствуют слиянию этого PR.
admin
merged commit 30065925e4 into main2026-08-12 06:54:19 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Что сделано
Ограничения этой версии
OCR, перевод, юридическое сопоставление редакций, PostgreSQL/OpenSearch и RAG намеренно оставлены для следующих этапов. Исходный архив не изменяется.
Проверка
Независимое code review
Итог: перед слиянием нужны исправления.
Замечания
Высокая — ошибочная конфигурация путей может удалить исходный архив.
backend/normalization/minjust_cbd.py:534-545,backend/normalization/minjust_cbd.py:574-577. Код не проверяет пересечениеinput_rootиoutput. Если передать одинаковые пути (например,--input data/minjust-cbd --output data/minjust-cbd), целевой каталогoutput/documents/<code>совпадает с исходным документом:publish_directory()переносит сырой каталог в.previous, ставит на его место нормализованный результат и затем удаляет backup. На временных данных сценарий воспроизведён:metadata.jsonисходника исчез, осталсяdocument.json. Это прямо нарушает обязательное требование не изменятьdata/minjust-cbd. Нужно до открытия manifest и обработки документов сравнитьresolve()путей (с учётом symlink) и отклонять равные/пересекающиеся input/output; добавить регрессионный тест.Средняя — manifest может навсегда скрыть отсутствующий результат после прерывания публикации.
backend/normalization/minjust_cbd.py:517-531,backend/normalization/minjust_cbd.py:569-572.publish_directory()сначала переносит target в.previous, затем ставит staged на его место. Если процесс завершится между этими операциями, target отсутствует, но manifest по-прежнему содержит прежнийsuccess. При следующем обычном запуске совпадение checksum/schema/version приводит кskipped, без проверки существованияdocuments/<code>/document.jsonи без восстановления.previous. Сценарий воспроизведён в/tmp: повторный запуск вернулskipped=1, target остался отсутствовать. Нужны восстановление незавершённой публикации при старте и/или проверка целевого результата перед skip; добавить тест имитации прерывания.Низкая — некорректный URL в HTML валит нормализацию всего документа вместо удаления атрибута.
backend/normalization/minjust_cbd.py:222-225,backend/normalization/minjust_cbd.py:236-255.urlsplit()может выброситьValueError; примерhref="http://["воспроизведён. Ошибка из санитайзера переводит весь документ вerror. На входной границе безопаснее считать такойhref/srcнедопустимым и удалить атрибут; нужен граничный тест.Что проверено
origin/main...07381ad(17 файлов);PYTHONPATH=backend python3 -m unittest backend/test_minjust_cbd.py backend/test_minjust_normalization.py -v— 8 тестов, OK;python3 -m unittest discover -s tools/telegram-bot -v— 6 тестов, OK;python3 -m py_compile backend/ingestion/minjust_cbd.py backend/normalization/minjust_cbd.py— OK;git diff --check origin/main...HEAD— OK;data/не выполнялись.Остальная реализация выглядит целостно: нормализатор использует стандартную библиотеку, сохраняет происхождение и контрольные суммы, разделяет RU/KY, публикует документ через staging и изолирует ошибку отдельной записи; исправление восстановления пагинации покрыто регрессионным тестом. Остаточные риски после перечисленных исправлений: отсутствие межпроцессной блокировки и стоимость полного повторного хеширования архива примерно из 210 тысяч документов.
Повторное code review после
752bd38Итог: замечания из предыдущего review закрыты, PR можно сливать.
Проверка прежних замечаний
HIGH — пересечение input/output: закрыто. Добавленный
validate_roots()сравнивает разрешённые абсолютные пути и отклоняет равенство, вложение output в input и input в output до создания выходного каталога и manifest. Тест проверяет все три варианта и неизменность исходногоmetadata.json.MEDIUM — восстановление
.previousи ошибочный skip: закрыто.recover_directory(target)вызывается перед вычислением решения о skip: если target отсутствует, backup атомарно возвращается на место; если target уже опубликован, оставшийся backup удаляется. Дополнительная проверкаtarget.is_dir()не позволяет manifest со статусомsuccessпропустить полностью отсутствующий результат. Регрессионный тест воспроизводит прерываниеtarget -> .previousи подтверждает восстановление до skip.LOW — malformed URL: закрыто.
safe_link()иsafe_image()теперь перехватываютValueErrorотurlsplit()и считают URL небезопасным. Тест покрываетhref="http://["иsrc="http://["; документ нормализуется, URL отсутствует в безопасном HTML.Регрессии и проверки
Новых блокирующих замечаний в diff
07381ad..752bd38не найдено. Исправления локальны, не добавляют зависимостей, backend patch-версия корректно повышена до0.2.2, документация и footer-версии обновлены.PYTHONPATH=backend python3 -m unittest backend/test_minjust_cbd.py backend/test_minjust_normalization.py -v— 10 тестов, OK;python3 -m unittest discover -s tools/telegram-bot -v— 6 тестов, OK;python3 -m py_compile backend/ingestion/minjust_cbd.py backend/normalization/minjust_cbd.py— OK;git diff --check origin/main...HEAD— OK.Полный проход и изменения локального
data/не выполнялись. Неблокирующие прежние остаточные риски (межпроцессная блокировка и стоимость полного повторного хеширования около 210 тысяч документов) остаются отдельными будущими задачами и не препятствуют слиянию этого PR.