# Design ## Context См. `proposal.md`, раздел Why. Переход уже отражён в staged-изменениях сервиса; этот change документирует выполненную работу и оставшиеся проверки. Основные спецификации после отмены предыдущих правок по-прежнему описывают копию исходников. Их изменение вынесено в delta-файлы этого change. Пример локальной установки — `../arm-core/Makefile`. Дистрибутив находится в `../pylib_ai_arm_statements`: имя установки `pylib-ai-arm-statements`, версия `0.1.1`, имя импорта `arm_statements`. Его API `statements_version(path)` читает VERSION, затем метаданные пакета; явный путь используется тестами и не включает fallback. `default_rules()` загружает словари из установленной библиотеки. ## Goals / Non-Goals **Goals:** единый установленный источник кода и словарей для разработки и контейнера; повторная установка локальных изменений без смены версии; проверка версии при старте; сохранение адаптеров и проверок сервиса. **Non-Goals:** редактирование исходников библиотеки, библиотечных тестов, внешнего CI/Compose и канонических схем. Денежные расчёты остаются Decimal, единицы (рубли / тысячи рублей) не меняются. ORM и консолидация не затрагиваются. ## Decisions 1. **Локальная установка через uv.** `make venv` фильтрует `^(raisa_|pylib_ai)` из `api/requirements.txt`, устанавливает оставшиеся зависимости и dev-инструменты, затем ставит `STATEMENTS_DIR`. `make install` создаёт окружение Python 3.11 при необходимости и использует `--reinstall-package pylib-ai-arm-statements`, чтобы подтянуть изменения при прежней версии. Путь по умолчанию совпадает с соседним checkout; переопределение поддерживает другую раскладку workspace. Альтернативы: editable-установка делает окружение зависимым от живых исходников; копирование возвращает проблему синхронизации; индекс недоступен для этого пакета. Pin в requirements документирует ожидаемую версию, но локальная установка не проверяет совпадение VERSION с pin автоматически. 2. **Удаление vendored-кода и его проверок.** `api/arm_statements/` удаляется целиком вместе с тестами, rules и VERSION. `statements-check` и зависимость `check-full` от него удаляются. Pytest собирает `tests`, mypy проверяет `src`; Ruff считает только `src` first-party, поэтому импорты библиотеки перегруппированы в адаптерах и тестах. В mypy строгий override для копии заменён `ignore_missing_imports`, поскольку дистрибутив не поставляет `py.typed`. Тесты сервиса остаются; библиотечные тесты принадлежат библиотеке. Альтернатива сохранения двух копий допускает затенение установленного пакета локальной папкой. 3. **Версия через API библиотеки.** `require_statements_version` продолжает вызывать `statements_version(path)` и запрещает старт при отсутствии версии. Меняются документация и RuntimeError: `библиотека arm_statements без VERSION и версии пакета: выполните make install`. VERSION сохраняет приоритет, метаданные дают fallback для установленного дистрибутива; прямое чтение метаданных в сервисе дублировало бы логику библиотеки. Тип поля контракта остаётся непустой строкой, JSON-схемы не редактируются. 4. **Именованный контекст BuildKit.** `make docker-build` передаёт `--build-context arm_statements="$(STATEMENTS_DIR)"`. Dockerfile подключает его через временный bind mount на шаге RUN и устанавливает `/tmp/arm_statements` вместе с отфильтрованными requirements. `rw` нужен сборочным инструментам для временных файлов; изменения mount не записываются обратно на хост и не становятся содержимым образа. Установленный пакет и его package data остаются в образе. Альтернатива расширения основного контекста на весь workspace меняет правила COPY и состав контекста; копирование checkout в слой добавляет ненужные исходники. Прямым вызовам Docker, внешним CI и Compose потребуется тот же именованный контекст; их конфигурация в этой задаче не менялась. 5. **Документация отражает библиотечную поставку.** README, CLAUDE.md, `api/rules/README.md`, комментарии в requirements-dev, проверке версии, тесте worker и модели `Service` обновлены. Архивные OpenSpec change и копии JSON-схем не переписываются. Внешнее описание поля как коммита требует согласования отдельным change в docs. ## Risks / Trade-offs - [Нет локального checkout или неверный STATEMENTS_DIR] → установка завершается ошибкой; путь и команда переустановки описаны в README. - [Отличие локальной версии от pin] → перед сборкой проверять метаданные дистрибутива и `statements_version()`; автоматическая сверка не добавлялась. - [Версия 0.1.1 не идентифицирует локальные изменения исходников] → переустановка принудительная, воспроизводимый релиз требует дисциплины версий библиотеки; Git SHA больше не является обязательным форматом поля. - [Нет BuildKit или не передан именованный контекст] → использовать `make docker-build`; в других системах сборки явно передавать контекст. - [Нет маркера типов у библиотеки] → mypy проверяет код сервиса, поведение интеграции подтверждают тесты адаптеров. - [Описание JSON-схемы ещё говорит о коммите] → не нарушать идентичность копии контракта; согласовать описание в docs отдельно. ## Migration Plan 1. Подготовить checkout библиотеки версии 0.1.1; выполнить `make venv` либо `make install` в существующем окружении. 2. Проверить, что `arm_statements.__file__` указывает на site-packages, версия равна 0.1.1, `default_rules()` загружает данные, `get_parser()` создаёт BookParser. 3. Выполнить lint, тесты адаптеров/версии и golden/fixtures-проверки на доступных реальных книгах. Перед релизом собрать и проверить контейнер с локальным контекстом. 4. Для отката восстановить удалённую копию и прежние Makefile/Dockerfile/конфиги одним откатом изменения; не оставлять обе модели поставки одновременно. ## Validation Evidence Результаты предыдущего выполнения в этой сессии (не повторная проверка в рамках proposal): - Локальная установка завершилась: `pylib-ai-arm-statements==0.1.1`; импорт из site-packages, библиотека сообщает `0.1.1`, словари загружаются, создаётся BookParser. - `make lint` прошёл (Ruff, format, mypy). - `tests/unit/parser`: 1206 passed, 5 skipped; три теста проверки версии/ошибки загрузки словарей в `test_worker.py` прошли. - Полный unit-прогон остановлен после зависания на worker-тестах; успешный полный прогон не заявляется. Для запуска тестов потребовалось переопределить некорректное внешнее значение DEBUG через `DEBUG=false`. - Docker-сборка не проверена: доступ к `/var/run/docker.sock` запрещён. Реальные fixtures/golden и интеграционные NATS/S3-проверки отдельно не выполнялись. ### Проверки при продолжении apply (24.10.2026) - Реальные формы: 27 passed; сравнение форм с golden-снимками: 27 passed. Снимки не перезаписывались. - `make venv` и повторный `make install` с абсолютным STATEMENTS_DIR прошли; библиотека 0.1.1 импортируется из site-packages. - `make test-integration` вне песочницы: 10 passed, включая реальные формы и версию библиотеки в результате. - Полный unit-прогон вне песочницы: 1733 passed, 1 failed за 4.78 s. В песочнице worker-тесты зависают; вне неё это не воспроизводится. - Единственная ошибка unit-прогона — идентичность `api/contracts/recognition-result.schema.json` актуальному файлу docs: в docs уже изменены два текстовых описания (библиотека и источник её версии), структура схемы прежняя. Синхронизация копии ожидает решения пользователя по границе задачи. - Docker-сборка завершилась успешно; запуск образа с `--network none` без mount исходников подтвердил импорт из site-packages, версию 0.1.1 и загрузку словарей. `make lint` повторно прошёл.