62 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 statements="$(STATEMENTS_DIR)"`. Dockerfile подключает его через временный bind mount на шаге RUN и устанавливает `/tmp/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()`; при Docker-сборке pip автоматически проверяет соответствие локальной версии pin из requirements.
- [Версия 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` повторно прошёл.