# Tasks: Срез 3a — горячие папки и нормализатор OCR Репозиторий — scan-gateway; команды `make` — из корня, `pytest` — из `api/`. Нумерация и состав задач — как в плане `docs/superpowers/plans/2026-10-06-slice-3a-ocr-normalizer.md` (Task 0–17); пошаговый код и проверки — там. Исполнение — subagent-driven-development: один коммит на задачу плюс правки ревью. CI нет — проверки локальные (`make check`, `make test-integration`, `make check-full`). - [x] 0 Контракт v1.2 в ветке и окружение: `origin/main` влит, схемы байт-в-байт равны `../docs/contracts`, `make venv`, `make check` зелёный; `CONTRACT_VERSION` остаётся `1.0` (решение №14) - [x] 1 Настройки и справочники: `SCAN_CLEANUP`, `SCAN_RULES_DIR`; `api/rules/` (`statement_lines.yaml` с редакциями `66n` и пустой `fsbu4`, `ocr_lookalikes.yaml`, `statement_labels.yaml`); `src/normalizer/rules.py` (`load_rules`, `OcrRules`) - [x] 2 Очистка текста (`src/normalizer/ocr/text.py`): двойники вплотную к цифре, `|` как разделитель клеток, `clean_value` - [x] 3 Разбор дат (`ocr/dates.py`): `parse_date`, дословный перенос `date_utils` старого ядра - [x] 4 Суммы (`ocr/amounts.py`): `parse_amount`, `is_blank` (решения №12, №13) - [x] 5 Раскладка колонок (`ocr/layout.py`): `build_layout` по шапке «Код», `infer_layout` по данным - [x] 6 Строки таблицы (`ocr/rows.py`): шапка настоящая или синтетическая, классы строк, тип по кодам - [x] 7 Шапка файла (`ocr/header.py`): организация, ИНН, единицы, дата отчёта - [x] 8 Сборка документа (`ocr/document.py`): тип, колонки, строки, значения с `is_recognized`, редакции (решения №15, №16, №19, №21–25) - [x] 9 `OcrNormalizer`: статусы `done` / `needs_check` / `error RECOGNITION_FAILED`, предупреждения, `metrics.rows`, подключение в `create_app` - [x] 10 Golden на реальных ответах `../fixtures/ocr/*.json` (эталоны `expected_normalize/` — в fixtures), `make golden-update` - [x] 11 Горячая папка `local`: имя `.`, `.part` в `Tmp`, запись без замены (`os.link`), `cleanup` под `SCAN_CLEANUP` (решения №17, №26, №27) - [x] 12 Горячая папка `smb`: `smbclient.rename` без замены, `cleanup` под флагом - [x] 13 Воркер и приложение: очистка после сохранения `raw/` (под флагом), `RECOGNITION_FAILED` без расширения, `OcrNormalizer` в lifespan - [x] 14 Эмулятор: пара по sha256 содержимого, `SCAN_EMULATOR_DEFAULT`, входящий `.`, игнор `.part`/`.tmp`/`.done` - [x] 15 Сквозной сценарий, README и CLAUDE.md сервиса: `test_roundtrip.py` (документы, `ok_doc`, неизвестное содержимое, `SCAN_CLEANUP=true`, файл результата по схеме), README, CLAUDE.md, этот файл - [ ] 16 Документы workspace (репозиторий docs, bounded-PR): `03-architecture.md`, `05-domain-rules.md` §11.1, OQ, `09-delivery-plan.md`, dev-compose и `qa-guide.md` - [ ] 17 Завершение: синхронизация с `main`, `make check-full`, `openspec validate --strict`, PR fixtures с эталонами, PR scan-gateway